Spinner
Shows an indeterminate loading state with a token-based gradient ring and size variants.
Spinner renders an indeterminate loading indicator. It uses a gradient foreground ring over a neutral background ring, with thickness scaled by design tokens so density adjustments remain visually balanced.
Installation
The component is exported from @by/experience-system. Add the package with your package manager:
pnpm add @by/experience-systemIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.
Composition
Use the following composition to render a Spinner:
SpinnerSpinner is a single-export component built from a styled span element and does not require additional parts.
Usage
import { Button, Spinner } from '@by/experience-system';Spinner is a client component ('use client' in the package). Use it inside a Client Component or a dynamic import when using the Next.js App Router.
A common pattern is an outline Button with a leading Spinner (use data-icon="inline-start" on the spinner so spacing matches icon-leading buttons). Pick a Spinner size explicitly—it is not sized like inline SVG icons inside Button.
<Button type="button" variant="outline" size="md" disabled aria-busy="true">
<Spinner data-icon="inline-start" size="sm" aria-hidden />
Saving…
</Button>You can also render Spinner on its own when no button chrome is needed:
import { Spinner } from '@by/experience-system';
<Spinner size="md" />Examples
Overview
Show progress on an action by placing Spinner inside an outline Button where you would normally put a leading icon. Disable the control while work is in flight, set aria-busy on the Button, and use aria-hidden on Spinner so assistive technology relies on the button label instead of duplicating a generic “Loading” announcement.
Sizes
Use size to scale the spinner: sm, md (default), lg, and xl.
API Reference
Spinner is a Experience System component (not a Radix primitive). It renders a styled span and forwards native HTML span attributes in addition to the Experience System variant props.
Spinner
Spinner applies spinnerVariants({ size }), sets role="status", defaults aria-label to Loading, and adds data-slot="spinner".
| Prop | Type | Default |
|---|---|---|
size | sm | md | lg | xl | md |
className | string | undefined |
Native React.HTMLAttributes<HTMLSpanElement> props are also supported (for example aria-label, aria-labelledby, id, and style).
| Data attribute | Values |
|---|---|
data-slot | spinner |
Accessibility
Spinner announces loading state with role="status" and defaults to aria-label="Loading". Override aria-label (or provide aria-labelledby) when a more specific loading message is required by the UI context.
Source in the repo: packages/experience-system/src/components/Spinner/Spinner.tsx and variants.ts. Agent-oriented contracts: packages/experience-system/src/components/Spinner/Spinner.instructions.md.