Button
The text button: a pill at weight 500, six variants that only reassign tokens, four sizes, loading and toggle states.
import { Button } from "@arcadia/ui"Variants only reassign --button-* knobs, so every variant shares one geometry. Use primary for the one action that moves the flow forward and ghost for everything around it. Primary is ink, never the accent: the beacon is kept for lights. Every button is a pill at weight 500 (shape="rounded" keeps the size's radius); its states change colour only, over 150ms. A leading icon, a trailing iconEnd and a quiet hint key sit inside the same padding.
Examples
Variants
Six variants on one geometry. danger is for the destructive action itself, not for the button that opens a confirmation.
Tones
plan is Plan mode's distance blue.
On primary it fills the button; on the other variants it tints the label. An answer to the agent (Submit, Allow) is the ordinary ink primary: amber stays on literal lights, never a control.
States
loading puts the spinner in the icon's slot (or over a label-only button), so the width never changes and focus stays.
pressed draws the toggled fill, draws the icon's filled form and sets aria-pressed. focusableWhenDisabled keeps a disabled button focusable, so a tooltip can say why.
rootStyle takes where the button sits (margins, self-alignment, flex and grid placement, size bounds) and the knobs below, each a token of its kind. A colour, a raw length or any other property is a type error: a look that comes up twice becomes a variant.
| Knob | Takes | Default | Sets |
|---|---|---|---|
Ref<Control> | var(--control-md) | Height. | |
Ref<Space> | var(--space-2-5) | Inline padding (a pill adds --space-1). | |
Ref<Radius> | var(--radius-lg) | Corner radius (a pill is full). | |
Ref<Space> | var(--space-1-5) | Gap between icon, label and hint. | |
Ref<IconSize> | var(--icon-base) | Icon and spinner size. | |
Paint | transparent | Fill at rest. | |
Paint | var(--fill-4) | Fill under the pointer, or while its popup is open. | |
Paint | var(--button-bg-hover) | Fill while pressed. | |
Paint | var(--fill-3) | Fill when toggled on (pressed). | |
Paint | transparent | Fill when disabled. | |
Paint | var(--text-2) | Label and icon colour at rest. | |
Paint | var(--text-1) | Label colour under the pointer. | |
Paint | var(--text-1) | Label colour when toggled on. | |
Paint | var(--text-4) | Label colour when disabled. | |
Paint | transparent | Border colour (1px) at rest. | |
Paint | var(--button-border) | Border colour under the pointer. | |
Paint | var(--text-3) | Key hint ink. | |
Paint | var(--button-fg-disabled) | Key hint ink when disabled. |
Variants, sizes and tones only reassign knobs, and each state reads its own (--button-bg-hover, --button-hint-disabled), so a knob you set never drops a state.
A native <button> by default. loading sets aria-busy and blocks activation without dropping focus; pressed sets aria-pressed. Keyboard focus draws the 2px ring, outset by 2px.
A hint is drawn only: it is aria-hidden and becomes aria-keyshortcuts ("⇧Tab" → Shift+Tab, "⏎" → Enter), so the accessible name is the label alone. Binding the key is yours.
- Enter
- Activates the button
- Space
- Activates the button
Button
variant- "primary" | "secondary" | "outline" | "ghost" | "text" | "danger"Default"ghost"
primaryis the monochrome ink CTA, for the one action that moves the flow forward (send).size- "xs" | "sm" | "md" | "lg"Default"md"
- Heights 20 / 24 / 28 / 32.
shape- "rounded" | "pill"Default"pill"
tone- "neutral" | "plan"
planis Plan mode's distance blue: a blue CTA onprimary, blue text elsewhere. There is no amber tone: the sodium light is for literal lights (a running edge, a needs-you dot), never a control's fill.iconEnd- IconName
hint- ShortcutSpec | ReactElement
- Trailing key hint, drawn quietly:
"⇧Tab","⏎",["⌘","K"],"mod+k", or aKbdGroup. Visual only (aria-hidden, so the name stays "Allow", not "Allow ⏎"); the keys are exposed asaria-keyshortcutson the button. Binding the key is yours. loading- booleanDefaultfalse
- Busy:
aria-busy, a spinner, and activation blocked while focus is kept (no native disabled). pressed- boolean
- Toggle state: sets
aria-pressedanddata-pressed. Omit for a plain action button. rootStyle- ButtonStyle
- Typed StyleX override: where the button sits (margins, self-alignment, flex and grid placement, size bounds) and its
--button-*knobs. render- RenderProp<{ disabled: boolean }>
- Replace the rendered element, e.g.
render={<a href="/settings" />}, or a function of props and state. disabled- boolean
- Native disabled: no pointer events, no tooltip, skipped by Tab.
focusableWhenDisabled- boolean
- Keep a disabled control focusable and hoverable (
aria-disabled), so a tooltip can say why. nativeButton- boolean
- Button semantics, inferred from what renders: a
<button>is native, an<a>stays a link, anything else getsrole="button"and key handling.falseforces button semantics on whatever renders (a link acting as a button). className- string
- A product's hook class for its own layout rules. Being retired: new code places a button with
rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.
Rest props go to <button> props.