JGuang

Spinner

Spinner · Astryx

Preview
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

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.

PropTypeDefaultDescription
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.
labelReactNodeVisible content below the spinner. String labels auto-set aria-label.
aria-labelstring'Loading'Accessible name for screen readers. Defaults to label (if string) or a translated "Loading" from the i18n catalog.
xstyleStyleXStylesStyleX 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.