Skip to main content

Button

Info:Controls html svelte astro Success: coverage 112/112

A clickable action: five variants across six semantic tones.

Live demo

live · @xoji/astro

Button

Variants × tones

Five visual treatments, six semantic tones — independent axes, mix freely.

Every tone

The matrix above shows the six semantic roles; the full vocabulary adds the accent ramp and the twelve named hues.

Accents

Statuses

Named hues

Sizes

Compact, default, and large.

States

Loading, disabled, toggles, and the anchor form — all from the same component.

As a link

Icons & shapes

Leading and trailing icon slots, the square icon-only form, and a full-width block.

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.

Pair with Field for form submit/reset actions; bind the button type accordingly.
Use the icon-start / icon-end slots with an Icon component once it lands; for now any inline SVG works.
The icon-only form is the seed of a future IconButton convenience wrapper.

Props

11 props, straight from the manifest.

PropTypeDefaultBindingsDescription
variant ButtonVariant
solid outline ghost subtle link
solid
html svelte astro
Visual treatment. Independent of tone.
tone FullTone
accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
accent
html svelte astro
Color tone driving fill, text, and border per variant — any semantic role, accent variant, or named hue.
size Size
sm md lg
md
html svelte astro
Control size.
type "button" | "submit" | "reset" button
html svelte astro
Native button type. Ignored when `href` is set.
href string
html svelte astro
When set, the button renders as an `<a>`. Dropped (with `aria-disabled`) when also disabled.
disabled boolean false
html svelte astro
Disables interaction. On an anchor, applies `aria-disabled` and removes `href`.
loading boolean false
html svelte astro
Shows the spinner, sets `aria-busy`, and blocks interaction.
block boolean false
html svelte astro
Stretches the button to fill its container's width.
iconOnly boolean false
html svelte astro
Square, equal-padding form for a single icon. Requires an accessible name (aria-label).
pressed boolean
html svelte astro
Turns the button into a toggle and reflects its state to `aria-pressed`. Set it (`pressed` / `pressed="true"`) for on, `pressed="false"` for off, and omit it entirely for a plain button. Controlled: flip it in your own click handler; the button reflects state, it does not self-toggle.
selected boolean
html svelte astro
Marks the button as selected within a set and reflects its state to `aria-selected`; distinct from `pressed`'s on/off toggle. Set it (`selected` / `selected="true"`) for selected, `selected="false"` for not, and omit it entirely for a button with no selection semantics. Controlled: flip it in your own click handler; the button reflects state, it does not self-toggle.

Appearance

Variants

solid

.xoji-button--solid

Filled with the tone color; the primary, highest-emphasis treatment.

outline

.xoji-button--outline

Transparent fill with a tone-colored border and text.

ghost

.xoji-button--ghost

No chrome until hover, then a neutral state tint; tone-colored text.

subtle

.xoji-button--subtle

A soft tone-tinted background with tone-colored text.

link

.xoji-button--link

Looks like a link: no box, no padding, underline on hover.

Sizes

xs

.xoji-button--xs

Densest, for compact toolbar pills.

sm

.xoji-button--sm

Compact.

md

default
.xoji-button

Default.

lg

.xoji-button--lg

Large.

States

hover

.xoji-button:hover::after

Pointer over the button: overlay paints the hover tint.

active

.xoji-button:active::after

Button pressed: overlay paints the press tint.

pressed

.xoji-button[aria-pressed="true"]

Toggle engaged: aria-pressed="true" paints the press overlay persistently, so an on toggle reads as sunken across every variant.

selected

.xoji-button[aria-selected="true"]

Selected within a set: aria-selected="true" paints the selected overlay persistently, so a chosen pill reads as active across every variant.

focus-visible

.xoji-button:focus-visible

Keyboard focus: a token-colored ring, plus a transparent outline that becomes real in forced-colors mode.

disabled

.xoji-button:disabled, .xoji-button[aria-disabled="true"]

Non-interactive: muted fill and ink, overlay suppressed.

loading

.xoji-button--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

.xoji-button

The button or anchor element carrying the variant, tone, and size classes.

--font-sans --text-body --weight-medium --leading-tight --border-thin --radius-md --space-2 --space-4 --duration-fast --ease-standard

overlay

.xoji-button::after

The pseudo-element behind the content that paints hover and active state tints.

--state-hover --state-press

icon

.xoji-button__icon

An icon slot rendered before or after the label (icon-start / icon-end).

label

.xoji-button__label

The text content wrapper, hidden while loading so the spinner takes its place.

spinner

.xoji-button__spinner

The inline accessible spinner shown while loading, drawn in currentColor.

--border-normal --radius-full --duration-slow

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 112/112 consumed tokens produced default register: 276 tokens

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

default
html svelte astro

The button label.

icon-start
html svelte astro

An icon rendered before the label.

icon-end
html svelte astro

An icon rendered after the label.

Accessibility

Renders a native <button> (or <a> with href) so keyboard and screen-reader semantics come for free.
disabled blocks interaction; on an anchor it applies aria-disabled and drops href (anchors cannot be natively disabled).
loading sets aria-busy="true" and prevents activation.
pressed makes it a toggle via aria-pressed; it is controlled, so the consumer flips the state on click. The button reflects, it does not self-toggle.
selected marks membership in a set via aria-selected (distinct from pressed); it is controlled the same way. The consumer flips it, the button reflects.
The icon-only form has no visible text, so it REQUIRES an aria-label; the binding warns at runtime when one is missing.
Focus is shown with a token ring and a transparent outline that the forced-colors base rule promotes to a real system outline.
The spinner is decorative (aria-hidden); busy state is conveyed by aria-busy, not the spinner.

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>