Shimmer Text — .mv-shimmer-text
Pure-CSS light sheen that sweeps across text (background-clip): a “New” badge, AI “thinking…” states with animated dots, and a pulse variant.
| Category | Animated text |
|---|---|
| Type | CSS only (.mv-shimmer-text) |
| Status | stable |
| Keywords | shimmer, shine, shiny, loading, thinking, ai, badge |
When to use
- An AI 'thinking' or loading label needs a light sweep with animated dots
- A 'New' badge or promo label should catch the eye with a subtle sheen
- A lightweight pure-CSS effect is needed on any inline text
Avoid when
- Page content is loading and should reserve its final layout → use Skeleton instead
- A multi-color headline emphasis is wanted rather than a sheen → use Gradient Text instead
- Many shimmering labels on the same screen would compete for attention
Install
node scripts/add.mjs shimmer-text --out ./src/marvelousAI agent with the Marvelous UI MCP server: install_components({ slugs: ["shimmer-text"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, components/shimmer-text/shimmer-text.css.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<div style="display:grid;gap:1.75rem;justify-items:center;text-align:center">
<a href="#" style="display:inline-flex;align-items:center;gap:.5rem;padding:.35rem .9rem;border:1px solid var(--mv-border);border-radius:999px;background:var(--mv-surface);font-size:.875rem;text-decoration:none;box-shadow:var(--mv-shadow-xs)">
<span class="mv-shimmer-text">✨ New: one-click Figma export</span>
<span aria-hidden="true" style="color:var(--mv-fg-muted)">→</span>
</a>
<p style="margin:0;font:700 clamp(1.8rem,4vw,2.6rem)/1.1 var(--mv-font-sans);letter-spacing:-.03em">
<span class="mv-shimmer-text" style="--mv-shimmer-text-color:var(--mv-fg);--mv-shimmer-text-highlight:var(--mv-accent);--mv-shimmer-text-width:3em;--mv-shimmer-text-duration:3s">Ship faster.</span>
</p>
<div style="display:flex;align-items:center;gap:.65rem;padding:.7rem 1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);min-width:17rem;text-align:start">
<span aria-hidden="true" style="width:1.6rem;height:1.6rem;border-radius:50%;background:conic-gradient(from 90deg,var(--mv-accent),var(--mv-info),var(--mv-accent));flex-shrink:0"></span>
<span id="shimmer-thinking" class="mv-shimmer-text" data-dots role="status" style="font-size:.925rem">Analyzing your documents</span>
</div>
<div style="display:flex;gap:1.5rem;flex-wrap:wrap;justify-content:center;font-size:.875rem">
<span class="mv-shimmer-text" data-variant="pulse" role="status">Connecting to server…</span>
<button class="mv-button" data-variant="outline"><span class="mv-shimmer-text" data-trigger="hover">Hover me</span></button>
</div>
</div>
<script type="module">
// Thinking states: cycle the label with a short blur swap.
const el = document.getElementById("shimmer-thinking");
const steps = ["Analyzing your documents", "Searching sources", "Writing the answer"];
let i = 0;
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
const timer = setInterval(async () => {
if (!el.isConnected) return clearInterval(timer);
i = (i + 1) % steps.length;
if (!reduce) await el.animate([{ opacity: 1 }, { opacity: 0, filter: "blur(2px)", translate: "0 -4px" }], { duration: 150, easing: "ease-in" }).finished;
el.textContent = steps[i];
if (!reduce) el.animate([{ opacity: 0, filter: "blur(2px)", translate: "0 6px" }, { opacity: 1 }], { duration: 220, easing: "ease-out" });
}, 2600);
</script>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
data-variant | shine | pulse | shine | shine: a light band sweeps across; pulse: the text breathes between the two colors. |
data-dots | boolean | Adds three dots that appear one by one (“thinking…” state). | |
data-trigger | hover | The sheen only runs on hover / focus (of the element or its parent). | |
data-direction | reverse | Sweeps right to left. |
CSS classes
| Name | Description |
|---|---|
mv-shimmer-text | On an inline element containing text (span, a, p…). |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-shimmer-text-color | var(--mv-fg-muted) | Base text color. |
--mv-shimmer-text-highlight | var(--mv-fg) | Sheen color. |
--mv-shimmer-text-width | 5em | Width of the light band. |
--mv-shimmer-text-angle | 100deg | Sheen angle. |
--mv-shimmer-text-duration | 2.4s | Duration of one cycle (sweep + pause). |
--mv-shimmer-text-delay | 0s | Delay before the first sweep. |
Accessibility
The text stays real text. For a loading state, add role="status" so label changes are announced. Reduced motion: static, readable text with all dots visible.