Gradient text in Tailwind CSS, static and animated

Make gradient text with Tailwind's bg-clip-text, animate it with one keyframe, or fill it with a live shader.

Updated

To make gradient text in Tailwind CSS, paint a gradient background on the text and clip it to the glyphs with bg-clip-text text-transparent:

<h1
  class="w-fit bg-linear-to-r from-pink-500 via-violet-500 to-sky-400 bg-clip-text pb-1 text-5xl font-semibold text-transparent"
>
  Gradient text
</h1>

That is Tailwind v4. In Tailwind v3 the gradient class is bg-gradient-to-r, and everything else is the same. The text stays real text: selectable, searchable and read by screen readers.

This post covers three levels: the static gradient above, an animated version in pure CSS, and the free Gradient Text component, which fills the letters with a live shader gradient:

Build something alive

How bg-clip-text works

Three classes do the work:

  • bg-linear-to-r from-… via-… to-… paints a linear gradient as the element's background. Use any direction (bg-linear-to-br, bg-linear-45) and any colours, including arbitrary ones like from-[#ff6b6b].
  • bg-clip-text sets background-clip: text, so the background only shows inside the letter shapes.
  • text-transparent hides the text's own colour so the background shows through.

Three things trip people up:

  • A heading spreads the gradient across the page. An <h1> is a block, so its background is as wide as its container, and short text only shows the first colour or two. w-fit shrinks the box to the text, so the whole gradient lands on the words.
  • Inline elements wrap the gradient per line. On a <span> that breaks across lines, each line gets its own slice. Add box-decoration-clone to repeat the full gradient on every line, or make the span inline-block to keep one gradient across the box.
  • Descenders get cut off. Tight leading-none can clip the bottom of a "g" or "y", because the background stops at the element's box. The pb-1 in the examples gives them room. Loosening the line height works too.

Animated gradient text in Tailwind

To animate the colours, make the background twice as wide as the text and slide it. Add a keyframe and an animation to your Tailwind v4 CSS:

app/globals.css
@theme {
  --animate-gradient-x: gradient-x 6s linear infinite;

  @keyframes gradient-x {
    from {
      background-position: 0% center;
    }
    to {
      background-position: 200% center;
    }
  }
}

Then use it with a gradient that starts and ends on the same colour:

<h1
  class="w-fit bg-linear-to-r from-pink-500 via-sky-400 to-pink-500 bg-[length:200%_auto] bg-clip-text pb-1 text-5xl font-semibold text-transparent motion-safe:animate-gradient-x"
>
  Animated gradient text
</h1>

Why it loops without a jump: the background is 200% of the text's width, and moving background-position from 0% to 200% shifts it by exactly one background width. The background repeats, and because the gradient begins and ends on pink, the end frame matches the start. motion-safe: turns the animation off for visitors who prefer reduced motion.

In Tailwind v3, put the keyframe and animation in tailwind.config.js under theme.extend.keyframes and theme.extend.animation instead of @theme.

Live gradient text in React

The CSS version slides one flat gradient sideways. The Gradient Text component fills the letters with a shader gradient instead: soft colour fields that drift through each other, drawn on the GPU and looping seamlessly. Install it with the shadcn CLI:

pnpm dlx shadcn@latest add https://gradients.sh/r/gradient-text.json

The CLI adds components/gradient-text.tsx, the shared components/gradient-surface.tsx and the engine in lib/gradient-engine/. Wrap the words you want filled:

components/headline.tsx
import { GradientText } from "@/components/gradient-text";

export function Headline() {
  return (
    <h1 className="text-5xl font-semibold tracking-tight">
      Build something{" "}
      <GradientText colors={["#ff6b6b", "#c084fc", "#38bdf8"]}>
        alive
      </GradientText>
    </h1>
  );
}

Gradient Text takes its font, size and weight from its parent, so style the heading as usual. It renders a <span>, which means it works inside headings, links and buttons.

How it stays accessible

The visible letters are an aria-hidden copy of the text, masked to the glyph shapes with mask-clip: text, with the gradient canvas inside. The real text sits on top with a transparent fill. Selection, copy, find-in-page and screen readers all use that ordinary text. Browsers without text masks, and the server render, get a static version of the same gradient through background-clip: text, so the text is never invisible.

Props

Gradient Text accepts the same gradient props as the background components. Its defaults are tuned for small shapes: crisper (blur 0.2), faster (speed 0.9), no grain, and no pointer or scroll response.

  • colors: two to six CSS colours, including theme variables like var(--primary).
  • kind: which gradient fills the text. The default is "mesh"; "grain", "aurora", "waves", "radial-glow" and "linear-flow" also work.
  • speed and loopSeconds: how far the colour travels and how often the loop repeats. speed={0} holds it still.
  • scale: higher means smaller, busier colour fields.

The Gradient Text props table lists everything.

Which one to use

ApproachMovesJavaScriptBest for
bg-clip-text gradientNoNoneSection headings, many gradient words per page
bg-clip-text + keyframeSlides sidewaysNoneA hero word that should shimmer
Gradient Text componentColour fields driftAbout 50 KB gzipped with the engine, shared with other gradients.sh componentsOne or two hero words that should feel alive

Each Gradient Text draws its own live gradient, so keep it to a headline word or two rather than whole paragraphs. Off screen it stops drawing, and visitors who prefer reduced motion see one still frame.

Whichever you pick, check contrast. Gradient text is only as readable as its lightest colour against the page, so test both ends of the gradient against your background in light and dark mode.