Live example

Usage
<Button variant="primary" iconStart={<Icon name="plus" />} onClick={add}>
  New booking
</Button>

Props

Button props
PropTypeDefaultDescription
variant?'primary' | 'secondary' | 'ghost' | 'danger''secondary'Visual weight. Use one primary button per view.
size?'sm' | 'md' | 'lg''md'Height follows size.control tokens times the density scale.
loading?booleanfalseShows a spinner and sets aria-busy. The button stays focusable and ignores clicks.
iconStart?ReactNodenoneDecorative icon before the label.
iconEnd?ReactNodenoneDecorative icon after the label.
fullWidth?booleanfalseStretch to the container width.
type?'button' | 'submit' | 'reset''button'Defaults to 'button' so it never submits a form by accident.

Do and don't

Do

  • Start labels with a verb: "Save booking", "Export CSV".
  • Use LinkButton when the action navigates.
  • Keep one primary action per view.

Don't

  • Disable a submit button to hide validation; show the errors instead.
  • Use a div or span with a click handler.
  • Put two primary buttons side by side.

Accessibility

  • Native button element, so Enter and Space work and it is announced as a button.
  • Loading keeps focus in place and announces busy state instead of removing the button from the Tab order.
  • Focus ring uses the focus token, checked at 3:1 against page and surface colors in every theme.

Keyboard

Button keyboard support
KeysAction
Enter, SpaceActivates the button
TabMoves focus to and from the button