ShinyText
// preview
Just shine on
Text
Sweep time (s)
Rest between sweeps (s)
Band angle (deg)
Band width
Base color
#8e8e97
Disabled
// source
TSJS
TailwindCSS
/*!
* ShinyText, a DesignPass.dev component by Ernest Liu (ernestliu.com)
* Docs & live playground: https://designpass.dev/components/shiny-text
* MIT licensed, keep this notice in copies and adaptations.
*/
"use client";
import React, { useEffect, useRef, type CSSProperties, type ReactNode } from "react";
export interface ShinyTextProps {
children: ReactNode;
/** Seconds for one shine sweep across the text. */
speed?: number;
/** Seconds of rest between sweeps. */
rest?: number;
/** Base text color the shine sweeps over. */
color?: string;
/** Color of the moving highlight band. */
shineColor?: string;
/** Angle of the shine band in degrees. */
angle?: number;
/** Width of the highlight band as a fraction of the text (0-1). */
spread?: number;
disabled?: boolean;
className?: string;
style?: CSSProperties;
}
/**
* Text with a light band that sweeps across the glyphs.
* The gradient is clipped to the text and driven by the Web Animations API,
* no keyframe CSS and no dependencies. Honors prefers-reduced-motion.
*/
export default function ShinyText({
children,
speed = 2.4,
rest = 1.2,
color = "#8e8e97",
shineColor = "rgba(255,255,255,0.95)",
angle = 120,
spread = 0.12,
disabled = false,
className = "",
style,
}: ShinyTextProps) {
const ref = useRef<HTMLSpanElement>(null);
useEffect(() => {
const el = ref.current;
if (!el || disabled) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
// The gradient is twice the element's width, so sliding the background
// position from 100% to -100% carries the band fully across the glyphs.
// A trailing hold keyframe gives the shine a beat of rest between sweeps.
const total = speed + rest;
const animation = el.animate(
[
{ backgroundPosition: "100% 0" },
{ backgroundPosition: "-100% 0", offset: speed / total },
{ backgroundPosition: "-100% 0" },
],
{
duration: total * 1000,
iterations: Infinity,
easing: "linear",
},
);
return () => animation.cancel();
}, [speed, rest, disabled]);
const half = Math.min(Math.max(spread, 0.01), 0.5) * 50;
return (
<span
ref={ref}
className={`inline-block bg-clip-text text-transparent ${className}`}
style={{
backgroundImage: `linear-gradient(${angle}deg, ${color} ${50 - half}%, ${shineColor} 50%, ${color} ${50 + half}%)`,
backgroundSize: "200% 100%",
backgroundPosition: "100% 0",
...style,
}}
>
{children}
</span>
);
}
// install
Install the ShinyText component from DesignPass into this project by running:
npx shadcn@latest add "https://designpass.dev/r/ShinyText-TS-TW.json"
If the project has no components.json yet, run `npx shadcn@latest init` first.
Then show me a minimal usage example.Need the license details? Read the component license.