Button
A clickable action: five variants across six semantic tones.
Live demo
Button triggers an action or navigates. Visual treatment (variant) and semantic color (tone) are independent axes: any of the five variants (solid, outline, ghost, subtle, link) can carry any of the six tones (accent, neutral, danger, success, warn, info).
It renders a native <button> by default and an <a> when given an href, so the same component covers both actions and links. Icon slots, an icon-only square form, a loading state with an inline accessible spinner, and a full-width block mode round out the surface.
When to use
How this component composes with the rest of the set.
Props
11 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Variants
solid
Filled with the tone color; the primary, highest-emphasis treatment.
outline
Transparent fill with a tone-colored border and text.
ghost
No chrome until hover, then a neutral state tint; tone-colored text.
subtle
A soft tone-tinted background with tone-colored text.
link
Looks like a link: no box, no padding, underline on hover.
Sizes
xs
Densest, for compact toolbar pills.
sm
Compact.
md
Default.
lg
Large.
States
hover
Pointer over the button: overlay paints the hover tint.
active
Button pressed: overlay paints the press tint.
pressed
Toggle engaged: aria-pressed="true" paints the press overlay persistently, so an on toggle reads as sunken across every variant.
selected
Selected within a set: aria-selected="true" paints the selected overlay persistently, so a chosen pill reads as active across every variant.
focus-visible
Keyboard focus: a token-colored ring, plus a transparent outline that becomes real in forced-colors mode.
disabled
Non-interactive: muted fill and ink, overlay suppressed.
loading
Busy: content hidden behind the centered spinner, pointer set to progress.
Anatomy
The named parts that make up the component, with their selectors.
root
The button or anchor element carrying the variant, tone, and size classes.
overlay
The pseudo-element behind the content that paints hover and active state tints.
icon
An icon slot rendered before or after the label (icon-start / icon-end).
label
The text content wrapper, hidden while loading so the spinner takes its place.
spinner
The inline accessible spinner shown while loading, drawn in currentColor.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xoji-default register
(derive(xojiDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--accent
--accent-2
--accent-2-bg
--accent-2-fg
--accent-2-text
--accent-3
--accent-3-bg
--accent-3-fg
--accent-3-text
--accent-4
--accent-4-bg
--accent-4-fg
--accent-4-text
--accent-bg
--accent-fg
--accent-text
--black
--black-bg
--black-fg
--black-text
--blue
--blue-bg
--blue-fg
--blue-text
--border-normal
--border-thick
--border-thin
--brown
--brown-bg
--brown-fg
--brown-text
--cyan
--cyan-bg
--cyan-fg
--cyan-text
--danger
--danger-bg
--danger-fg
--danger-text
--duration-fast
--duration-slow
--ease-standard
--fg-disabled
--font-sans
--gray
--gray-bg
--gray-fg
--gray-text
--green
--green-bg
--green-fg
--green-text
--info
--info-bg
--info-fg
--info-text
--leading-tight
--neutral
--neutral-bg
--neutral-fg
--neutral-text
--orange
--orange-bg
--orange-fg
--orange-text
--pink
--pink-bg
--pink-fg
--pink-text
--purple
--purple-bg
--purple-fg
--purple-text
--radius-full
--radius-md
--radius-sm
--red
--red-bg
--red-fg
--red-text
--ring
--space-0
--space-1
--space-2
--space-3
--space-4
--space-5
--state-disabled
--state-hover
--state-press
--state-selected
--success
--success-bg
--success-fg
--success-text
--text-body
--text-lg
--text-sm
--text-xs
--warn
--warn-bg
--warn-fg
--warn-text
--weight-medium
--white
--white-bg
--white-fg
--white-text
--yellow
--yellow-bg
--yellow-fg
--yellow-text
Slots
The button label.
An icon rendered before the label.
An icon rendered after the label.
Accessibility
Code
Variants and tones
The five variants and the six tones are independent. Mix freely.
<xoji-button variant="solid" tone="accent">Save changes</xoji-button>
<xoji-button variant="outline" tone="neutral">Cancel</xoji-button>
<xoji-button variant="subtle" tone="danger" icon-only aria-label="Delete">
<svg slot="icon-start" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path fill="currentColor" d="M9 3h6l1 2h4v2H4V5h4l1-2Zm-3 6h12l-1 12H7L6 9Z" />
</svg>
</xoji-button>
<xoji-button variant="solid" tone="success" loading>Saving…</xoji-button>
<xoji-button variant="link" tone="info" href="/docs">Read the docs</xoji-button>
<script lang="ts">
import { Button } from "@xoji/svelte";
</script>
<Button variant="solid" tone="accent" onclick={() => save()}>Save changes</Button>
<Button variant="outline" tone="neutral">Cancel</Button>
<Button variant="subtle" tone="danger" iconOnly ariaLabel="Delete">
{#snippet iconStart()}
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path fill="currentColor" d="M9 3h6l1 2h4v2H4V5h4l1-2Zm-3 6h12l-1 12H7L6 9Z" />
</svg>
{/snippet}
</Button>
<Button variant="solid" tone="success" loading>Saving…</Button>
<Button variant="link" tone="info" href="/docs">Read the docs</Button>
---
import { Button } from "@xoji/astro";
---
<Button variant="solid" tone="accent">Save changes</Button>
<Button variant="outline" tone="neutral">Cancel</Button>
<Button variant="subtle" tone="danger" iconOnly aria-label="Delete">
<svg slot="icon-start" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path fill="currentColor" d="M9 3h6l1 2h4v2H4V5h4l1-2Zm-3 6h12l-1 12H7L6 9Z" />
</svg>
</Button>
<Button variant="link" tone="info" href="/docs">Read the docs</Button>