Summit Themes
Blog

Gradients in Tailwind CSS v4

Tailwind CSS v4 overhauled gradients more thoroughly than most people realize. The familiar bg-gradient-to-r class is gone — replaced by a cleaner API that adds proper radial and conic gradients, first-class angle support, and a built-in interpolation modifier that lets you pick your color space. If you've been writing gradients from muscle memory, it's worth taking twenty minutes to understand what actually changed and why the new approach is better.

This article walks through every gradient type in v4, explains color stop positioning, covers the interpolation modifier in enough detail to use it confidently, and shows how the CSS-first @theme block ties the whole system together. All class names are verified against the current Tailwind v4 documentation.

The renamed starting point: linear gradients

The most immediate breaking change: bg-gradient-to-r and friends are now bg-linear-to-r. The eight direction variants map directly:

bg-linear-to-t     /* to top */
bg-linear-to-tr    /* to top right */
bg-linear-to-r     /* to right */
bg-linear-to-br    /* to bottom right */
bg-linear-to-b     /* to bottom */
bg-linear-to-bl    /* to bottom left */
bg-linear-to-l     /* to left */
bg-linear-to-tl    /* to top left */

Color stops use the same from-*, via-*, to-* utilities as v3, so this pattern still works:

<div class="h-14 bg-linear-to-r from-cyan-500 to-blue-500"></div>

Angle-based gradients

New in v4: you can specify an angle directly without using a direction keyword. bg-linear-45 creates a 45-degree gradient. Negative values work too:

<div class="bg-linear-65 from-violet-500 to-fuchsia-400 h-20"></div>

For a completely custom angle or a hand-written gradient string, use bracket notation:

<div class="bg-linear-[25deg,red_5%,yellow_60%,lime_90%,teal]"></div>

Inside brackets, spaces in CSS values are replaced with underscores — the same convention Tailwind uses throughout its arbitrary-value system.

Radial gradients

Radial gradients are genuinely new in v4. The base class is bg-radial, which centers the gradient at 50% 50% by default. To move the origin, use bracket notation with the at keyword:

<div class="size-32 rounded-full bg-radial-[at_25%_25%] from-white to-zinc-900 to-75%"></div>

<div class="h-48 bg-radial-[at_70%_30%] from-sky-200 via-blue-400 to-indigo-900 to-90%"></div>

The position value accepts any valid CSS radial-gradient position — percentages, keywords, or lengths. Because it uses arbitrary-value syntax, you get full flexibility without any configuration.

Radial gradients pair naturally with the to-* percentage utilities to control where the solid color begins — to-75% in the example above means the gradient fades to indigo by 75% of the element's radius, giving you a sharper falloff.

Conic gradients

Conic gradients sweep colors around a central point. The base class is bg-conic; add a degree value to rotate the starting angle:

<div class="size-24 rounded-full bg-conic from-red-500 via-yellow-400 to-green-500"></div>

<div class="size-24 rounded-full bg-conic-180 from-indigo-600 via-indigo-50 to-indigo-600"></div>

The second example — same color at both ends with a light midpoint — is a common trick for a glowing highlight effect when applied to a circular element. The 180-degree rotation just shifts where the seam falls.

Color stop positioning

All three gradient types share the same color stop system. You can add percentage classes immediately after a color class to pin exactly where it starts:

<div class="bg-linear-to-r
            from-indigo-500 from-10%
            via-sky-500 via-30%
            to-emerald-500 to-90%"></div>

Without position utilities, the browser distributes stops evenly. Explicit positions let you create gradients with quick transitions on one side and slow ones on the other, or push most of the gradient into one portion of the element — useful for text-overlay overlays where you want most of the element to be transparent and only the bottom 20% to go dark.

For a color that falls on an unusual value, use bracket syntax for the stop itself:

<div class="bg-linear-to-r from-purple-500 from-[15%] to-pink-400 to-[85%]"></div>

Color interpolation: the modifier that actually matters

This is the most significant conceptual addition in v4. Gradients in CSS can interpolate between two colors in different color spaces, and the space you choose changes the visual result substantially when the colors are far apart on the color wheel.

Tailwind v4 defaults to oklab interpolation for all gradients. Oklab is a perceptually uniform color space, which means the gradient's midpoint looks roughly as bright and saturated as the endpoints — you don't get the muddy gray in the middle that sRGB gradients often produce between, say, blue and orange.

Apply an interpolation modifier with a / after the gradient direction class:

bg-linear-to-r/srgb      /* standard sRGB — the old browser default */
bg-linear-to-r/hsl       /* interpolates through hue, saturation, lightness */
bg-linear-to-r/oklab     /* perceptually uniform (the Tailwind v4 default) */
bg-linear-to-r/oklch     /* oklch polar — vivid hues, passes through the color wheel */

HSL and oklch are polar color spaces — they interpolate by rotating through hue. This means a gradient from red to blue passes through purple, whereas sRGB would pass through a grayish midpoint. Whether that's what you want depends on the design.

Here is a side-by-side comparison worth building in a browser:

<div class="h-12 bg-linear-to-r/srgb  from-indigo-500 to-teal-400"></div>
<div class="h-12 bg-linear-to-r/oklab from-indigo-500 to-teal-400"></div>
<div class="h-12 bg-linear-to-r/oklch from-indigo-500 to-teal-400"></div>

The sRGB version will look slightly dull in the middle. Oklab stays vivid and even. Oklch rotates through the hue wheel, potentially introducing a green or cyan band in the midpoint — sometimes exactly what you want for a vibrant hero section, sometimes not.

The modifier also accepts hue interpolation hints for polar spaces:

bg-conic/[in_hsl_longer_hue]   /* traverse the long way around the hue wheel */
bg-linear-to-r/shorter          /* shorter hue path between two colors */
bg-linear-to-r/increasing       /* increasing hue value */
bg-linear-to-r/decreasing       /* decreasing hue value */

These map directly to CSS Color Level 4 hue interpolation methods and are mostly useful for conic gradients that sweep through a rainbow of colors.

CSS-first theme setup with @theme

Tailwind v4 dropped the JavaScript config file in favor of a CSS-first @theme block. For gradients, this is primarily relevant when you want custom brand colors available as from-*, via-*, and to-* utilities:

/* your main CSS file */
@import "tailwindcss";

@theme {
  --color-brand-500: oklch(0.62 0.22 258);
  --color-brand-300: oklch(0.78 0.16 258);
  --color-accent-500: oklch(0.70 0.20 150);
}

Once defined, these tokens work identically to built-in palette utilities:

<div class="bg-linear-to-r from-brand-500 to-accent-500"></div>

Note that v4's entire default palette is defined in oklch internally, so defining your own tokens in oklch keeps the system consistent. The numbers are oklch(lightness chroma hue) — lightness ranges 0–1, chroma roughly 0–0.37, hue 0–360.

You can also store entire gradient strings in the theme, using the --background-image-* namespace:

@theme {
  --background-image-hero: linear-gradient(
    101.98deg,
    oklch(0.53 0.25 259) 41.25%,
    oklch(0.75 0.19 194) 130.18%
  );
}

This becomes available as bg-hero in your markup. Useful if a complex gradient is used in more than one place and you want a single source of truth.

For one-off gradients that don't belong in the theme, reference a CSS custom property directly using parenthesis shorthand:

<div class="bg-linear-(--my-gradient)"></div>

This is shorthand for bg-linear-[var(--my-gradient)]. Tailwind automatically wraps it in var().

A practical example: a service-card hero

Putting it together, here is a realistic pattern for a dark hero section with a subtle radial glow, using custom brand colors and oklab interpolation:

@theme {
  --color-brand-600: oklch(0.48 0.22 258);
  --color-brand-950: oklch(0.18 0.10 258);
}
<section class="relative overflow-hidden bg-brand-950">
  <!-- radial glow centered top -->
  <div class="absolute inset-0
              bg-radial-[at_50%_0%]
              from-brand-600/30
              to-transparent
              to-60%">
  </div>

  <!-- bottom fade to solid -->
  <div class="absolute bottom-0 left-0 right-0 h-32
              bg-linear-to-t/oklab
              from-brand-950
              to-transparent">
  </div>

  <div class="relative z-10 px-6 py-24 text-white">
    <h1>Your content here</h1>
  </div>
</section>

The radial glow uses /30 color opacity on the from-* color to keep it subtle. The bottom fade uses /oklab so the transition to solid color stays perceptually smooth rather than washing out.

What to watch out for

A few things trip people up when migrating from v3:

  • Class rename. Any bg-gradient-to-* in existing code needs to become bg-linear-to-*. A project-wide find-and-replace handles this in minutes.
  • Default interpolation changed. If your v3 gradients looked a certain way, they may look subtly different in v4 because the default color space shifted from the browser default (sRGB) to oklab. Usually it's an improvement, but verify visually.
  • Conic and radial need no polyfill. Browser support for conic-gradient() and radial-gradient() is near-universal for several years now. The in oklab interpolation syntax (CSS Color Level 4) has strong support in evergreen browsers; the main gap is older Safari/Firefox releases, but that percentage is small and shrinking.
  • Arbitrary gradient strings inside brackets use underscores for spaces. bg-linear-[25deg,red_5%,yellow_60%,teal] — not spaces, underscores. Easy to forget.

Tailwind v4's gradient system is genuinely more capable than v3's, and the interpolation modifier alone is worth the migration effort — gradients that previously looked muddy between two saturated colors now just work correctly out of the box. Once you get comfortable with the new class names and the /modifier syntax for color space, you'll rarely need to reach for hand-written CSS gradient strings.