Shimmer
An animated highlight sweep that runs across the letterforms of live text —
the "Generating response…" / "Thinking…" affordance you see while an AI assistant
streams or a long task runs. Unlike Skeleton, which
shimmers a gray placeholder block, Shimmer clips the gradient to the real
copy via background-clip: text, so the words themselves shimmer.
The text stays in the DOM and is read by screen readers — the shimmer is purely decorative and is disabled under reduced-motion, forced-colors, and reduced-transparency.
Basic Usage
Settled state (active)
The active prop is what makes this Livewire-native: bind it to a component
property so the shimmer runs only while work is in flight and settles to
plain text when done — no conditional markup, no JavaScript.
Duration
duration accepts any CSS time value and overrides the --shimmer-wk-duration
token for that instance — slower reads as calmer, faster as more urgent.
In context
Because the wrapper defaults to an inline <span>, a shimmer sits inside a
heading, a row, or next to a Spinner.
Livewire
Drive active straight from a component property so the effect mirrors the
real streaming state:
{{-- 1. `$streaming` is true only while the assistant streams tokens --}}
<x-wirekit::shimmer :active="$streaming">
{{ $partialResponse ?: 'Generating response…' }}
</x-wirekit::shimmer>
// 2. Flip $streaming around the streamed call — the shimmer follows it
public bool $streaming = false;
public function ask(): void
{
$this->streaming = true;
$this->stream(to: 'partialResponse', content: '…'); // wire:stream
$this->streaming = false;
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
active |
bool | true |
When false the slot renders as plain inherited-color text (no shimmer). Bind to a Livewire property to gate the effect on streaming state. |
as |
string | 'span' |
Wrapper tag: span, div, p, strong, em. |
duration |
string|null | null |
Any CSS <time>; overrides --shimmer-wk-duration for this instance. |
scope |
string|null | null |
Scoped personalization name. |
Accessibility
- The shimmering text is real text — it stays in the DOM and is announced by screen readers. The shimmer is decorative only.
- Under
prefers-reduced-motion: reducethe sweep animation is removed. - Under
forced-colors: active(Windows High Contrast) the transparent glyph fill is restored to a solid, system-colored glyph so the text never vanishes. - Under
prefers-reduced-transparency: reducethe transparent fill is likewise restored to solid inherited-color text. - For a streaming region, pair the shimmer with an
aria-live="polite"container and announce the settled text once — not every token.
Keyboard Interaction
This component is purely presentational and does not respond to keyboard input.
Pitfalls
- Don't shimmer text the user must read carefully. The effect is for transient status ("Generating…"), not for body copy that lingers.
- Turn it off when done. Bind
activeto the real loading state so settled content renders as ordinary, high-contrast text. - Keep runs short. A shimmer on a long paragraph draws the eye away from the content — reserve it for a status line.
Design Tokens
| Element | Token |
|---|---|
| Travel duration | --shimmer-wk-duration (default 2s) |
| Highlight band half-width | --shimmer-wk-band (default 3ch) |
| Band tilt | --shimmer-wk-angle (default 105deg) |
| Base glyph color | --color-wk-text-muted |
| Highlight band color | --color-wk-text |
The gradient is clipped to the text via background-clip: text, which is in the
supported-browser baseline (Chrome/Edge 111, Safari 16.4, Firefox 128).