action · Normal DOM only

Button

A single discrete action. The variant axis encodes emphasis, the accent axis encodes tone — keep one primary-blue per surface.

Buttons trigger an action — they do not navigate. For navigation render an `<a>` or `<Link>` styled as a button. The variant axis goes from filled and visible (primary) to outlined (secondary) to chrome-on-hover (tertiary). Accent overrides the colour — default for neutral chrome, blue for the main affirmative action, danger for irreversible operations. Hover, active, focus-visible and disabled states are all token-driven; nothing is hard-coded.

Inside a Twenty front-component, prefer the bridge import
This Button is Radix-based and uses data-* selectors that the Twenty Remote DOM bridge strips. For panel content, import { Button } from @8maverik8/twenty-design/twenty-ui — re-exports the same primitive from twenty-sdk/ui, which Twenty has verified inside front-component'ов.

Install

Pull from the workspace packages (already available if you ran pnpm add):

ts
import { Button } from '@8maverik8/twenty-design';

Examples

Variants × default accent

Same affordance, decreasing emphasis. Hover any of them — the background, border and focus ring all respond.

tsx
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>

Blue accent — the affirmative axis

Primary blue is the loudest CTA. Secondary / tertiary blue keep the colour but quiet down the surface — useful for inline links or "More info" actions inside content.

tsx
<Button variant="primary" accent="blue">Save changes</Button>
<Button variant="secondary" accent="blue">Learn more</Button>
<Button variant="tertiary" accent="blue">Subscribe</Button>

Danger accent — irreversible

Reserve red for actions a user cannot undo without backups. Pair with a confirm dialog when destructive.

tsx
<Button variant="primary" accent="danger">Delete project</Button>
<Button variant="secondary" accent="danger">Revoke access</Button>
<Button variant="tertiary" accent="danger">Remove member</Button>

Sizes

Pick the smallest size that still feels comfortable for the surface. Settings forms = md, dense table rows = sm, marketing heroes = lg.

tsx
<Button size="sm" accent="blue">Small</Button>
<Button size="md" accent="blue">Medium</Button>
<Button size="lg" accent="blue">Large</Button>

With icons

Leading icon = preview of the action ("✚ Add"), trailing icon = direction or external link.

tsx
<Button accent="blue" startIcon={<IconPlus size={14} stroke={2} />}>Add deal</Button>
<Button variant="secondary" endIcon={<IconArrowRight size={14} stroke={2} />}>Continue</Button>
<Button variant="tertiary" startIcon={<IconReceipt size={14} stroke={2} />}>View invoice</Button>
<Button variant="primary" accent="danger" startIcon={<IconTrash size={14} stroke={2} />}>Delete</Button>

States — focus, disabled, loading

Tab into the row to see the focus ring. The disabled button is still tab-stopped (screen readers can announce it). The loading button swaps the start icon for a spinner and blocks further clicks.

tsx
<Button accent="blue" autoFocus>Has focus on mount</Button>
<Button disabled>Disabled</Button>
<Button accent="blue" isLoading>Saving…</Button>
<Button variant="secondary" accent="danger" disabled>Disabled danger</Button>

Full width — inside narrow surfaces

For sign-in cards, sheet footers, mobile primary CTAs.

tsx
<Button accent="blue" fullWidth>Continue with email</Button>

Variants

variant
primarydefaultFilled surface, highest emphasis. Use for the single main action on a screen.
secondaryOutlined, equal-weight alternative next to a primary (Save / Cancel pairs, secondary toolbar actions).
tertiaryNo chrome until hover. Use inside menus, table rows, and dense toolbars where buttons should disappear when idle.
accent
defaultdefaultNeutral tone — uses `--t-background-*` for fills, `--t-font-color-secondary` for text.
blueAffirmative or primary CTA — uses `--t-color-blue` family. At most one per screen.
dangerIrreversible operations only — delete, purge, revoke. Uses `--t-color-red` family.
size
sm24 px tall — inline row actions, chips, dense toolbars.
mddefault32 px tall — forms, dialogs, settings (default Twenty surface density).
lg40 px tall — heroes, onboarding, marketing CTAs.

Anatomy

startIconOptional leading icon — automatically sized down and tightened to the label.
children (label)Action verb. Use sentence case ("Save changes", not "SAVE CHANGES").
endIconOptional trailing icon — most often a directional cue (`→`, `▾`) or external-link marker.
isLoadingReplaces the start icon with a spinner; button becomes disabled and announces busy.

Guidelines

Use exactly one primary-blue button per surface — the affirmative action.Two filled blue buttons next to each other read as equal priority; users hesitate.
Use a danger button for a reversible action like "Cancel" or "Discard draft".Danger tone trains users that the button is irreversible; using it for safe actions teaches them to ignore the warning.
Always pair an icon-only button with an `aria-label`.Without a visible text label the screen reader has nothing to announce. Prefer `IconButton` instead.
Disable a button via `pointer-events: none` or hiding it behind opacity.Custom disabled styling skips focus management and a11y semantics — always use the `disabled` prop.
Use `isLoading` for async submits (saving, sending). The button becomes disabled and announces busy automatically.Hiding loading state behind your own spinner duplicates the behaviour and risks double-submits.
Stack three or more equal-weight primary buttons in a row.Demote the secondary options to `secondary` or `tertiary`. The eye should land on one button first.