Button
An action control. Renders an <a-button> web component (or
<a role="button"> when href is set) with a tone × priority matrix
applied via attributes.
style?set by codeTones
Six named tones — brand (default), neutral, critical, info,
success, warning — plus a one-off custom tone for any literal CSS
color value:
<Button tone="brand" label="Save" /><Button tone="#ff1493" label="Pink one-off" /><Button tone="oklch(0.6 0.25 30)" label="oklch one-off" />For custom tones, only the hue of the input is taken. Lightness and
chroma come from the brand-tone curve, so pale or low-chroma inputs
(tone="#cccccc", tone="white") still produce a legible button. The
full priority × state matrix — rest, hover, active, secondary alpha
overlay, tertiary fill — is derived automatically.
If you need pixel-precise color, set --button-fg-color,
--button-bg-color, or --button-br-color directly via style —
inline declarations beat the resolver.
Dark mode reuses the same curve. To pair light/dark variants for a custom tone, scope it yourself:
.dark a-button[tone="…"] { --button-tone-source: …; }Priorities
Four priorities, each with a distinct visual signature:
| Priority | Visual |
|---|---|
primary | Saturated fill. Main call to action. |
secondary | Pastel background, no border. Everyday button. |
tertiary | Background-less at rest, fills on hover. Dense toolbars. |
quaternary | Text only — no background, no border, no transitions. |
Two prop constraints follow from priority and are enforced at the type level:
underline— only ontertiary/quaternary. Filled chips don’t need an underline; passing it onprimary/secondaryis a TypeScript error.paddingless— only onquaternary. Zeros the outer padding so the button sits flush with surrounding prose.
<Button priority="tertiary" underline="dashed" label="Read more" /><Button priority="quaternary" paddingless label="here" />The underline holds at a 0.5px hairline in every state; only the
underline alpha lifts from 75% → 100% on hover/active. The color
tracks --button-fg-color so a tone shift updates the underline in
lockstep with the label.
Sizes
Three sizes; only padding changes (font size stays at 15px):
<Button size="small" label="Small" /><Button size="default" label="Default" /><Button size="large" label="Large" />Icon-only
Pass iconButton="<shape>" for a square icon button. The label and
content slots are TypeScript errors in this mode — icon-only buttons
render the named icon standalone:
<Button iconButton="check" /><Button iconButton="trash" tone="critical" /><Button iconButton="dots-vertical" size="small" />A min-width is pinned to the natural square (20px / 24px / 28px for small / default / large) so a tight flex parent can’t clip the icon.
Leading and trailing icons
For non-icon-only buttons, pass leadingIcon and/or trailingIcon:
<Button leadingIcon="check" label="Confirm" /><Button trailingIcon="external-link" label="Read the docs" /><Button leadingIcon="info" trailingIcon="chevron-down" label="Filter" />Icon shape names come from the IconShape union — see the
Icon page for the full set.
States
<Button loading label="Submitting" /><Button disabled label="Locked" /><Button selected label="Toggled on" /><Button loading disabled label="Critical" />loading— diagonal stripe overlay slides across the button. Stripe color followscurrentColor, so it tracks the tone. Blocks clicks viapointer-events: none.disabled— locks the colors to the disabled palette, setspointer-events: none, and removes the button from the tab order. Beats inline--button-bg-coloroverrides.selected— toggled-on / pressed visual; shares the active state’s look. Useful for filter chips and icon toggles.
Anchor mode
Setting href switches the rendered tag from <a-button> to
<a role="button">. Styling is identical — both selectors share the
same CSS rules. Form-submission props (type, form) are TypeScript
errors in anchor mode; navigation takes over.
<Button href="/docs" target="_blank" rel="noopener" label="Read the docs" />Form submission
For non-anchor buttons, type="submit" and type="reset" integrate
with native forms:
<form id="signup"> <Button type="submit" label="Sign up" /> <Button type="reset" label="Clear" /></form>
{/* Associate with a form by id when the button is outside */}<Button type="submit" form="signup" label="Submit from outside" />type="submit" calls form.requestSubmit() and also dispatches a
submitdetailed event on the form with { formData, submitter: { tag, attrs } }
in detail — handy for analytics or multi-button forms.
Custom click events
data-custom-event="<name>" makes the button dispatch a bubbling
CustomEvent("<name>") on click. Use it to instrument analytics
without taking ownership of onClick:
<Button label="Save" data-custom-event="save-clicked" />Children alongside label
Children render after the label and before the trailing icon, so you can mix custom inline content — keyboard hints, badges, counters — with the prop-driven API:
<Button label="Save" leadingIcon="check"> <span style={{ opacity: 0.7, fontSize: 12 }}>⌘S</span></Button>Children also work without label — pass them as the only content.
Props
| Prop | Type | Notes |
|---|---|---|
tone | 'brand' | 'neutral' | 'critical' | 'info' | 'success' | 'warning' | <css color> | Default brand. Literal colors derive the full matrix from the input hue. |
priority | 'primary' | 'secondary' | 'tertiary' | 'quaternary' | Default primary. |
size | 'small' | 'default' | 'large' | Default default. Only padding changes. |
underline | 'solid' | 'dashed' | 'dotted' | tertiary / quaternary only. |
paddingless | boolean | quaternary only. |
loading | boolean | Stripe overlay; blocks clicks. |
disabled | boolean | Disabled palette; no clicks; removed from tab order. |
selected | boolean | Toggled-on visual (shares active look). |
leadingIcon / trailingIcon | IconShape | Icon shape names. Forbidden with iconButton. |
iconButton | IconShape | Icon-only mode. label / leadingIcon / trailingIcon / children are TypeScript errors when set. |
label | string | Rendered inside <a-button-label>. |
href | string | Switches to <a role="button">. |
target / rel | string | Anchor mode only. |
type | 'button' | 'submit' | 'reset' | Form integration. Forbidden with href. |
form | string | Associate with a form by id. Forbidden with href. |
onClick | (e) => void | Click handler. |
data-custom-event | string | Dispatches CustomEvent(<name>) on click. |
Component tokens
Override any of these on a single instance
(style={{ '--button-padding-x': '12px' }}) or on a selector wrapping
the button (e.g. a tone or variant of your own). Color tokens are
dual-declared hex; oklch; alpha fills use color-mix(in oklch, …)
with a hex8 fallback first.
| Token | Description |
|---|---|
--button-fg-color | Text and icon color. Resolved per tone × priority × state. |
--button-bg-color | Background fill. |
--button-br-color | Border color. Primary mirrors the fill; others are transparent. |
--button-focus-color | Focus-ring outline color. |
--button-padding-x / --button-padding-y | Inner spacing. |
--button-br-radius | Border radius (default 4px). |
--button-br-width | Border width (default 0 — no border). Set to 1px for an outlined variant. |
--button-font-size / --button-font-family / --button-font-weight / --button-line-height | Type. |
--button-gap | Spacing between icon, label, and children. |
--button-timing-in / --button-timing-out / --button-timing-active | Per-state transition durations (50ms / 150ms / 50ms). Quaternary zeroes all three. |
--button-outline-offset | Focus-ring offset (default 1px). |
--button-loading-duration | One slide cycle for the loading stripe (default 1.1s). |
--button-loading-angle | Stripe angle (default 125deg). |
--button-loading-period | Stripe + gap along the gradient axis (default 30px). |
--button-loading-stripe / --button-loading-stripe-gap | Stripe geometry (default 14px / 16px). |
--button-loading-opacity | Overlay opacity (default 0.3). |
--button-loading-blur | Stripe-edge softening (default 1px). |