Buttons
stable
Buttons trigger actions. Four variants cover the full intent spectrum from primary confirmation
to destructive operations. All variants share a base class and consume Tier-3 --btn-*
tokens so the entire button palette can be rethemed in one place without touching the semantic layer.
Variants
Apply .btn first, then exactly one variant modifier. Use the variant that matches
the intent of the action — not its visual weight preference.
| Variant | Class | Use |
|---|---|---|
.btn-p | The single most important action on a page or in a dialog. Use once per view. | |
.btn-s | Supporting actions alongside a primary button — cancel, back, view details. | |
.btn-g | Low-emphasis actions in toolbars, cards, or dense layouts where ink weight matters. | |
.btn-d | Irreversible operations: delete, remove, revoke. Pair with a confirmation dialog. |
Sizes
Size modifiers adjust height and padding only. Font weight and variant colours are unaffected.
The default size fits most layouts; use .btn-sm for dense UIs and .btn-lg
for hero CTAs.
| Size | Class | Height | Padding | Font size |
|---|---|---|---|---|
| Small | .btn-sm | 32px | 0 12px | 12px |
| Default | — | 40px | 0 16px | 13px |
| Large | .btn-lg | 48px | 0 20px | 14px |
States
Hover, active (pressed), and focus states are driven by --btn-* token values.
Focus uses the global :focus-visible outline from base.css.
Disabled buttons reduce opacity via --btn-disabled-opacity and suppress pointer events.
For <a> elements acting as disabled buttons, add the aria-disabled="true"
attribute and the .btn--disabled class instead of relying on the disabled attribute,
which has no effect on anchor elements.
<button class="btn btn-p" disabled>Can't proceed</button>
<!-- link variant — disabled state -->
<a class="btn btn-p" aria-disabled="true" tabindex="-1"
style="opacity: var(--btn-disabled-opacity); pointer-events: none;">
Can't proceed
</a> Link buttons
Any <a> element accepts the .btn classes. The base styles set
text-decoration: none so links render identically to <button> elements.
Use <button> for in-page actions and <a> for navigation.
<a href="/docs" class="btn btn-p">Read the docs</a>
<a href="/cancel" class="btn btn-s">Cancel</a>
Naked <a> elements (without .btn) inherit --link-color,
--link-hover-color, and --link-visited-color from tokens/base.css.
Override these tokens to retheme inline links without touching --color-accent.
Token reference
All --btn-* tokens are defined in tokens/components.css. The dark-mode
secondary hover override lives in tokens/dark-light.css alongside all other theme overrides.
| Token | Default | Purpose |
|---|---|---|
--btn-radius | var(--radius-md) | Border radius for all button variants |
--btn-disabled-opacity | 0.5 | Opacity applied to disabled buttons |
| Primary | ||
--btn-p-bg | var(--color-accent) | Primary background |
--btn-p-text | var(--color-accent-text) | Primary text colour |
--btn-p-border | transparent | Primary border colour |
--btn-p-hover-bg | var(--color-accent-hover) | Primary hover background |
--btn-p-active-bg | var(--color-accent-active) | Primary pressed background |
| Secondary | ||
--btn-s-bg | var(--color-bg-inset) | Secondary background |
--btn-s-text | var(--color-text) | Secondary text colour |
--btn-s-border | transparent | Secondary border colour |
--btn-s-hover-bg | var(--color-bg-panel) / dark: var(--in3-ash) | Secondary hover background |
--btn-s-active-bg | var(--bm0-sand) / dark: var(--in2-slate) | Secondary pressed background |
| Ghost | ||
--btn-g-bg | transparent | Ghost background |
--btn-g-text | var(--color-text) | Ghost text colour |
--btn-g-border | var(--color-border) | Ghost border colour |
--btn-g-hover-bg | var(--color-bg-panel) | Ghost hover background |
--btn-g-active-bg | var(--bm0-sand) / dark: var(--in0-void) | Ghost pressed background |
| Destructive | ||
--btn-d-bg | var(--color-error) | Destructive background |
--btn-d-text | var(--color-on-error) | Destructive text colour |
--btn-d-border | transparent | Destructive border colour |
--btn-d-hover-bg | var(--in0-void) | Destructive hover background (theme-invariant) |
--btn-d-active-bg | var(--in0-void) | Destructive pressed background (same as hover — darkest available token) |
| Links | ||
--link-color | var(--color-accent) | Naked link colour |
--link-hover-color | var(--color-accent-hover) | Naked link hover colour |
--link-visited-color | var(--color-accent) | Naked link visited colour |
Overriding tokens
Override tokens on a scoped selector to retheme buttons in one section without affecting the rest
of the page. Because --btn-* tokens sit above the semantic layer, changing
--btn-p-bg has no effect on any other accent-coloured element.
/* Retheme the primary button for a promotional section */
.promo-section {
--btn-p-bg: #7C3AED;
--btn-p-hover-bg: #6D28D9;
--btn-p-text: #fff;
}
/* Make all buttons pill-shaped in a card component */
.card {
--btn-radius: var(--radius-full);
} Anatomy
<!-- base class + variant modifier -->
<button class="btn btn-p">Label</button>
<!-- with size modifier -->
<button class="btn btn-s btn-sm">Label</button>
<!-- with icon (gap: 6px handled by flexbox) -->
<button class="btn btn-g">
<i class="ti ti-download"></i>
Download
</button>