# Gradient text

> Color flows slowly through the letters.

- Type: Component (light)
- Page: https://textlab.dev/components/gradient-text
- Markdown: https://textlab.dev/components/gradient-text.md
- Registry item: https://textlab.dev/r/gradient-text.json
- Source file: `registry/textlab/gradient-text/gradient-text.tsx`
- Dependencies: none
- Variants: flow, conic, spectrum
- Keywords: gradient, animated gradient, rainbow, conic, background clip text

## When to use

- One accent word or short headline per screen.
- Brand moments that should feel alive but calm.

## When not to use

- Body text or long headlines; contrast varies along the gradient.
- Several gradients competing on one page.

## Installation

### CLI

```bash
npx shadcn@latest add @textlab/gradient-text
pnpm dlx shadcn@latest add @textlab/gradient-text
```

The `@textlab` name needs `"registries": { "@textlab": "https://textlab.dev/r/{name}.json" }` in `components.json`. Without it, use the full URL:

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

Import the tokens once in your root layout: `import "@/components/textlab/tokens.css";`

### Manual

1. Install the dependencies: `npm install motion@12`
2. Copy `text-fx` (https://textlab.dev/r/text-fx.json) and this component into `components/textlab/`.

## Usage

```tsx
import { GradientText } from "@/components/textlab/gradient-text/gradient-text";

export function Example() {
  return <GradientText as="h1" text="Color in motion" variant="conic" />;
}
```

## API reference

### GradientText

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` (required) | `string` | – | The copy. |
| `variant` | `"flow" \| "conic" \| "spectrum"` | `"flow"` | Drifting linear gradient, a turning color wheel, or a hue cycle. |
| `colors` | `string[]` | `spectrum tokens` | Gradient stops. |
| `speed` | `number` | `8` | Seconds per full cycle. |
| `as` | `ElementType` | `"span"` | Rendered element. |
| `reducedMotion` | `"user" \| "always" \| "never"` | `"user"` | user follows the OS setting; always shows the still version. |
| `className` | `string` | – | Merged onto the root. |

## Accessibility

- The text is never split; it reads, selects and wraps normally.
- Pick stops that all pass contrast against the background.

## Motion

- A full cycle takes 8 seconds by default, slow enough to read as ambient.
- Conic uses a registered angle property for a smooth rotation.

## Reduced motion

The gradient stays still.

## Performance

- Background position and a registered property; no JavaScript animation.

## Notes for AI

- Use only the props documented above. Do not invent props.
- Keep the screen-reader text and the reduced-motion branch when editing the source.
- Pick a variant from: flow, conic, spectrum.

## Related

- [Shimmer text](https://textlab.dev/components/shimmer-text.md): A calm light passes across the words.
- [Aurora text](https://textlab.dev/components/aurora-text.md): Northern lights drift inside the letters.
- [Holographic text](https://textlab.dev/components/holo-text.md): Foil that shifts color as you move across it.

## Also in light

- [Neon sign](https://textlab.dev/components/neon-text.md): A neon sign that hums and flickers on.
- [Spotlight text](https://textlab.dev/components/spotlight-text.md): A light follows the cursor and reveals the copy.
- [Glass text](https://textlab.dev/components/glass-text.md): Glass letters that bend the scene behind them.
- [Ember text](https://textlab.dev/components/ember-text.md): Letters land hot and cool down to the text color.

## Source

### components/textlab/gradient-text/gradient-text.tsx

```tsx
"use client";

import type { CSSProperties, ElementType } from "react";
import { useReduced, type ReducedMotion } from "../lib/use-reduced";

export type GradientTextVariant = "flow" | "conic" | "spectrum";

export interface GradientTextProps {
  text: string;
  /** flow drifts a linear gradient, conic turns a color wheel inside the letters, spectrum cycles hue. */
  variant?: GradientTextVariant;
  /** Gradient stops. Defaults to the spectrum tokens. */
  colors?: string[];
  /** Seconds per full cycle. */
  speed?: number;
  as?: ElementType;
  reducedMotion?: ReducedMotion;
  className?: string;
  style?: CSSProperties;
}

const CSS = `
@property --tl-angle { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@keyframes tl-gradient-flow { from { background-position: 0% 50%; } to { background-position: 200% 50%; } }
@keyframes tl-gradient-turn { to { --tl-angle: 360deg; } }
@keyframes tl-gradient-hue { to { filter: hue-rotate(360deg); } }
.tl-gradient { background-clip: text; -webkit-background-clip: text; color: transparent; -webkit-text-fill-color: transparent; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
@media (prefers-reduced-motion: reduce) { .tl-gradient[data-motion="user"] { animation: none !important; } }
`;

const DEFAULT = ["var(--tl-fx-spectrum-1)", "var(--tl-fx-spectrum-2)", "var(--tl-fx-spectrum-3)", "var(--tl-fx-spectrum-4)"];

/**
 * Color flows slowly through the letters. The text stays real text, clipped to a gradient, so it
 * selects, wraps and reads like any other heading.
 */
export function GradientText({ text, variant = "flow", colors = DEFAULT, speed = 8, as: Tag = "span", reducedMotion = "user", className, style }: GradientTextProps) {
  const reduced = useReduced(reducedMotion);
  const loop = [...colors, colors[0]].join(", ");
  const look: CSSProperties =
    variant === "conic"
      ? { backgroundImage: `conic-gradient(from var(--tl-angle) at 50% 50%, ${loop})`, animation: `tl-gradient-turn ${speed}s linear infinite` }
      : variant === "spectrum"
        ? { backgroundImage: `linear-gradient(90deg, ${colors.join(", ")})`, animation: `tl-gradient-hue ${speed}s linear infinite` }
        : { backgroundImage: `linear-gradient(90deg, ${loop})`, backgroundSize: "200% 100%", animation: `tl-gradient-flow ${speed}s linear infinite` };

  return (
    <Tag className={["tl-gradient", className].filter(Boolean).join(" ")} data-motion={reducedMotion === "user" ? "user" : undefined} style={{ ...look, ...(reduced ? { animation: "none" } : null), ...style }}>
      <style href="tl-gradient-text" precedence="default">{CSS}</style>
      {text}
    </Tag>
  );
}

export default GradientText;
```

Full library index: https://textlab.dev/llms.txt
