DesignPass.dev

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.

// updates

Know when new components drop

A short email when something new lands in the library. No noise, unsubscribe anytime.