LoadingIndicator from @prepared911/ui-core is an indeterminate spinner that signals work in progress. The component renders the animated glyph; your job is to name the busy region, avoid hijacking focus, and pair the spinner with a text label or live announcement.
What the component handles
- Animated glyph. Renders a size-appropriate spinner.
- Size variants.
LoadingIndicatorSize.Small / Regular / Large for inline, block, and page-level use.
- Decorative markup. The SVG is decorative by default; the surrounding container carries the semantics.
- A named busy region. Wrap the spinner (and its adjacent label text) in a container with
role="status" and aria-live="polite", and set aria-busy="true" on the region that is actually loading.
- A text label nearby. "Loading incidents…" beside the spinner is more useful than the spinner alone—especially for screen-reader users who hear the label and can move on.
- Focus that doesn't jump. Don't steal focus when the spinner appears. When loading finishes, restore focus to a known place (the results container with
tabIndex="-1" is a common anchor).
- Long-wait fallback. For operations > ~3 s, pair the spinner with a
Progress bar or descriptive phases ("Preparing… Uploading… Verifying…") so the wait feels informed.
LoadingIndicator is non-interactive and not focusable. If the surrounding region disables actions during load, pair disabled buttons with a tooltip or helper text explaining why.
- Outer container →
role="status" with aria-live="polite".
- Spinner glyph →
aria-hidden="true" (semantics live on the container).
- Accompanying text → plain prose inside the live region so it announces alongside state changes.
- Honor
prefers-reduced-motion: reduce by swapping the spinning animation for a static dot or a progress bar fill. The indicator must still communicate "busy" without motion.
- Spinner without a label. An unlabeled spinner creates silence for screen readers. Always pair it with text, even if the text is visually hidden.
- Overlay lock-ups. Full-screen loading overlays that trap pointer events must also be reachable or dismissible via keyboard; otherwise users get stranded during long loads.