Skip to content
TrademarkTrademark
Docs
Modules

AI

Updated: October 20, 2026
View on GitHub
Soon
Not released yet. The published package does not include this yet, so it will not install today.

Use these where an assistant is doing something the person is waiting on. Every piece takes the brand color, so inside a themed chat it follows the chat's theme, and every piece keeps saying "working" under reduced motion without moving.

WorkingIndicator

A moving mark and a verb that changes every few seconds, like "Imagining…" or "Noodling…". The first render always shows the first verb, so server and client agree; the order shuffles after mount.

Your own words

Pass verbs to give a product its own voice, or spread workingVerbs to add to the default list. label shows one fixed line instead, for a step you can name.

Elapsed time and a hint

showElapsed counts seconds since startedAt (or since the indicator appeared), and detail adds a quiet hint after it.

Sizes

VoiceVisualizer

The animation for a voice conversation. Each state moves differently: idle breathes, listening ripples, thinking swirls and speaking pulses like syllables. Give it the microphone stream while listening and the reply's <audio> element while speaking, and it follows the real sound. Use orb for a full-screen voice mode and bars for a composer or a call bar.

Levels are written straight to CSS variables every frame, so a live microphone never re-renders React. When your voice SDK already gives you a loudness number, pass it as level instead. To drive something of your own, useAudioLevel(source) returns the same loudness from 0 to 1.

Mascot

A companion for an assistant: one soft, single-color shape with a pair of eyes. It idles, listens, thinks, speaks and sleeps, with a mood on top (happy, surprised, sad, skeptical, wink, focused, determined). By default the eyes are tiles of the Once UI mark, and while it thinks they turn like the LogoSpinner. When it looks around, the eyes slide over the body and the one nearer the edge narrows, as if the body were round; while speaking, the body pulses with the reply's audio.

Make it yours

Every part of the look is a prop, so two products never have to share a character. color takes a scheme or any CSS color and every shade comes from it; roundness, aspect or your own bodyPath set the outline; surface is glossy, matte, flat or glass. eyes is tile, pill, round, wide, dot or your own path, placed with eyeSize, eyeSpacing and eyeHeight. mouth, brows and cheeks add features that follow the mood, accessory draws your own SVG with the body, and personality makes it calm or bouncy. Or pass a seed (a product name, a user id) and mascotLook(seed) picks a whole look; props you pass still win.

LogoSpinner

The four tiles of the Once UI mark, moving. twist parts the tiles, turns a quarter and clicks them back; wave lifts one tile at a time. Keep Spinner for buttons and fields, and use this one where the product's own loader belongs.

Accessibility

WorkingIndicator is one status region with a fixed name, "Working" unless you pass ariaLabel, so a screen reader announces it once instead of reading every new verb. With reduced motion the tiles stop moving and fade in turn, the verbs swap without sliding and the shimmer is off.

VoiceVisualizer is a status region that announces its state ("Listening", "Speaking"). With reduced motion its stand-in animations stop; real audio still shows, as glow on the orb and as bar height, because that is feedback the person needs while they talk.

Mascot is one image named for what it is doing ("Assistant, thinking"). With reduced motion it holds still and keeps its poses and expressions; it does not blink, bob or pulse on its own.

API reference

Type
Default
verbs
The words to cycle through. Defaults to workingVerbs.
readonly string[]workingVerbs
label
A fixed line instead of the cycling verbs.
ReactNode—
interval
Milliseconds each verb stays.
number2800
shuffle
Random order, never the same verb twice in a row.
booleantrue
shimmer
A light sweep across the words.
booleantrue
showElapsed
Show seconds since startedAt.
booleanfalse
startedAt
When the work began, as a Date or epoch milliseconds.
Datenumber
—
detail
Quiet text after the verb.
ReactNode—
loader
The loader before the words; false for none.
ReactNodefalse
—
size
Text and loader size.
sml
m
ariaLabel
What screen readers announce once.
stringWorking
…and every FlexProps propinherited—
Type
Default
state
idle, listening, thinking or speaking.
idlelisteningthinkingspeaking
idle
variant
orb or bars.
orbbars
orb
level
Loudness from 0 to 1; takes precedence over source.
number—
source
A MediaStream, an audio or video element, or an AnalyserNode.
AudioLevelSourcenull
—
size
s, m, l or xl.
smlxl
m
color
brand, accent or neutral.
brandaccentneutral
brand
bars
Number of bars in the bars variant.
number5
ariaLabel
What screen readers announce; defaults to the state.
string—
…and every FlexProps propinherited—
Type
Default
state
idle, listening, thinking, speaking or sleeping.
idlelisteningthinkingspeakingsleeping
idle
mood
neutral, happy, surprised, sad, skeptical, wink, focused or determined.
neutralhappysurprisedsadskepticalwinkfocuseddetermined
neutral
seed
Picks a whole look (mascotLook); props you pass win over it.
stringnumber
—
accessory
SVG drawn with the body in its 200 × 200 box.
React.ReactNode—
lookAt
pointer, center, or a direction from -1 to 1.
pointercenter{ x: number; y: number }
pointer
level
Loudness from 0 to 1 that makes the body pulse while speaking.
number—
source
Audio to pulse with: an element, a stream or an AnalyserNode.
AudioLevelSourcenull
—
size
s, m, l, xl or a number of pixels.
smlxlnumber
l
ariaLabel
What screen readers hear.
string—
color
brand, accent, neutral or any CSS color; every shade comes from it.
brandaccentneutral(string & {})
—
roundness
0, a rounded square, to 1, a circle.
number—
aspect
Width over height: below 1 is tall, above 1 is wide.
number—
bodyPath
Your own outline: an SVG path in a 200 × 200 box.
string—
surface
glossy, matte, flat or glass.
glossymatteflatglass
—
eyes
tile (Once UI, turns while thinking), pill, round, wide, dot, or { path }.
tilepillroundwidedot{ path: string }
—
eyeSize
Scale of the eyes, 1 by default.
number—
eyeSpacing
Distance between the eyes, 1 by default.
number—
eyeHeight
-1 (up) to 1 (down).
number—
eyeColor
Color of the eyes and features.
string—
mouth
none, smile, dot or line; follows the mood and moves while speaking.
nonesmiledotline
—
brows
Brows that follow the mood.
boolean—
cheeks
Blush under the eyes.
boolean—
personality
calm, default or bouncy.
calmdefaultbouncy
—
…and every FlexProps propinherited—
Type
Default
size
Loader size, the same footprint as Spinner.
xllmsxs
m
variant
twist or wave.
twistwave
twist
color
brand, accent, neutral, or inherit for the text color.
inheritneutralbrandaccent
brand
speed
Seconds per cycle.
number—
ariaLabel
What assistive tech announces.
stringLoading
…and every FlexProps propinherited—
Edit this page on GitHub