Spinner
A loading indicator, sized to match what is loading. For a value on a scale, use a Meter.
Installation
bun add @harmo-ui/css @harmo-ui/reactnpm install @harmo-ui/css @harmo-ui/reactpnpm add @harmo-ui/css @harmo-ui/reactyarn add @harmo-ui/css @harmo-ui/reactbunx harmo-ui add spinnernpx harmo-ui add spinnerpnpm dlx harmo-ui add spinneryarn dlx harmo-ui add spinnerUsage
import { Spinner } from "@harmo-ui/react";
<Spinner size="large">Loading</Spinner>
<Spinner delay>Loading conversations</Spinner>Examples
Sizes
Match the spinner to what is loading: data-size="small" (16px) inline and in table cells, the default (24px) for a section, and data-size="large" (40px) for a whole page or tab. On its own, each takes role="status" and is announced as "Loading".
<>
<Spinner size="small">Loading</Spinner>
<Spinner>Loading</Spinner>
<Spinner size="large">Loading</Spinner>
</><span class="hspinner" data-size="small" role="status">
<span class="hspinner-label">Loading</span>
</span>
<span class="hspinner" role="status">
<span class="hspinner-label">Loading</span>
</span>
<span class="hspinner" data-size="large" role="status">
<span class="hspinner-label">Loading</span>
</span>Delayed
data-delay keeps it hidden for 300ms, so quick loads never flash a spinner. Replay to watch it appear.
<Spinner delay>Loading</Spinner><span class="hspinner" data-delay role="status">
<span class="hspinner-label">Loading</span>
</span>Inside a button
When the control's label already explains the wait, the spinner is aria-hidden, and the button draws it at 16px in its text color. Button covers loading states in full.
<Button loading>Saving changes</Button><button class="hbutton" type="button" aria-busy="true" aria-disabled="true">
<span class="hbutton-label">Saving changes</span>
<span class="hspinner" aria-hidden="true"></span>
</button>Custom size and color
--hspinner-size and --hspinner-stroke set the size and ring width, and the arc follows the element's color.
<>
<Spinner
style={
{ "--hspinner-size": "3.5rem", "--hspinner-stroke": "4px" } as CSSProperties
}
>
Loading
</Spinner>
<Spinner style={{ color: "var(--harmo-green)" }}>Loading</Spinner>
</><span class="hspinner" style="--hspinner-size: 3.5rem; --hspinner-stroke: 4px" role="status">
<span class="hspinner-label">Loading</span>
</span>
<span class="hspinner" style="color: var(--harmo-green)" role="status">
<span class="hspinner-label">Loading</span>
</span>API reference
React
<Spinner> renders the spinner with role="status"; its children, which are required, are the label screen readers announce, such as "Loading". With aria-hidden="true", it's decorative and has neither. A Button's loading renders one for you. Other props are the <span>'s attributes.
| Prop | Effect |
|---|---|
size | small or large |
delay | Keeps it hidden for its first 300ms |
HTML
A <span class="hspinner">. On its own, it takes role="status" and a visually hidden .hspinner-label; inside a control whose label explains the wait, aria-hidden="true". Load spinner.css. It needs no script: render it while something loads and remove it when done.
| Hook | Effect |
|---|---|
data-size="small" | 16px, for buttons, table cells, and inline |
| (default) | 24px, for a section while the rest of the page shows |
data-size="large" | 40px with a 3px ring, for a whole page or tab |
data-delay | Stays hidden for 300ms, so quick loads never flash a spinner |
--hspinner-size, --hspinner-stroke | The size and the ring width |
color | The arc's color |
See Spinner's full contract.
Accessibility
A standalone spinner takes role="status" with a visually hidden .hspinner-label, so screen readers announce what is loading. Inside a control whose label explains the wait, it is aria-hidden.