Spinner
An animated loading indicator for processes with unknown duration, such as data fetching or form submission. Supports visible labels, multiple sizes, and a dark background variant. For content with known dimensions, use Skeleton instead.
Installation
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
Requires React 19+, base CSS and a Theme provider. See Setup.
Spinner
An animated loading indicator for processes with unknown duration, such as data fetching or form submission. Supports visible labels, multiple sizes, and a dark background variant. For content with known dimensions, use Skeleton instead.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Spinner size: ring diameter (10px, 14px, 18px, 28px). |
| shade | 'default' | 'onMedia' | 'subtle' | 'inherit' | 'default' | Color shade for light or dark backgrounds. |
| label | ReactNode | Visible content below the spinner. String labels auto-set aria-label. | |
| aria-label | string | 'Loading' | Accessible name for screen readers. Defaults to label (if string) or a translated "Loading" from the i18n catalog. |
| xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}. |
Usage guidance
- Provide a meaningful label to describe what is loading for screen reader users.
- Use the "onMedia" shade when placed on dark or accent-colored backgrounds.
- Avoid: Use for content areas with known dimensions; use Skeleton to preserve layout instead.
- Avoid: Stack multiple spinners in the same view; use one to represent the overall loading state.
Theming API
{
"targets": [
{
"className": "astryx-spinner",
"visualProps": [
"size",
"shade"
]
}
],
"vars": [
{
"name": "--spinner-diameter",
"description": "Diameter of the drawn ring. Set it on a size-variant target to retheme what each named size resolves to, e.g. spinner: { 'size:xl': { '--spinner-diameter': '2.5rem' } }. The rendered box is this plus the stroke width on each side, and follows automatically. Any length works: rem, em and calc() are resolved before the ring is drawn.",
"default": "10px (sm), 14px (md), 18px (lg), 28px (xl)"
},
{
"name": "--spinner-stroke-width",
"description": "Stroke width of both circles the ring is drawn from: the moving arc and the track behind it. Set it per size alongside the diameter. One stroke width drives both, so 0 is honoured as a zero-width stroke and paints nothing at all rather than falling back to the default; for an arc with no track behind it, set --spinner-track-color to transparent instead.",
"default": "2px (sm), 3px (md), 3px (lg), 4px (xl)"
},
{
"name": "--spinner-color",
"description": "Color of the moving arc. Defaults to the shade's token, so set it on a shade-variant target to retheme one shade (spinner: { 'shade:subtle': { '--spinner-color': 'var(--color-text-tertiary)' } }), or on the base target to retheme all four. Accepts any color notation, including var(), color-mix() and currentColor.",
"default": "var(--color-accent) (default), var(--color-text-secondary) (subtle), var(--color-on-dark) (onMedia), currentColor (inherit)"
},
{
"name": "--spinner-track-color",
"description": "Color of the track the arc travels on. Set it to `transparent` for an arc with no track. The onMedia and inherit shades draw the track at reduced alpha (30%) so it reads against an arbitrary backdrop; that fade applies to a themed color too.",
"default": "var(--color-track) (default, subtle), var(--color-on-dark) (onMedia), currentColor (inherit)"
},
{
"name": "--spinner-arc-fraction",
"description": "Fraction of the ring the moving arc covers, as a plain number (not a percentage or angle). Set it on a size-variant target the same way as --spinner-diameter, e.g. spinner: { 'size:xl': { '--spinner-arc-fraction': '0.75' } } for a 270deg sweep. Only takes effect once the stylesheet loads; a render with no CSS (SSR, no-JS) always draws the default 135deg arc.",
"default": "0.375 (135deg), same for every size"
}
]
}
Examples
- Spinner: A large spinner indicator.
- Spinner — On Media Shade: Default and onMedia shade spinners for light and dark backgrounds.
- Spinner — Sizes: All spinner sizes displayed side by side.
- Spinner — With Label: Spinners with text and rich multi-line labels.