Button

A button engineered as an interaction system — press physics, keyboard parity, motion tokens.

pnpm dlx shadcn@latest add @aster/button

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 button semantics | no ARIA re-implementation.
  • Tab focuses; the focus ring appears only for keyboard focus (:focus-visible), never on pointer press.
  • disabled uses 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

PropTypeDefaultDescription
variant
solid|outline|ghost
"solid"Visual style of the button surface.
size
sm|md|lg
"md"Height and padding scale.
disabled
boolean
falseDisables interaction and press tracking.

All other native button props are forwarded, and event handlers compose with the press system instead of replacing it.

Source

Did you like the content?