Feedback

Shimmer

An animated bright band sweeping across some text, or over whatever's in the default slot — for an 'AI is thinking...' indicator, a shimmering skeleton block, or a progress bar's buffering state.

html
<UiShimmer />

Text

text sweeps a highlight across the glyphs themselves. Omit it and the default slot is shimmered instead, as a block.

Ada is thinking...

text and the default slot are two different techniques, not two ways of writing the same thing. text clips the gradient to the glyphs themselves (crisp, but only works for actual text); the slot instead wraps whatever you put in it and blends a translucent highlight over the top, so it works on a colored fill, a skeleton block, an image, anything.

In text mode, the resting (non-swept) color is deliberately dimmer than this app's normal body text, not just text-muted — a lighter resting color reads as plain white-on-white text next to everything else on the page, and the bright sweep barely registers against it.

Under prefers-reduced-motion: reduce, the sweep is dropped entirely: text falls back to a plain text-muted color instead of freezing mid-gradient, and the slot mode just hides the overlay, leaving whatever's inside it static.

As

as sets what element or component to render as. div plus a sized, filled box is the skeleton shape.

Duration

duration sets how long one sweep takes, in seconds. Slower reads as calmer — worth it for something that will be on screen a while.

1s sweep2s sweep5s sweep

Spread

spread is the width of the bright band. With text it's a multiplier of the string's length; without it, a pixel width — so the same number means different things depending on which mode you're in.

Generating a response...Generating a response...

spread's unit depends on which mode you're in: with text it's a multiplier of the string's length (text.length * spread px), since a fixed pixel width would look too narrow on a long line and too wide on a short one; without text there's no string to measure, so it's a flat percentage of the element's own width instead.

Active

active stops the sweep without unmounting anything — for a skeleton that should freeze rather than disappear. Only relevant without text.

active only matters without text — it lets you keep content mounted (e.g. ProgressBar's buffer fill) while toggling just the sweep on and off, instead of v-if-ing the whole thing. With text, the sweep always runs while the component is rendered.

Swapped for the real reply once it "arrives"

Ada is typing...

There's no loading prop wiring this to other components — it's a standalone placeholder. Swap it for the real content yourself, e.g. <Shimmer v-if="loading" text="Ada is typing..." /><span v-else>{{ reply }}</span>.

Powering ProgressBar's buffering state under the hood


API Reference

Generated from the component's source — props, slots and emits as the component actually declares them.

Props

Prop
Type
Default
Description
string
Text to render with the sweep-across-glyphs effect. Omit this and put arbitrary content (a progress fill, a skeleton block, an image, ...) in the default slot instead to shimmer that — the two are mutually exclusive.
keyof HTMLElementTagNameMap | Component
'span'
The HTML tag or Vue component to render this as.
number
2
How long one shimmer sweep takes, in seconds.
number
2 with `text`, 30 without
Width of the bright highlight band. With `text`, this is a multiplier of the string's length (the actual spread is `text.length * spread` pixels), so it scales with the string instead of needing a fixed pixel value per usage. Without `text` (shimmering the default slot instead), it's a flat percentage of this element's own width instead, since there's no string length to scale from.
boolean
true
Whether the sweep is currently animating. Only relevant without `text`, for toggling the effect on content that stays mounted either way (e.g. a progress bar's buffering state) — `text` always sweeps while rendered.

Slots

Slot
default