Button
Trigger an event or action. The button family includes three components for different semantic needs.
The button family is three components for three semantic needs. Pick the right one before you reach for variants — the wrong shell semantically can't be fixed with styling.
Button
Use when: The action stays on this page (submit, open dialog, confirm).
Don't: Use it for navigation — right-click and keyboard nav break.
<Button>Save</Button>LinkButton
Use when: The action navigates to a URL. Renders an `<a>` element.
Don't: Wrap a Button in a link — that breaks accessibility.
<LinkButton href="/docs">…</LinkButton>IconButton
Use when: There's no visible text label. Single-icon trigger.
Don't: Downgrade emphasis when converting from Button — keep the variant.
<IconButton icon={…} label="Close" />Use it for
- Submitting a form
- Confirming or cancelling a destructive operation
- Opening a sheet, dialog, or popover
- Triggering an immediate, synchronous action
Import
import { Button, LinkButton, IconButton } from '@brikdesigns/bds';Anatomy
- 1Container
The hit target. Sets size, padding, focus ring, and base variant styles.
- 2Leading icon
Optional icon before the label. Use 1em sizing to scale with text.
- 3Labelrequired
The accessible name of the button. Required for Button and LinkButton.
- 4Trailing icon
Optional icon after the label. Common for "Continue →" or external-link affordances.
Variants
Twelve variants. Pick by action emphasis and destructive intent, not by aesthetics.
Standard ladder
primaryMain call-to-action. One per section.
outlineSecondary emphasis. The "alongside primary" choice.
secondaryTertiary, subtle. Supporting actions that don't compete.
ghostMinimal emphasis. Repeated actions, navigation, dismiss.
Destructive ladder
Three flavors of destructive map onto the same ladder.
dangerPrimary destructive action — confirm dialogs, delete-and-go flows.
danger-outlineDestructive with less emphasis. Pairs with a non-destructive primary.
danger-ghostInline delete in lists, table rows, repeated remove affordances.
State variants
positive confirms a completed action. selected indicates the active option in a group. inverse and on-color are for placement on dark or branded surfaces.
Sizes
Five sizes. md is the default. Use sm only inside dense surfaces (table rows, popovers); use lg/xl for primary CTAs on landing pages.
Icons
Use iconBefore and iconAfter props. Icons should use 1em sizing to scale with the button's font size.
<Button iconBefore={<PlusIcon />}>Add item</Button>
<Button iconAfter={<ArrowRightIcon />}>Continue</Button>
<Button iconBefore={<DownloadIcon />} variant="outline">
Export CSV
</Button>Loading
The loading prop replaces button content with a spinner while preserving button width. The button is automatically disabled during loading.
<Button loading variant="primary">
Saving...
</Button>LinkButton
An <a> element styled as a button. Use when the action navigates to a URL. The href prop is required.
import { LinkButton } from '@brikdesigns/bds';
<LinkButton href="/docs" variant="outline">
Read docs
</LinkButton>
<LinkButton
href="https://github.com/brikdesigns/brik-bds"
target="_blank"
rel="noopener"
iconAfter={<ExternalLinkIcon />}
>
GitHub
</LinkButton>IconButton
An icon-only button with a required label prop for accessibility. The label becomes the button's aria-label.
import { IconButton } from '@brikdesigns/bds';
<IconButton icon={<CloseIcon />} label="Close dialog" variant="ghost" />
<IconButton icon={<TrashIcon />} label="Delete item" variant="danger-ghost" />Don't downgrade emphasis when converting Button → IconButton. A primary action stays a primary action. The 2026-04 IconButton-ghost regression came from agents converting <Button variant="primary"> into <IconButton variant="ghost">. Match the emphasis level, always.
When not to use
- Don't use
ghostfor the only action on a page. Ghost is the lowest emphasis tier — it should appear alongside a higher-emphasis sibling. - Don't stack two
primarybuttons next to each other. Only one primary action per surface. - Don't use Button for navigation. If clicking takes the user to a new URL, use LinkButton so right-click + keyboard navigation work correctly.
Accessibility
- Renders a real
<button>element. Keyboard navigation, focus ring, andEnter/Spaceactivation come from the platform. - Accessible name comes from the visible label. For IconButton,
labelbecomesaria-label. - Disabled state uses the
disabledattribute, which removes it from the tab order. For "blocked but explainable" actions, prefer keeping it focusable and showing a tooltip. - Loading state announces via
aria-busyand disables interaction without removing focus.
API
The full prop reference (with auto-extracted TypeScript types and live controls) lives in Storybook → Components/Action/Button. Summary below.
Button
| Prop | Type | Default |
|---|---|---|
variant | ButtonVariant | 'primary' |
size | ButtonSize | 'md' |
iconBefore | ReactNode | — |
iconAfter | ReactNode | — |
loading | boolean | false |
fullWidth | boolean | false |
disabled | boolean | false |
LinkButton
| Prop | Type | Default |
|---|---|---|
href | string (required) | — |
variant | ButtonVariant | 'primary' |
size | ButtonSize | 'md' |
iconBefore | ReactNode | — |
iconAfter | ReactNode | — |
fullWidth | boolean | false |
IconButton
| Prop | Type | Default |
|---|---|---|
icon | ReactNode (required) | — |
label | string (required) | — |
variant | ButtonVariant | 'ghost' |
size | ButtonSize | 'md' |
loading | boolean | false |
Type unions
type ButtonVariant =
| 'primary' | 'outline' | 'secondary' | 'ghost'
| 'inverse' | 'on-color'
| 'danger' | 'danger-outline' | 'danger-ghost' | 'destructive'
| 'positive' | 'selected';
type ButtonSize = 'tiny' | 'sm' | 'md' | 'lg' | 'xl';CSS Override API
Component-scoped CSS variables exposed on .bds-button. Set these on a wrapping element or directly on the button to override without touching BDS internals.
| Variable | Default | Controls |
|---|---|---|
--button-active-translate-y | 1px | Downward shift on :active press |
--button-focus-outline-width | 2px | Focus ring stroke width |
--button-focus-outline-offset | 2px | Gap between button edge and focus ring |
/* Example: flatten press effect in a toolbar context */
.my-toolbar {
--button-active-translate-y: 0;
--button-focus-outline-width: 3px;
}Related
- Storybook playground — every variant × size × state, live controls
- Forms pattern — Save/Cancel placement, submit-state handling
- Components index — full component catalog