Centrim Design System — a token-first, accessible React component system.
Buttons
One button component carries every action in the system. Weight is a variant, scale is a size, and every measurement below comes from a token — there are no per-screen button styles.
What is this?
The V2 Button system: eight variants (primary, secondary, tertiary, subtle, outline, ghost, destructive, link), three sizes, a full-width option, leading/trailing/icon-only configurations, and IconButton plus ButtonGroup for compression and grouping.
When to use it
- A person performs an action: create, save, export, delete, continue
- A row, toolbar or card needs a compact action affordance
- Two or more related actions belong to one visual set
How to use it
- Exactly one primary action per view; step everything else down
- Pass icons through leadingIcon / trailingIcon, never as bare children
- Use IconButton with a label for anything without visible text
What to avoid
- Legacy per-screen button colours — variants replace them
- Two primary buttons competing in one view
- A button for navigation (use link variant or an anchor)
Standardized V2 values
Every value resolves to an existing V1 token. Nothing here is a raw literal.
| Property | Small | Medium (default) | Large |
|---|---|---|---|
| Height | 32px (h-8) | 40px (h-10) | 44px (h-11) |
| Horizontal padding | space-3 (12px) | space-4 (16px) | space-5 (20px) |
| Gap | space-1.5 (6px) | space-1.5 (6px) | space-2 (8px) |
| Border radius | radius-md (8px) | radius-md (8px) | radius-md (8px) |
| Font | label-sm | label-md | label-lg |
| Font weight | weight-semibold (600) | weight-semibold (600) | weight-semibold (600) |
| Icon size | icon-xs (12px) | icon-sm (16px) | icon-md (20px) |
| Border width | border-width-thin (1px) | border-width-thin | border-width-thin |
| Focus ring | ds-focus-ring | ds-focus-ring | ds-focus-ring |
| Transition | ds-transition (fast · ease-standard) | ds-transition | ds-transition |
| Touch target | ds-target (≥24px / ≥44px coarse) | intrinsic | intrinsic |
Groups
ButtonGroup joins related actions; SegmentedControl holds a selection.
Attached group — related actions
One control, several actions.
Attached group — pill corners
radius='full' rounds the outer edges of a split action.
Spaced group — independent actions
Segmented control — mutually exclusive views
Usage examples
Buttons in the shape they actually appear in the application.
Table toolbar
One primary action (Add resident); the toolbar action steps down to tertiary.
Destructive confirmation
Deleting this recurring schedule removes every future occurrence. This cannot be undone.
Form footer
Stacks vertically on mobile with fullWidth buttons in reverse importance order.
Mobile sheet
Do / Don't
Do
- Use exactly one primary button per view and step the rest down.
- Pass icons via leadingIcon / trailingIcon so gap and icon size stay tokenized.
- Use IconButton with a label for icon-only controls, and a Tooltip when the meaning is not obvious.
- Reach for fullWidth on mobile sheets and narrow forms instead of custom widths.
- Confirm every destructive button through a confirm dialog.
Don't
- Don't recreate the legacy palette of green/orange/red buttons — variants carry meaning now.
- Don't add one-off classes for height, padding, radius or font size.
- Don't put an icon in children as a bare element; it loses the tokenized icon size.
- Don't use a button for navigation, or a link for a state-changing action.
- Don't disable a button without telling people what is missing.
Why it is designed this way
Legacy Centrim screens carried at least eight visually distinct button styles for the same semantic action. V2 collapses them into one CVA map: colour communicates weight and intent, never a feature area.
Loading keeps the label in the flow (invisible but measured) so the button never changes width mid-request, and disabled uses dedicated disabled tokens so the label stays readable while emphasis clearly drops.