Spin
A loading indicator that can be used standalone or wrap content with an overlaid mask; supports tips, progress, fullscreen and custom indicators.
Basic usage
size supports five tiers: xs / small / medium (default) / large / xl; the old abbreviations sm / md / lg remain supported.
Five sizes
Legacy abbreviations (sm / md / lg)
size also accepts arbitrary CSS sizes: plain numbers are interpreted as px, values with units (2rem / 40px / 10%) and calc() are used as-is.
Arbitrary sizes
Variants
variant provides three forms: ring (default border ring) / dot (three pulsing dots) / bars (three stretching bars). The size system applies to all variants.
ring / dot / bars
Inline dot next to text
Tip text
Use the tip attribute for plain text or the named tip slot for rich content (the slot wins); tip-position controls placement (above / below default / before / after); hide-icon keeps only the text.
Tip text and four positions
Rich tip slot and hide-icon
Wrapping content
When wrapping content, the indicator and tip are centered with a translucent mask; show-overlay="false" disables the mask.
Wrapping content
Disable mask (show-overlay="false")
Delay anti-flicker
When a request finishes faster than the animation can be perceived, a flashing spinner is pure noise. delay (milliseconds) defers the indicator: if loading finishes within the window it never appears; aria-busy takes effect immediately regardless of the delay.
Fast requests don't flicker
Custom indicator
The named icon slot replaces the default ring (size comes from the content itself); the rotate attribute spins the custom indicator — add rotate for regular SVG icons, omit it for GIFs / static SVGs.
icon slot and rotate
Progress mode
Use the default form for unknown durations; when progress is known, percent (0-100) switches to a determinate progress ring (role="progressbar" + the aria-value trio); percent="auto" advances a simulated progress while loading (capped at 90%, restarts after finishing). For deterministic progress the Progress circle form is an alternative.
Determinate progress ring
Stepping progress and auto simulation
Paused
paused freezes looping animations (keeping the current frame) for screenshots / demos / frame inspection.
Paused comparison
Visual customization
Component-level CSS variables: --oas-spin-indicator-color / --oas-spin-track-color / --oas-spin-border-width / --oas-spin-duration / --oas-spin-mask-bg / --oas-spin-z-index. inherit-color makes the indicator follow the host text color; block makes the host occupy a full line.
CSS variable customization
inherit-color follows text color
Fullscreen
The fullscreen attribute enters a fullscreen centered mask; the imperative OASSpin.fullscreen() returns a { close } handle for async flows (calls stack, each handle closes independently).
Fullscreen loading (auto-closes after 2s)
Global default indicator
OASSpin.setDefaultIndicator(html) registers a branded loading animation: afterwards, newly created instances without an icon slot render the registered HTML instead of the built-in ring; pass null to restore. Priority: icon slot > global default > built-in ring.
Register a global indicator and spawn an instance
Accessibility
- The indicator has
role="status"with a visually hidden screen-reader label (locale default "Loading…", readstipwhen set, hostaria-labelwins) - Progress mode switches to
role="progressbar"witharia-valuemin/max/now - Host
aria-busysyncs immediately withspinning; for region loading, also mark the content container witharia-busyand pointaria-describedbyat the spin:
<section aria-busy="true" aria-describedby="page-spin">
<oas-spin id="page-spin" spinning tip="Loading"></oas-spin>
<!-- region content -->
</section>- Animations degrade to a static indicator when the OS enables "reduce motion" (
prefers-reduced-motion)
API
oas-spin
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
aria-label | Screen-reader text (synced with aria-busy) | — | — |
block | Block level filling the container width | — | — |
delay | Anti-flicker delay (ms): renders late after spinning is set; ends within delay = never shows | string | — |
fullscreen | Fullscreen loading (mask covers the viewport) | — | — |
hide-icon | Hide the default indicator (with slot="icon" or text-only) | boolean | — |
inherit-color | Indicator inherits the host text color | — | — |
paused | Pause rotation (freezes progress advance) | — | — |
percent | Progress mode: number for explicit percent, or auto (simulated unknown-progress advance) | string | — |
rotate | Whether the custom indicator auto-rotates (off by default — rotates only when the attribute is present; leave unset for gif/static SVG to stay still) | boolean | — |
show-overlay | Whether to show the mask when wrapping content | string | true |
size | Indicator size: xs / small / medium (default) / large / xl; legacy abbreviations sm/md/lg remain supported | string | medium |
spinning | Whether loading; when set, wraps content with an overlaid mask | boolean | — |
tip | Loading text (below the indicator standalone; centered on the mask when wrapping); rich content via slot="tip" | string | — |
tip-position | Text position | string | below |
variant | Visual variant | string | ring |
Slots
| Name | Description |
|---|---|
| default | Wrapped content (mask overlays in wrapping mode) |
icon | Custom indicator slot, replaces the default ring |
tip | Rich loading text slot; overrides the tip attribute |
CSS Variables
| CSS Variable | Description | Default |
|---|---|---|
--oas-spin-border-width | Stroke width (ring border / progress stroke); 2px for xs and small | 3px |
--oas-spin-duration | Animation duration | 0.8s |
--oas-spin-indicator-color | Indicator color | var(--oas-color-primary) |
--oas-spin-mask-bg | Mask background (wrapping / fullscreen mode) | color-mix(in srgb, var(--oas-color-bg) 70%, transparent) |
--oas-spin-track-color | Track color (ring base / progress track) | var(--oas-color-bg-hover) |
--oas-spin-z-index | Fullscreen z-index | 3500 |
Static methods
| Method | Description |
|---|---|
OASSpin.fullscreen(options?) | Imperative fullscreen loading, returns { close() }; options: { tip?: string; delay?: number } |
OASSpin.setDefaultIndicator(html | null) | Register a global default indicator (affects newly created instances); null restores the built-in ring |
The indicator uses role="status" (role="progressbar" + the aria-value trio in progress mode) with a built-in screen-reader label; host aria-busy syncs with spinning.