Text filled with a gradient of theme colors that slowly drifts, or a shiny variant that sweeps a glint of light across it on a pause. Static under reduced motion; plain text in forced colors.
Introducing Acme 3
The calm way to ship software
One place for previews, rollbacks and logs. No pager at 3 a.m.
import { GradientText } from "@/components/ballmac/gradient-text"
export default function GradientTextDemo() {
return (
<div className="flex max-w-xl flex-col items-center text-center">
<p className="font-mono text-xs tracking-widest text-muted-foreground uppercase">Introducing Acme 3</p>
<h2 className="mt-4 text-4xl font-semibold tracking-[-0.04em] sm:text-6xl">
The calm way to{" "}
<GradientText className="pb-[0.08em]">ship software</GradientText>
</h2>
<p className="mt-4 max-w-sm text-balance text-muted-foreground">
One place for previews, rollbacks and logs. No pager at 3 a.m.
</p>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/gradient-textInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/gradient-text.tsx// Ballmac UI: Gradient Text. https://ui.ballmac.com/components/gradient-text "use client" import * as React from "react" import { animate, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type GradientTextElement = "span" | "p" | "div" | "h1" | "h2" | "h3" | "h4" type GradientTextProps = React.ComponentProps<"span"> & { /** The element to render. */ as?: GradientTextElement /** "flow" drifts the gradient across the text; "shiny" keeps it still and sweeps a glint of light over it. */ variant?: "flow" | "shiny" /** Gradient colors as CSS colors or variables. */ colors?: string[] /** Seconds for one drift (flow) or one sweep (shiny). */ duration?: number /** Shiny only: seconds between sweeps. */ repeatDelay?: number /** Gradient angle in degrees. */ angle?: number /** Shiny only: color of the glint. White reads best over saturated colors. */ shineColor?: string } function GradientText({ as = "span", variant = "flow", colors = ["var(--chart-1)", "var(--chart-4)", "var(--chart-5)", "var(--chart-3)"], duration, repeatDelay = 2.4, angle = 100, shineColor = "rgb(255 255 255 / 0.95)", className, style, ...props }: GradientTextProps) { const Comp = as as React.ElementType const ref = React.useRef<HTMLElement>(null) const reduceMotion = useReducedMotion() const seconds = duration ?? (variant === "flow" ? 6 : 1.4) React.useEffect(() => { const node = ref.current if (!node || reduceMotion) return const controls = variant === "flow" ? animate(node, { "--gradient-x": ["0%", "100%"] }, { duration: seconds, ease: "easeInOut", repeat: Infinity, repeatType: "mirror" }) : animate(node, { "--shine-x": ["100%", "0%"] }, { duration: seconds, ease: [0.4, 0, 0.2, 1], repeat: Infinity, repeatDelay }) return () => { controls.stop() node.style.removeProperty("--gradient-x") node.style.removeProperty("--shine-x") } }, [variant, seconds, repeatDelay, reduceMotion]) // Flow repeats the first color at the end so the drift has no hard edge. const gradient = `linear-gradient(${angle}deg, ${[...colors, ...(variant === "flow" ? [colors[0]] : [])].join(", ")})` const glint = `linear-gradient(${angle + 10}deg, transparent 40%, ${shineColor} 50%, transparent 60%)` return ( <Comp ref={ref} data-slot="gradient-text" data-variant={variant} className={cn( "inline-block bg-clip-text bg-no-repeat text-transparent [-webkit-background-clip:text] forced-colors:bg-none forced-colors:text-[CanvasText]", className )} style={{ backgroundImage: variant === "flow" ? gradient : `${glint}, ${gradient}`, backgroundSize: variant === "flow" ? "300% 100%" : "250% 100%, 100% 100%", // Resting positions double as the reduced-motion state: a centered gradient, glint parked off the text. backgroundPosition: variant === "flow" ? "var(--gradient-x, 30%) 50%" : "var(--shine-x, 100%) 0%, 0% 0%", ...style, }} {...props} /> ) } export { GradientText, type GradientTextProps }Update the import paths to match your project setup.
Usage
import { GradientText } from "@/components/ballmac/gradient-text"The full example is in the Code tab above.
Examples
Shiny
Acme Plus
New every weekimport { GradientText } from "@/components/ballmac/gradient-text"
export default function GradientTextShiny() {
return (
<div className="flex flex-col items-center gap-6 text-center">
<GradientText as="h2" variant="shiny" className="text-5xl font-bold tracking-[-0.04em]">
Acme Plus
</GradientText>
<GradientText
variant="shiny"
colors={["var(--chart-2)", "var(--chart-1)"]}
angle={90}
duration={1.2}
className="text-sm font-medium"
>
New every week
</GradientText>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
asThe element to render. | GradientTextElement | "span" |
variant"flow" drifts the gradient across the text; "shiny" keeps it still and sweeps a glint of light over it. | "flow" | "shiny" | "flow" |
colorsGradient colors as CSS colors or variables. | string[] | ["var(--chart-1)", "var(--chart-4)", "var(--chart-5)", "var(--chart-3)"] |
durationSeconds for one drift (flow) or one sweep (shiny). | number | — |
repeatDelayShiny only: seconds between sweeps. | number | 2.4 |
angleGradient angle in degrees. | number | 100 |
shineColorShiny only: color of the glint. White reads best over saturated colors. | string | "rgb(255 255 255 / 0.95)" |
Also accepts the standard attributes of its root element.
Use with AI
Wrap a few words: <GradientText>ship software</GradientText> inside a heading, or as="h2". variant="flow" (default) drifts the gradient; variant="shiny" adds a periodic white glint. Colors default to --chart-1/4/5/3. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Gradient Text (@ballmac/gradient-text) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Highlighting two or three words in a hero headline
- Product or plan names (shiny)
Not for
- Body text or long passages (contrast varies along the gradient)
- A muted loading shimmer on status text (use shimmer-text)
Registry JSON: https://ui.ballmac.com/r/gradient-text.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Word Rotate
Cycles one word of a sentence with a vertical slide and blur while its width springs to the next word, so the rest of the line reflows smoothly. Screen readers hear one stable sentence.
Shimmer Text
Text with a band of light sweeping across it, from muted to full foreground color. Suited to AI "thinking" and loading status lines; static under reduced motion.
Highlighter
Hand-drawn marks around inline text: highlight, underline, box, circle, strike-through and brackets, drawn in when the phrase scrolls into view.
Hyper Text
Characters flip through random letters like a split-flap board and settle left to right, as plain text or on tiles, on load, on scroll or on hover and focus.