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 and sets aria-pressed. focusableWhenDisabled keeps a disabled button focusable, so a tooltip can say why. progress (0..1) fills a ring in the icon's slot while the label says the number.
Press
A pressed button sets down: it scales to --press-scale (2%) in --duration-instant and springs back on --ease-release, while its fill holds the step it took under the pointer.
Only the scale moves, never the layout. Hold one down and let go.
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.
--button-h- Ref<Control>Default var(--control-md)
- Height.
--button-px- Ref<Space>Default var(--space-2-5)
- Inline padding (a pill adds
--space-1). --button-radius- Ref<Radius>Default var(--radius-lg)
- Corner radius (a pill is full).
--button-gap- Ref<Space>Default var(--space-1-5)
- Gap between icon, label and hint.
--button-icon- Ref<IconSize>Default var(--icon-text)
- The icon's frame, and the spinner's and the progress ring's:
--icon-textbeside the label (the whole-pixel frame of a drawing as tall as the label's capitals, stroked at the medium stem); an icon-only button takes the standalone scale. --button-bg- PaintDefault transparent
- Fill at rest.
--button-bg-hover- PaintDefault var(--fill-4)
- Fill under the pointer, or while its popup is open.
--button-bg-active- PaintDefault var(--button-bg-hover)
- Fill while pressed.
--button-bg-pressed- PaintDefault var(--fill-3)
- Fill when toggled on (
pressed). --button-bg-disabled- PaintDefault transparent
- Fill when disabled.
--button-fg- PaintDefault var(--text-2)
- Label and icon colour at rest.
--button-fg-hover- PaintDefault var(--text-1)
- Label colour under the pointer.
--button-fg-pressed- PaintDefault var(--text-1)
- Label colour when toggled on.
--button-fg-disabled- PaintDefault var(--text-4)
- Label colour when disabled.
--button-border- PaintDefault transparent
- Border colour (1px) at rest.
--button-border-hover- PaintDefault var(--button-border)
- Border colour under the pointer.
--button-hint- PaintDefault var(--text-3)
- Key hint ink.
--button-hint-disabled- PaintDefault 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 FadeText label names the button by its whole text, whichever form shows.
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- booleanDefault false
- Busy:
aria-busy, a spinner, and activation blocked while focus is kept (no native disabled). progress- number
- Determinate progress, 0..1: a ring fills in the icon's slot (a download, an upload) while the label says the number. The button stays pressable;
loadingwins when both are set. pressed- boolean
- Toggle state: sets
aria-pressedanddata-pressed. Omit for a plain action button. labelRow- boolean
- The label is a row of parts (an avatar beside words), each on its capitals.
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.