---
title: Spinner
description: Accessible loading spinner
visibility: guest
draft: false
---

# Spinner

A lightweight, accessible loading indicator. Use it for in-place activity that
has no measurable progress (a form submitting, a panel fetching) — when you can
show a layout preview instead, reach for [Skeleton](/components/skeleton); when
you have a measurable percentage, reach for [Progress](/components/progress).

The spinner announces its loading state to screen readers via `role="status"`
and a visually-hidden label, so assistive technology hears "Loading" regardless
of the animation.

## Basic Usage

:::preview{title="Default spinner"}
<x-wirekit::spinner />
:::

## Sizes

Four sizes cover everything from inline-with-text to a page-center loader.

:::preview{title="Spinner sizes"}
<x-wirekit::row gap="md" align="center">
    <x-wirekit::spinner size="sm" />
    <x-wirekit::spinner size="md" />
    <x-wirekit::spinner size="lg" />
    <x-wirekit::spinner size="xl" />
</x-wirekit::row>
:::

## Color

By default the spinner inherits the surrounding text color (`currentColor`), so
it matches whatever context you drop it into. Set `intent` for a standalone
semantic color — every value maps to a theme token, so it restyles with the
active theme.

:::preview{title="Spinner intents"}
<x-wirekit::row gap="md" align="center">
    <x-wirekit::spinner intent="accent" />
    <x-wirekit::spinner intent="success" />
    <x-wirekit::spinner intent="warning" />
    <x-wirekit::spinner intent="danger" />
    <x-wirekit::spinner intent="neutral" />
</x-wirekit::row>
:::

## In Context

Because the spinner inherits `currentColor`, it sits naturally inside a button,
a badge, or any text run.

:::preview{title="Spinner inside a badge"}
<x-wirekit::row gap="sm" align="center" wrap>
    <x-wirekit::badge intent="info"><x-wirekit::spinner size="sm" /> Syncing</x-wirekit::badge>
    <x-wirekit::badge intent="warning" surface="outline"><x-wirekit::spinner size="sm" /> Generating</x-wirekit::badge>
</x-wirekit::row>
:::

## Livewire

Pair with `wire:loading` to show the spinner only while a request is in flight:

```blade
{{-- 1. The spinner shows only during the saveProfile() round-trip --}}
<div wire:loading wire:target="saveProfile">
    <x-wirekit::spinner intent="accent" />
</div>

{{-- 2. The real content hides while loading --}}
<div wire:loading.remove wire:target="saveProfile">
    {{-- saved state --}}
</div>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | string | `'md'` | `sm`, `md`, `lg`, `xl` |
| `intent` | string\|null | `null` | `null` inherits `currentColor`; or `accent`, `success`, `warning`, `danger`, `neutral` |
| `label` | string | `'Loading'` | Screen-reader accessible name announced via the live region |
| `scope` | string\|null | `null` | Scoped personalization name |

## Accessibility

- The wrapper carries `role="status"` + `aria-live="polite"`, so screen readers
  announce the loading state politely without interrupting.
- The visually-hidden `label` (default `"Loading"`) is the accessible name — set
  it to something specific like `label="Loading results"` when helpful.
- The SVG glyph is `aria-hidden="true"`; the meaning is carried entirely by the
  live-region text, so the loading state is conveyed even when motion is reduced.
- Pair with `aria-busy="true"` on the region being loaded for the fullest
  assistive-technology signal.

## Keyboard Interaction

This component is purely presentational and does not respond to keyboard input.

## Pitfalls

- **Don't use a spinner when you have a layout to preview.** A [Skeleton](/components/skeleton)
  that mirrors the eventual content reduces perceived load time better than a
  bare spinner.
- **Don't use a spinner when you know the percentage.** A [Progress](/components/progress)
  bar communicates "how much longer" — a spinner cannot.
- **Give long loads a specific label.** `label="Loading invoices"` is more useful
  to a screen-reader user than the generic default.

## Design Tokens

| Element | Token |
| --- | --- |
| Default color | `currentColor` (inherited) |
| `intent="accent"` | `--color-wk-accent` |
| `intent="success"` | `--color-wk-success` |
| `intent="warning"` | `--color-wk-warning` |
| `intent="danger"` | `--color-wk-danger` |
| `intent="neutral"` | `--color-wk-text-muted` |

Glyph dimensions (`h-4`/`h-5`/`h-6`/`h-8`) are structural SVG sizes, matching the
icon-sizing convention used across WireKit.

## Further Reading

- [MDN: ARIA `status` role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/status_role)
- [WAI-ARIA: `aria-busy`](https://www.w3.org/TR/wai-aria-1.2/#aria-busy)
