# Gradient Text

The GradientText component paints a line of text with a colour ramp that travels across it on a loop, so a headline word carries the accent colour as movement rather than as a flat fill.

- **Category:** Text
- **Docs:** https://uibeats.com/docs/text/gradient-text
- **Registry:** https://uibeats.com/r/gradient-text.json
- **License:** MIT
- **npm dependencies:** `motion`

## Installation

```bash
npx shadcn@latest add https://uibeats.com/r/gradient-text.json
```

Or install the dependencies and copy the source below:

```bash
npm install motion
```

## When to use

For the one word in a headline that should carry the eye: a product name, the verb in a value proposition. Use it on a short span inside a larger sentence rather than on the whole line: a paragraph of moving gradient is unreadable, and the effect stops meaning anything once everything has it.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | `-` | The text to paint (required) |
| `from` | `string` | `'var(--brand)'` | First and last stop of the gradient. Any CSS colour |
| `via` | `string` | `'var(--accent-pink)'` | Middle stop: the colour that travels across the text |
| `duration` | `number` | `4` | Seconds for one pass of the gradient |
| `className` | `string` | `''` | Additional CSS classes for styling |

## Example usage

```tsx
"use client";

import { GradientText } from "@/components/demo/text/gradient-text";

const GradientTextUsage = () => {
  return (
    <h3 className="text-3xl font-semibold tracking-tight sm:text-4xl">
      Supercharge your <GradientText text="interface" />
    </h3>
  );
};

export default GradientTextUsage;
```

## Source

Save as `components/ui/gradient-text.tsx`.

```tsx
"use client";

import { motion, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

interface GradientTextProps {
  text: string;
  /** First and last stop of the gradient. Any CSS colour. */
  from?: string;
  /** Middle stop: the colour that travels across the text. */
  via?: string;
  /** Seconds for one pass of the gradient. */
  duration?: number;
  className?: string;
}

/**
 * Text painted with a gradient that travels across it.
 *
 * The ramp is mirrored (`from, via, from, via, from`) and the background is
 * sized to twice the element, so shifting the position by exactly one image
 * width returns to the frame it started on. That is what makes the loop
 * seamless without a reverse: a one-directional sweep that snaps back at the
 * end reads as a dropped frame, and `repeatType: "mirror"` reads as a
 * pendulum rather than a sweep.
 *
 * Both colours default to theme tokens rather than hex, so installed into a
 * project with its own accent the text picks that up instead of ours.
 */
export function GradientText({
  text,
  from = "var(--brand)",
  via = "var(--accent-pink)",
  duration = 4,
  className = "",
}: GradientTextProps) {
  const prefersReducedMotion = useReducedMotion();

  return (
    <motion.span
      style={{
        backgroundImage: `linear-gradient(90deg, ${from}, ${via}, ${from}, ${via}, ${from})`,
        backgroundSize: "200% 100%",
        // `background-clip: text` needs the text itself to be transparent for
        // the paint underneath to show through.
        WebkitBackgroundClip: "text",
        backgroundClip: "text",
        color: "transparent",
      }}
      initial={{ backgroundPosition: "0% 50%" }}
      animate={{
        backgroundPosition: prefersReducedMotion ? "0% 50%" : "200% 50%",
      }}
      transition={
        prefersReducedMotion
          ? { duration: 0 }
          : { duration, ease: "linear", repeat: Infinity }
      }
      className={cn("inline-block", className)}
    >
      {text}
    </motion.span>
  );
}

export default GradientText;
```

## Credits

Written by [Claude Code](https://claude.com/claude-code).
