BridgeComfortable

Previews use

Applies to every design system preview on the site. The Bridge app chrome stays the same.

Brand

Density

Button

Stable

Triggers an action on the current screen: save, publish, approve, open a dialog. If it takes the user somewhere else, it is a Link, not a Button.

Updated Oct 16, 2026

Rendered with the real component
Light
Dark

When to use

Use it for

  • The user commits an action on this screen: Publish, Approve, Create token, Open pull request.
  • Primary: the one action you most want the user to take in this view. One per form, dialog or page section.
  • Secondary: the alternatives next to the primary, like Save as draft or Request changes.
  • Ghost: low-emphasis actions repeated in lists, tables or toolbars (Edit, View diff), where filled buttons would fight for attention.
  • Danger: destructive actions (Delete, Revoke access). Usually inside a confirmation dialog.
  • Size sm in dense rows and toolbars, md everywhere else, lg only for a single call to action on an empty or onboarding screen. Heights follow the density setting.

Not for

  • Going to another page or URL: use Link. Links and buttons are announced differently and users expect Cmd+click to open links in a new tab.
  • Turning a setting on or off: use Switch or Checkbox.
  • Picking one of several views: use Tabs or an attached ButtonGroup.
  • More than three actions in one row: keep the main ones and move the rest to a Menu behind an IconButton.

Do and don't

Every example is rendered with the real component, so it always matches what ships.

Do

Start the label with a verb that names the outcome

"Publish tokens" and "Approve change" tell the user what happens before they click.

Do

One primary per view, the others step down

Hierarchy tells users where to look first. Secondary and ghost carry the alternatives.

Do

Show progress with loading, keep the label

The button stays in place and focusable, so keyboard users do not lose their position. Change the label to the present participle.

Don't

Two primary buttons side by side

When everything is primary, nothing is. Users hesitate and pick at random.

Don't

Vague labels

"OK" and "Click here" say nothing out of context, and screen reader users often list buttons out of context.

Don't

Danger for actions that are not destructive

Red trains people to ignore red. Keep danger for actions that remove or revoke something.

Accessibility

  • Native button through Base UI: Enter and Space activate it, it is in the tab order, no extra ARIA needed.
  • The focus ring (focus.ring.color, 2px, offset 2px) shows on keyboard focus only, never on mouse click.
  • Loading sets aria-busy and aria-disabled, ignores clicks, and keeps the button focusable so keyboard users keep their place.
  • Disabled removes the button from the tab order and explains nothing. Prefer an enabled button plus a message saying what is missing.
  • With render={<a href />} the element stays a link (role link), which is right for navigation styled as a button.
  • Heights are 32/40/48px comfortable and 28/32/40px compact: all above the WCAG 2.2 minimum target of 24px.

Anatomy and tokens

Read from packages/react/src/components/Button/Button.module.css at build time. Every value is a token: open one to see its alias chain in both themes.

39 tokens 26 semantic or component, change with theme and brand 7 density, change with comfortable or compact 6 from the shared scales

Props

Parsed from the TypeScript types in packages/react/src/components/Button/Button.tsx, so this table cannot drift from the code. It also accepts every native <button> attribute.

Prop Type Default Description
variant 'primary' | 'secondary' | 'ghost' | 'danger' 'primary' Visual emphasis. Use one primary action per view; danger only for destructive actions.
size 'sm' | 'md' | 'lg' 'md' Height follows the density tokens: sm 32/28, md 40/32, lg 48/40 (comfortable/compact).
loading boolean false Shows a spinner, keeps the button focusable, ignores clicks and sets aria-busy.
iconStart LucideIcon none Lucide icon before the label. Replaced by the spinner while loading.
iconEnd LucideIcon none Lucide icon after the label, for example a chevron on a menu trigger.
fullWidth boolean false Stretch to the width of the container.
render ReactElement none Render another element with button styling, for example a router link: `render={<a href="/tokens" />}`. The element keeps its own role (a link stays a link), so only use it for navigation.

Playground

Change the props and see the real Button update. Controls are generated from the TypeScript props; nothing here changes the system.

<Button>Publish tokens</Button>
variant
size

Workbench

For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open Button in Storybook) .

Embed Storybook stories here (9)

Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay empty.

Primary

Secondary

Ghost

Danger

WithIcons

Loading

Disabled

FullWidth

AllStates

Changelog

  • New

    Actions: Button gains `danger` variant, `lg` size, `iconStart`, `iconEnd`, `fullWidth` and `render` (a link stays a link); loading keeps focus. New IconButton, ButtonGroup (spaced or attached) and Link (inline, standalone, subtle, external).

    Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026

  • New

    Bridge token system v1. Adds a brand mapping layer (`bridge` default, `ocean` alternate) between primitives and semantic tokens, a component tier (button, input, checkbox, switch, slider, card, popup, dialog, tooltip, toast, table, tabs, progress, nav), comfortable and compact density, motion durations and easings with a reduced-motion override, layers (z-index), icon sizes, breakpoints, a 1.2 type scale from 14px with tracking tokens, and self-hosted IBM Plex Sans and Mono (`@bridge-ds/tokens/fonts.css`). Primitives are regenerated in OKLCH on one lightness ladder. The build now writes `dist/resolved.json` and fails when any pair in `contrast.checks.json` drops below its WCAG minimum in any brand and theme. Fixes `color.border.strong` against `color.surface.default` (now at least 3:1 in both themes).

    Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026

  • Fix

    Add usage guidance files for Button, Input, Badge and Card, plus the list of color pairs to check for contrast. They feed the new documentation site; no visual or API change.

    Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026

  • New

    Initial foundation: DTCG 2025.10 primitives and light/dark semantic tokens built to CSS variables, plus Button, Input, Badge and Card consuming tokens only.

    Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026

All release notes