Spinner
An accessible loading indicator — renders role="status" with an aria-label (default "Loading")
so assistive technology announces the busy state without any extra work. Use it standalone for a
page-level or section-level loader, or embed it inline inside a button or label.
The examples are organised by layer: the class reference is the standalone
@oriui/css layer, and the Framework API is the @oriui/vue component. Every
example is live — flip its code between HTML (the standalone classes, also your htmx / Astro / Svelte /
plain-HTML usage), Vue; HTML is the default.
Classes
A spinner is a single block class. Each axis is one class that repoints one token — no base class needed. The Vue props in Framework API map 1:1 to these. There is no variant or radius — a spinner only takes a size and an optional color.
| Class | Type | Description |
|---|---|---|
ori-spinner | Block | Required base class. |
ori-spinner_* (size) | Size | text (inherits) · xs · sm · md · lg · xl · xxl — text is the default; bare ori-spinner without a size modifier inherits its size from the surrounding font |
ori-color_* | Color | primary · secondary · success · warn · danger · info · surface · background — inherits currentcolor when omitted |
ori-spinner_inline | Layout | switches to inline-block with a small margin so it sits in a line of text |
role=status · aria-label | State | live region that announces the busy state; label defaults to Loading |
Sizes
xs → xxl, plus the default text which inherits its size from the surrounding font.
<!-- ori-spinner_<size> sets the ring diameter; omit the size modifier to inherit from text -->
<div class="ori-spinner ori-spinner_sm ori-color_primary" role="status" aria-label="Loading"></div>
<div class="ori-spinner ori-spinner_xl ori-color_primary" role="status" aria-label="Loading"></div>
Colors
Every semantic role. Omit color entirely to inherit currentcolor from the parent.
<!-- swap the color class: ori-color_primary → _secondary / _success / _warn / _danger / _info -->
<div class="ori-spinner ori-spinner_lg ori-color_danger" role="status" aria-label="Loading"></div>
Inline
inline switches the spinner to inline-block with a small margin so it flows in a sentence or
sits beside a label without breaking layout.
<div class="ori-spinner ori-spinner_inline ori-spinner_sm ori-color_primary" role="status" aria-label="Loading"></div>
Custom label
Override the default "Loading" with a more descriptive message for the current context.
<div class="ori-spinner ori-spinner_md ori-color_primary" role="status" aria-label="Saving your changes"></div>
Common patterns
A full-page loader and a button-level spinner — the everyday compositions.
<!-- page-level -->
<div style="display: flex; justify-content: center; padding: 2rem">
<div class="ori-spinner ori-spinner_xl ori-color_primary" role="status" aria-label="Loading page"></div>
</div>
<!-- inside a loading button: spinner is decorative, parent carries the announcement -->
<button class="ori-button …" aria-busy="true">
<div class="ori-spinner ori-spinner_inline ori-spinner_sm" role="status" aria-hidden="true"></div>
<span class="ori-button__text">Saving</span>
</button>
Accessibility
The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes.
- Renders a
<div role="status">— a polite live region that announces itsaria-labelto screen readers when it appears. - The default
aria-labelis"Loading". Use thelabelprop to provide a more specific message ("Saving your changes","Fetching results", …). - When the spinner is decorative — e.g. inside an
aria-busybutton that already carries the announcement — passaria-hidden="true"as a fall-through attribute so it is invisible to assistive technology. - No keyboard interaction is required; the spinner is not focusable.
Framework API
The props, events, and slots of the Vue component. The standalone CSS layer has no component API — its surface is the classes above. (Svelte bindings are planned.)
Props
| Prop | Type | Default | Description |
|---|---|---|---|
color | ThemeColor | — | Semantic color role. Omit to inherit currentcolor from the parent. |
inline | boolean | false | Renders as inline-block with a small margin for use inside text or buttons. |
label | string | 'Loading' | aria-label text read by assistive technology. |
size | ActionSize | 'text' | Ring size (xs–xxl). Default 'text' inherits its size from the font. |
ThemeColor: 'primary' | 'secondary' | 'success' | 'warn' | 'danger' | 'info' | 'surface' | 'background'
ActionSize: 'text' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'
Events & attributes
OriSpinner declares no custom events and does not set inheritAttrs: false, so any extra
attributes you pass (e.g. class, style, data-*) fall through to the root <div>. The
aria-label is set from the label prop (the template binds :aria-label="label" directly);
change it via that prop, not as a fall-through attribute.
When the spinner is embedded inside a control that already has an accessible name (a loading
button, for example), set aria-hidden="true" on the spinner — because aria-hidden is not
explicitly bound in the template it does fall through to the root element, so the live region
will be hidden from assistive technology.
Slots
OriSpinner has no slots. Its visual output is a pure CSS ring driven entirely by class and attribute props.