Shiny text
Free · MITA soft light band sweeps across muted text on a loop or on hover, made for badges like "New: AI receptionist". Pure CSS.
Installation
$ npx shadcn@latest add https://goodui.dev/r/shiny-text.jsonThe file lands in components/goodui/shiny-text.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/shiny-text works too.
Usage
import { ArrowRight } from "lucide-react"
import { ShinyText } from "@/components/goodui/shiny-text"
export function Announcement() {
return (
<a
href="/changelog/ai-receptionist"
className="inline-flex items-center gap-1.5 rounded-full border bg-background px-3 py-1 text-sm"
>
<ShinyText trigger="hover">New: AI receptionist answers missed calls</ShinyText>
<ArrowRight className="size-3.5 text-muted-foreground rtl:rotate-180" />
</a>
)
}When to use
- Announcement pills and eyebrow labels ("New: AI receptionist →") that should glint without being loud.
- Inline text inside a link or button that shines while it is hovered or focused (trigger="hover").
- "Thinking…" or "Generating…" status text in AI features.
- A whole heading with a shimmer: heading with treatment="shimmer" is simpler and keeps size, font and tone in one component.
- A badge that shines: badge with animation="shine" sweeps the whole pill. Use ShinyText when only the text should shine.
- Colorful gradient text without a sweep: use gradient-text.
| If you need | Use |
|---|---|
| Always-on glint for a badge or label | trigger="loop" (default) |
| Shine only while a link or button is hovered or focused | trigger="hover" inside the <a> or <button> |
| Shine when a larger card is hovered | trigger="hover" + data-shiny-group on the card |
| Brand-colored band | shineColor="var(--primary)" |
| Wider, softer band | spread={16} |
Examples
AI status text
<ShinyText duration={2} className="text-sm font-medium">
Drafting your quote…
</ShinyText>Shine when a whole card is hovered
<div data-shiny-group className="rounded-xl border p-4">
<ShinyText trigger="hover" shineColor="var(--primary)">Pro plan · 2 months free</ShinyText>
</div>API reference
| Prop | Type | Default |
|---|---|---|
| duration Seconds per cycle (the sweep takes about two thirds of it). | number | 2.8 |
| trigger "loop" sweeps forever; "hover" sweeps while the text or its parent link/button is hovered or focused. | "loop" | "hover" | "loop" |
| baseColor Text color at rest. Defaults to muted-foreground. | string | — |
| shineColor Color of the light band. Defaults to foreground. | string | — |
| spread Half-width of the band, as a percent of the gradient. | number | 9 |
Also accepts every <span> attribute.
Accessibility
- The text stays real text in a <span>; only its paint changes, so screen readers and copy-paste are unaffected.
- Resting color is muted-foreground by default; for small text on tinted backgrounds pass a stronger baseColor.
- Browsers without background-clip: text show the base color with no sweep.
Motion
- A light band (half-width `spread` %, 9 by default) sweeps from left to right on ease-in-out, then rests off the text for the last third of each `duration` s cycle (2.8).
- trigger="hover" runs the loop while the text, or a parent <a>, <button> or [data-shiny-group], is hovered or has keyboard focus.
- Reduced motion: no sweep; the text shows in its base color.
- Pure CSS (background-position on a clipped gradient); no JavaScript runs.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- Children are the text; there is no text prop.
- Do not set a text color class (text-*): the color is painted by the gradient. Use baseColor and shineColor.
- duration is seconds per cycle; spread is a percentage.
- For hover on a larger area, add data-shiny-group to the container instead of handling hover in JavaScript.
FAQ
Why does my text-* color class not work?
The fill is a background clipped to the text, with a transparent text color. Use baseColor for the resting color and shineColor for the band.
Heading treatment="shimmer" or ShinyText?
For a heading, the Core heading's shimmer is the simpler choice. ShinyText is for inline text anywhere, with a hover trigger and custom colors and band width.
Related
Text filled with a slowly drifting multi-stop gradient, with Aurora, Sunset, Ocean and theme palettes tuned separately for light and dark.
Layered heading: semantic level and size decoupled, sans/serif/mono/display font slots you map to your own fonts, eight ink treatments (gradient, muted tail, outline, marker highlight, hand-drawn squiggle, shimmer…), serif-italic emphasis via _words_, and on-view word, letter and mask reveals.
One badge, every layer: solid, soft, outline, status dot, gradient, glass and neo in six tones, with pulse, shine, bounce-in and count-pop animations, icons, removable chips and clickable filters.
A light sweep runs across the button. Pure CSS; the sweep is removed for reduced motion.