Docs draft
Button
Component storyFigma component set โReact component โDSDS entity โ
When to use it
linkbutton Outline/ghost.Anatomy
container โ Pill (radius.full), 44px tall. Hugs its content horizontally.
leading icon โ Optional, 20px. Waits on the Icon set; placeholder swap target until then.
label โ Sentence case. The component never transforms case.
trailing icon โ Optional, mirrors leading icon.
spinner โ Replaces the label in the loading state; label stays in the accessibility tree.
Variants
variant ยท primary / secondary / outline / ghost / brand-outline / brand-ghost โ primary = filled accent pill; secondary = inverse fill; outline/ghost = quiet neutrals; brand-outline/brand-ghost = orange text roles on quiet surfaces. One size (44px), per the reference โ a size axis is added only when a rebuilt component demands one.
States
rest โ At rest with no interaction.
hover โ Pointer is over the control.
pressed โ Actively pressed by pointer or key.
focused โ Holds keyboard focus and shows the focus ring.
disabled โ Not interactive; rendered with the lifted disabled tokens so it stays perceivable.
loading โ Waiting on content; shows the loading treatment.
Guidelines
API
| Prop | Type | Description |
|---|---|---|
variant | enum | Visual style of the button. |
leadingIcon | ReactNode | Rendered aria-hidden at 1.25em. |
trailingIcon | ReactNode | Icon rendered after the label. |
loading | boolean | Implies disabled + aria-busy; label moves to a visually hidden span. |
...rest | ButtonHTMLAttributes | Forwarded to the native button. |
Design specifications
| radius | radius.full |
| height | space.44 |
| padding-x | space.20 |
| gap | space.4 |
| label | type.label-lg |
| fill-primary-rest | color.accent.base |
| fill-primary-hover | color.accent.hover |
| fill-primary-pressed | color.accent.pressed |
| text-primary | color.accent.on-accent |
| fill-secondary | color.bg.inverse |
| text-secondary | color.text.on-inverse |
| border-outline | color.border.subtle |
| wash-hover | color.bg.secondary |
| wash-pressed | color.bg.pressed |
| border-brand | color.accent.base |
| text-brand | color.accent.text |
| text-brand-pressed | color.accent.text-pressed |
| wash-brand | color.accent.wash |
| border-disabled | color.border.subtle |
| text-disabled | color.text.disabled |
Accessibility
Enter / Space โ Activates the button.
Tab โ Moves focus; focus-visible draws a 2px ring offset 3px in color.text.primary.
color.accent.on-accent on color.accent.base โ Contracted at โฅ 4.5:1 by the build gate.
color.accent.on-accent on color.accent.hover โ Contracted at โฅ 4.5:1 by the build gate.
color.accent.on-accent on color.accent.pressed โ Contracted at โฅ 4.5:1 by the build gate.
color.text.on-inverse on color.bg.inverse โ Contracted at โฅ 4.5:1 by the build gate.
color.accent.text on color.accent.wash โ Contracted at โฅ 4.5:1 by the build gate.
The focus ring deliberately deviates from the reference's brand-orange ring: orange on cream is 2.7:1, below the 3:1 focus-appearance floor, so the ring binds color.text.primary in both themes. Loading announces via aria-busy with the label preserved in a visually hidden span. Disabled buttons stay visible at the declared muted contrast; the reference's #BBBBBB (1.7:1) was replaced by the lifted disabled token.