Spinner
Preview Available in release assets
Announce an indeterminate wait with a small CSS-only rotating indicator whose accessible name explains what is loading.
Spinner primary example
<p class="thin-cluster" style="align-items: center">
<span class="thin-spinner" role="status" aria-label="Loading results"></span>
<span aria-hidden="true">Loading results…</span>
</p>Show code
<p class="thin-cluster" style="align-items: center">
<span class="thin-spinner" role="status" aria-label="Loading results"></span>
<span aria-hidden="true">Loading results…</span>
</p>Variant gallery
Each authored example uses the production assets and includes copyable markup.
Inherited text color
The named status communicates the task. The ring inherits surrounding text color; the visible sibling avoids repeating the label in the accessibility tree.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading results" ></span>
<span aria-hidden="true">Loading results…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading results" ></span>
<span aria-hidden="true">Loading results…</span>
</p>Small ring
The documented small attribute changes the ring to one em without altering its name.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading messages" data-size="small"></span>
<span aria-hidden="true">Loading messages…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading messages" data-size="small"></span>
<span aria-hidden="true">Loading messages…</span>
</p>Large ring
A two-rem ring keeps a visible, task-specific label.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading report" data-size="large"></span>
<span aria-hidden="true">Loading report…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading report" data-size="large"></span>
<span aria-hidden="true">Loading report…</span>
</p>Semantic tone choices
Only the ring takes the chosen tone; the accompanying task text remains readable. A tone does not establish an operation's success or failure.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading neutral example" data-tone="neutral"></span>
<span aria-hidden="true">Loading neutral example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading success example" data-tone="success"></span>
<span aria-hidden="true">Loading success example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading warning example" data-tone="warning"></span>
<span aria-hidden="true">Loading warning example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading danger example" data-tone="danger"></span>
<span aria-hidden="true">Loading danger example…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading neutral example" data-tone="neutral"></span>
<span aria-hidden="true">Loading neutral example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading success example" data-tone="success"></span>
<span aria-hidden="true">Loading success example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading warning example" data-tone="warning"></span>
<span aria-hidden="true">Loading warning example…</span>
</p>
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading danger example" data-tone="danger"></span>
<span aria-hidden="true">Loading danger example…</span>
</p>Decorative marker with visible status
The marker is hidden from the accessibility tree because the surrounding live status already carries visible task text.
<p role="status" style="overflow-wrap: anywhere">
<span class="thin-spinner" aria-hidden="true"></span> Saving changes…</p>Show code
<p role="status" style="overflow-wrap: anywhere">
<span class="thin-spinner" aria-hidden="true"></span> Saving changes…</p>Anatomy
- Inline host element with class="thin-spinner"
- Accessible name from role="status" plus aria-label or an associated text label
- Optional visible descriptive text next to the spinner
- Currentcolor-driven ring with a leading accent quadrant
Variants and sizes
- Default 1.25em ring inheriting text color — shown above
- Small (data-size="small", 1em) — shown above
- Large (data-size="large", 2rem) — shown above
- Tone overrides (data-tone="neutral|success|warning|danger") — shown above
- Decorative marker paired with authored visible label (aria-hidden="true") — shown above
States and difficult content
Rendered fixtures exercise authored content and constrained layouts with the same production assets.
Indeterminate wait and enlarged text
Rotation is decorative; the label remains the useful state. Inspect at 200%/400% zoom. Automated root-text checks remain separate from human zoom and announcement review.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading results" ></span>
<span aria-hidden="true">Loading results…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading results" ></span>
<span aria-hidden="true">Loading results…</span>
</p>Reduced motion and forced colors
System reduced-motion preferences slow this CSS-only ring. Forced colors use system track/highlight colors. Enable the real OS modes for human review; browser checks exercise available emulation.
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading system-mode preview" ></span>
<span aria-hidden="true">Loading system-mode preview…</span>
</p>Show code
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading system-mode preview" ></span>
<span aria-hidden="true">Loading system-mode preview…</span>
</p>RTL status
The task remains explicit in mixed-direction content.
<div dir="rtl" lang="ar">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="جار تحميل النتائج" ></span>
<span aria-hidden="true">جار تحميل النتائج…</span>
</p>
</div>Show code
<div dir="rtl" lang="ar">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="جار تحميل النتائج" ></span>
<span aria-hidden="true">جار تحميل النتائج…</span>
</p>
</div>Pending native action snapshot
An application-authored pending response disables repeated activation and marks the native button busy. This static example does not start a background request.
<thin-button>
<button type="button" disabled aria-busy="true" style="box-sizing: border-box; max-inline-size: 100%; flex-wrap: wrap; padding-inline: var(--thin-space-xs)">
<span class="thin-spinner" aria-hidden="true"></span>
<span style="min-inline-size: 0">Saving changes</span>
</button>
</thin-button>Show code
<thin-button>
<button type="button" disabled aria-busy="true" style="box-sizing: border-box; max-inline-size: 100%; flex-wrap: wrap; padding-inline: var(--thin-space-xs)">
<span class="thin-spinner" aria-hidden="true"></span>
<span style="min-inline-size: 0">Saving changes</span>
</button>
</thin-button>Raised and accent surfaces
The same ring inherits the surface's chosen foreground. Color and motion never replace the task label.
<section style="padding: var(--thin-space-s); background: var(--thin-color-surface-raised)">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading raised preview" ></span>
<span aria-hidden="true">Loading raised preview…</span>
</p>
</section>
<section style="padding: var(--thin-space-s); background: var(--thin-color-accent); color: var(--thin-color-accent-text)">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading accent preview" ></span>
<span aria-hidden="true">Loading accent preview…</span>
</p>
</section>Show code
<section style="padding: var(--thin-space-s); background: var(--thin-color-surface-raised)">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading raised preview" ></span>
<span aria-hidden="true">Loading raised preview…</span>
</p>
</section>
<section style="padding: var(--thin-space-s); background: var(--thin-color-accent); color: var(--thin-color-accent-text)">
<p style="overflow-wrap: anywhere">
<span class="thin-spinner" role="status" aria-label="Loading accent preview" ></span>
<span aria-hidden="true">Loading accent preview…</span>
</p>
</section>- Default rotation — shown above
- Reduced-motion slower rotation — shown above
- Forced-colors system CanvasText and Highlight — shown above
- Right-to-left content — shown above
- Inline inside a button or link — shown above
- 200% and 400% zoom — shown above
- Placed on a raised or accent surface — shown above
Interaction and keyboard behavior
The spinner owns no interaction. Any cancel, retry, or dismiss control is authored separately as a native button or link.
Accessibility
Give the spinner an accessible name that describes what is loading, either through role="status" with aria-label on the host or by placing it inside an element whose visible text is announced. When a visible loading label already exists, mark the spinner aria-hidden="true" to avoid duplicate announcements. Never rely on rotation alone to communicate that a task is in progress.
- Contract
NATIVE- Basis
- Styles native HTML elements; no APG widget
API
Use any inline element with class="thin-spinner". Add role="status" and aria-label for a self-describing spinner, or aria-hidden="true" when a sibling label carries the announcement. Size the ring with data-size="small|large" or by overriding --thin-spinner-size; recolor with data-tone or by setting color on an ancestor.
Behavior tier: css-only
Custom properties
--thin-spinner-size1.25emInline and block size of the spinner ring.Fallback
Without Thin-Sky CSS, the accessible name and any adjacent visible text still communicate that a task is in progress; no rotation is required for the announcement.
Assets and dependencies
/components.css
Dependencies: None
Source: packages/ui/src/items/spinner/style.ts
Composition
Derived from the examples and style specs, not authored.