Button
A button engineered as an interaction system — press physics, keyboard parity, motion tokens.
Hold, then drag off before releasing - the press cancels instead of firing. Space behaves the same from the keyboard.
Interaction spec
A press begins on pointer-down, not on click. While pressed, the surface
compresses (data-pressed); releasing inside fires the action, dragging off
before release cancels it, matching native platform button physics.
- Pointer down inside → pressed
- Drag off while pressed → cancelled, no action
- Pointer up inside → action fires (native click)
- Space held → pressed; released → action fires
- Enter → action fires immediately on key down
Interruption is free: state is attribute-driven, so a cancelled press transitions back through CSS mid-animation without JavaScript cleanup.
Accessibility
- Native
buttonsemantics | no ARIA re-implementation. - Tab focuses; the focus ring appears only for keyboard focus
(
:focus-visible), never on pointer press. disableduses the native attribute: unfocusable and announced as unavailable by screen readers.
Motion
Press compression runs at --motion-dur-instant on the way down, response
must feel immediate, and --motion-dur-fast on release. Every value comes
from the motion tokens; nothing is hard-coded. Users with
prefers-reduced-motion get state changes with no transform animation.
API
Button
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | solid|outline|ghost | "solid" | Visual style of the button surface. |
| size | sm|md|lg | "md" | Height and padding scale. |
| disabled | boolean | false | Disables interaction and press tracking. |
All other native button props are forwarded, and event handlers compose with the press system instead of replacing it.