Skip to main content

Spinner

Info:Feedback html svelte astro Success: coverage 25/25

An indeterminate loading indicator: a spinning ring in any of the six semantic tones, in three sizes.

Live demo

live · @xoji/astro

Spinner

Six semantic tones

accent
neutral
info
success
warn
danger

Every tone

The open vocabulary — semantic roles, the accent ramp, and every named hue.

Accents

accent
accent-2
accent-3
accent-4
neutral

Statuses

success
info
warn
danger

Named hues

red
orange
yellow
green
blue
purple
brown
pink
cyan
gray
white
black

Three sizes

sm
md
lg

In context

Loading projects…
Buttons borrow the same indicator.

Spinner signals that work is in progress without a known endpoint. It draws a rotating ring with a single transparent gap, sized in em so it scales with the surrounding type, and carries role="status" so assistive tech announces the busy state.

tone colors the ring across the six semantic roles (accent, neutral, danger, success, warn, info), and size picks sm, md, or lg. The spin runs on --duration-slow; the reduced-motion base rule freezes it for users who ask for less motion. It is presentational and standalone; Button carries its own inline spinner for the loading state.

When to use

How this component composes with the rest of the set.

Drop inside any container to signal loading; pair with an aria-live region when you also surface text.
Use Button's own loading state for in-button spinners. This standalone Spinner is for page, panel, and inline loading.
Size in em means setting font-size on the spinner (or its parent) scales the ring without a size class.

Props

3 props, straight from the manifest.

PropTypeDefaultBindingsDescription
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
Semantic color role driving the ring color.
size Size
sm md lg
md
html svelte astro
Ring size. Scales with `em`, so it also tracks the surrounding font size.
ariaLabel string Loading
html svelte astro
Accessible name announced for the busy region. Maps to `aria-label`.

Appearance

Variants

accent

.xoji-spinner--accent

Accent-toned ring, the default.

neutral

.xoji-spinner--neutral

Neutral-toned ring.

danger

.xoji-spinner--danger

Danger-toned ring.

success

.xoji-spinner--success

Success-toned ring.

warn

.xoji-spinner--warn

Warn-toned ring.

info

.xoji-spinner--info

Info-toned ring.

Sizes

sm

.xoji-spinner--sm

Compact: thinner ring.

md

default
.xoji-spinner

Default.

lg

.xoji-spinner--lg

Large.

Anatomy

The named parts that make up the component, with their selectors.

spinner

.xoji-spinner

The rotating ring: a bordered circle with one transparent side, animated on the slow duration.

--accent --border-thick --radius-full --duration-slow

Tokens & coverage

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

Success:fully covered 25/25 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-3 --accent-4 --black --blue --border-normal --border-thick --brown --cyan --danger --duration-slow --gray --green --info --neutral --orange --pink --purple --radius-full --red --success --warn --white --yellow

Accessibility

Carries role="status" so the busy state is announced to assistive technology.
Has no visible text, so it requires an accessible name; ariaLabel defaults to "Loading" and the binding warns at runtime if it is cleared.
The ring is decorative; the loading meaning lives in the role and label, not the visual.
Motion routes through --duration-slow; the forced-reduced-motion base rule freezes the animation automatically.

Code

Tones and sizes

The six tones across the three sizes: color and scale are independent.

<xoji-spinner aria-label="Loading"></xoji-spinner>

<xoji-spinner size="sm" tone="neutral" aria-label="Loading"></xoji-spinner>

<xoji-spinner size="lg" tone="success" aria-label="Saving changes"></xoji-spinner>
<script lang="ts">
	import { Spinner } from "@xoji/svelte";
</script>

<Spinner ariaLabel="Loading" />

<Spinner size="sm" tone="neutral" ariaLabel="Loading" />

<Spinner size="lg" tone="success" ariaLabel="Saving changes" />
---
import { Spinner } from "@xoji/astro";
---

<Spinner aria-label="Loading" />

<Spinner size="sm" tone="neutral" aria-label="Loading" />

<Spinner size="lg" tone="success" aria-label="Saving changes" />