Zetta v1.6

Components · Data display & feedback

Spinner

Indeterminate loading indicator. Split from zetta-feedback-v1_4.md (removed in v1.5) in v1.4.

Preview

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Spinner" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `spinner` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `spinner` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

  ◌   ← spinner-track ({colors.spinner-track}, 2px border)
  ◕   ← animated arc ({colors.primary}, 2px)
State Visual
Active Continuously rotating arc on gray track
Embedded in button Replaces button label, cursor: wait

When to use

  • Button loading state (replaces button label).
  • Inline loading within a component (e.g. async validation).
  • Small component-level loading where Skeleton is too complex.

When not to use

  • When you know the progress percentage — use Progress Indicator.
  • When loading a content area — use Skeleton.
  • Page-level initial load — use Skeleton for the full layout.

Do

  • Always include a visually hidden label prop for screen readers.
  • Use cursor: wait on the parent when the spinner blocks interaction.

Don't

  • Don't show a spinner for operations that complete in under 300ms — it causes flicker.
  • Don't use spinner and skeleton simultaneously in the same area.
  • Accessible label: "Loading…" (default) or context-specific: "Saving changes…", "Uploading file…".
  • Never show the label text visually alongside the spinner — it is screen-reader only.

Keyboard

Spinner is non-interactive and has no keyboard behavior. It is a visual loading indicator only.

Accessibility

  • role="status" with aria-label="Loading…" (or specific label).
  • aria-live="polite" on the container so screen readers announce when loading completes.
  • When embedded in a button: aria-busy="true" on the button + visually hidden "Loading…" text.

Spec

Values and token references resolved straight from the Zetta spec.

spinner
color
{colors.primary}
trackColor
{colors.spinner-track}
borderWidth
2px
sizeSm
16px
sizeMd
20px
sizeLg
24px