actions
Spinner
Eighteen pure-CSS activity indicators — rings, dots, bars and shapes — all inheriting their colour from the text around them.
Examples
Sizes and colour
It has no tone prop — the spinner inherits `currentColor`, so it matches whatever it sits inside. Nothing below is a spinner setting: the first group is the five hues the token system exposes plus three neutral foreground steps, and the second is plain CSS colours set straight on the element. Any colour reaches it, token or not.
Eighteen designs
One `variant` prop, eighteen looks, and nothing else changes with it: every design takes the same sizes, inherits the same `currentColor`, carries the same label, and slows rather than freezes under reduced motion. They are composed from eight shared keyframes — geometry and `animation-delay` are what separate the dot ring from the spoke ring.
Rings
ringarcdualdashsegmentcometDots
dotsbouncebeatorbithaloBars
barswavespokesShapes
pulseripplegridflipBold contexts
Twenty-two modern placements for the same eighteen designs — a gradient card, frosted glass, a neon glow, an avatar-upload overlay, a real `loading` Button, a Skeleton with a spinner badge, a video-buffering overlay, and more. The variant axis is already covered above; this is what it looks like dropped into an actual interface.
Loading with text
Give the spinner children and it becomes a labelled group instead of a bare mark: `placement` puts the text before, after, above or below it, and the text is what a screen reader reads out — the group is the live region, so no hidden `label` competes with the sentence on screen. `label={null}` drops the live region and leaves the text as ordinary prose, which is what you want inside something that already announces itself.
endstarttopbottomYour own icon
`icon` swaps the drawing for any element you pass — a Lucide or react-icons component, or the eight hand-written SVGs below — and the component keeps the tedious half: `size` scales it, `speed` paces it, reduced motion slows it instead of freezing it, and the label contract is unchanged. A custom mark turns once a second and `variant` no longer applies, since the variant is the drawing it replaced.
arcatomcompasspinwheelhexorbitradarpetalsSpeed
`speed` scales each design’s own tempo rather than replacing it, so a bounce stays slower than a ring at every setting. It multiplies `--noksha-spinner-duration`, which you can also set directly for a value between the three.
slownormalfastIn context
The spinner is sized off the control scale, so it drops into a button, a field or a line of text without measuring anything.
Checking 214 files — the label is silenced here because the sentence already says what is happening.
Own the source
Noksha ships as a package and as copy-paste source. Take the files and they are yours to change. These are read from the generated registry, so they are exactly what the library ships — never a paraphrase of it.
Self-contained apart from @noksha-ui/core. Or let the CLI do it: npx @noksha-ui/cli add spinner writes them, follows the same dependency graph, and fixes up the imports.
export { Spinner, spinnerGroupVariants, spinnerVariants } from './spinner.js';
export type {
SpinnerPlacement,
SpinnerProps,
SpinnerSize,
SpinnerSpeed,
SpinnerVariant,
} from './spinner.types.js';API reference
Accepted values
- SpinnerSize
xssmmdlgxl2xl3xl4xl- SpinnerSpeed
slownormalfast- SpinnerPlacement
endstarttopbottom- SpinnerVariant
ringarcdualdashsegmentcometdotsbouncebeatorbithalobarswavespokespulseripplegridflip
SpinnerProps
Also accepts everything from React.HTMLAttributes<HTMLSpanElement>.
| Prop | Type | Description |
|---|---|---|
| size | SpinnerSize | — |
| variant | SpinnerVariant | — |
| speed | SpinnerSpeed | — |
| label | string | null | — |
| placement | SpinnerPlacement | — |
| icon | React.ReactNode | — |