CSS Gradient Builder: Code Examples

Build linear, radial, and conic CSS gradients with a visual editor. Drag color stops, pick presets, and copy ready-to-use CSS, Tailwind, or custom property output.

ZERO UPLOAD · ALL LOCAL
  1. Choose a gradient type with the Linear / Radial / Conic toggle, or click a preset to load a starting point.
  2. Click anywhere on the gradient track to add a color stop, then drag it left or right to reposition it.
  3. Click a handle to select it, then type any CSS color format (HEX, RGB, HSL, or OKLCH) into the color field, or use the swatch to open the browser color picker.
  4. Drag the angle dial to rotate a linear gradient or set the starting angle for a conic gradient. For radial and conic types, set the center position with the X and Y inputs.
  5. Choose CSS, Tailwind, or Vars in the output panel and click Copy to grab the result.
Gradient Type
Presets
Color Stops
0 50 100
Angle
135° deg
Results
 

Export Figma Gradients to CSS

Copying CSS gradient code from Figma takes a single click from the Design panel, but understanding what Figma exports and where the format falls short helps you decide when to use the pasted value as-is and when to refine it manually. Selecting a layer with a gradient fill reveals a CSS copy icon next to the fill swatch in the Fill section.

Figma supports three gradient fill types: Linear, Radial, and Angular. Linear corresponds to CSS linear-gradient(); Radial to radial-gradient(); Angular to conic-gradient().1 Each exports as a CSS-ready string with percentage-based stop positions.2 Figma exports stop colors as HEX values regardless of the input color format, which constrains wide-gamut OKLCH colors to the sRGB gamut.3 For paste-ready gradient values that match any Figma geometry, the gradient builder on this page generates equivalent CSS output.

Copying CSS from the Design panel

The copy icon for gradient CSS appears on the Fill row in the Design panel, not in the Code panel.4 Hover over the gradient fill swatch row and the icon appears at the right edge of the row. The Code panel in Inspect mode also shows CSS output, but the Fill row copy is the faster path for gradient values specifically since it skips the layout properties and constraints that the Code panel includes alongside the gradient.

The exported CSS includes the full background property, including the shorthand or longhand format Figma uses for the specific gradient type. Pasting directly gives background: linear-gradient(...), which is valid as an inline style or a CSS property value. For use in background-image without the shorthand, remove the background: prefix from the pasted value and keep only the gradient function itself, which is the approach most component-based frameworks expect when you assign gradient values to a dedicated background-image property.

Design panel vs Code panel

The Design panel copy icon gives the background shorthand, which is the most common need. The Code panel in Inspect mode shows the same CSS but also includes layout properties and constraints, which adds noise when you only need the gradient value. Building on this, the Fill row copy is the faster path for gradient values specifically. The Code panel is still useful when you need to inspect other layer properties alongside the gradient, such as opacity, blend mode, or fill rule settings.

Verifying exported stop colors

Consequently, the pasted CSS works immediately in most contexts. Building on this, Figma occasionally outputs intermediate stop colors that represent Figma's own color management conversions rather than the designer's original input colors, which can shift colors slightly for wide-gamut or HSL-specified stops, so a quick visual check in the browser confirms the export matches the design intent.

Always verify the stop colors against the original design values after pasting, especially when the design relies on precise brand color matching or subtle gradient transitions between closely related hues. A practical verification workflow is to paste the exported gradient into a test element, screenshot it, and compare the result side-by-side with the Figma design at the same viewport dimensions to catch any color or positioning discrepancies before committing the value to production stylesheets.

Linear vs Radial vs Angular fill types

Figma's Linear fill corresponds to CSS linear-gradient(). The direction uses the to keyword: to right, to bottom right, or similar. Figma calculates the CSS direction from the gradient handle positions in the design canvas, translating them to a to direction keyword or percentage-based angle. Figma's Radial fill exports as radial-gradient() with an ellipse shape and an at position, while Figma's Angular fill exports as conic-gradient() with a from angle and at position.

Figma's Radial fill exports as radial-gradient() with an ellipse shape and an at position. The position reflects the Figma center handle placement as percentages of the layer's bounding box. Figma's Angular fill exports as conic-gradient() with a from angle and at position, and the from angle reflects the Figma rotation handle position on the canvas.

Yet conic-gradient() CSS support differs slightly from Figma's Angular behavior: Figma's Angular fill can produce effects the browser conic-gradient() replicates only approximately when the Figma center is outside the layer bounds. Building on this, verify Angular gradient exports in the browser's DevTools to confirm they render as expected. When the Figma center handle sits outside the layer bounds, the conic-gradient() at position in CSS places the center at a percentage of the element's bounding box, which may not match Figma's visual result if the design relies on an off-canvas focal point. For designs that depend on precise off-center conic positioning, manually adjusting the at position values in the exported CSS after pasting from Figma gives you pixel-level control that matches the original design intent without needing to rework the Figma source file.

Multi-stop gradients and position accuracy

Figma exports multi-stop gradients with all stop positions as percentages of the gradient line length. Each stop's position in the exported CSS matches its handle placement in Figma at the percentage scale. A stop at 30% in Figma exports as a 30% position in the CSS gradient, so stop positions transfer directly without conversion.

HEX vs OKLCH export limitation

Figma exports all stop colors as HEX regardless of the input format. Colors specified in Figma's OKLCH editor, HSL picker, or oklch() notation all export as HEX approximations. The HEX approximation represents the sRGB equivalent of the input color. For colors within the sRGB gamut, the conversion is lossless in practice. For wide-gamut P3 colors specified in Figma, the HEX export clips to the nearest sRGB value. Consequently, the exported CSS gradient may appear noticeably less saturated than the Figma design when viewed side-by-side on a wide-gamut P3 display. To preserve P3 colors, manually replace the HEX stops with color(display-p3 ...) values in the CSS after copying from Figma.

Notes

Figma panel location: Select a layer with a gradient fill. In the Design panel (right sidebar), locate the Fill section. A gradient swatch appears with the fill type label (Linear, Radial, or Angular). Hover the fill row to reveal a copy icon (two overlapping squares) on the right. Click the copy icon to copy the CSS background property value. For linear gradients, Figma outputs linear-gradient() with to [direction] syntax. For radial, it outputs radial-gradient() with ellipse at [position]. For angular, it outputs conic-gradient() with from [angle] at [position]. All stop colors export as HEX.

Examples

Figma linear gradient export

background: linear-gradient(135deg, #e66465 0%, #9198e5 100%);

Figma exports the background shorthand. For use as background-image, strip the background: prefix. The 135deg angle reflects the Figma gradient handle rotation on the canvas.

Figma radial gradient export

background: radial-gradient(ellipse at 50% 50%, #e66465 0%, #9198e5 100%);

Figma exports radial gradients as ellipse with an at position. The 50% 50% reflects the Figma center handle at the layer midpoint. Moving the Figma center handle changes the at position in the CSS export.

Figma angular (conic) gradient export

background: conic-gradient(from 0deg at 50% 50%, #e66465 0%, #9198e5 100%);

Figma's Angular fill exports as conic-gradient() with a from angle. The stop percentages in Figma Angular fills map to degree percentages of the 360-degree sweep. A stop at 50% in Figma equals 180deg in the conic sweep.

Try in the tool

What this page covers

  • Copy icon location Design panel > Fill row (not the Code panel), visible only on hover
  • Linear / Radial / Angular map to linear-gradient(), radial-gradient(), and conic-gradient() respectively
  • HEX-only export OKLCH, HSL, and P3 colors all flatten to sRGB HEX approximations on export
  • Angular stop-to-degree mapping a 50% stop in Figma's Angular fill equals 180deg in the CSS conic sweep

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Figma, "Use gradients as a fill or stroke," help.figma.com, accessed June 2026. https://help.figma.com/hc/en-us/articles/34208860210199-Use-gradients-as-a-fill-or-stroke

  2. 2.

    Figma, "About color models," help.figma.com, accessed June 2026. https://help.figma.com/hc/en-us/articles/360043042113-About-color-models

  3. 3.

    Mozilla Developer Network, "linear-gradient() CSS function," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient

  4. 4.

    Designilo, "How to Copy a Gradient in Figma," designilo.com, July 2025. https://designilo.com/2025/07/21/how-to-copy-a-gradient-in-figma-without-losing-the-stops-or-angle/

FAQ

CSS Gradients in React with Tailwind CSS v4

Applying Tailwind CSS v4 gradient utilities in React follows the same patterns as plain HTML, but dynamic gradients require a different approach since Tailwind's JIT engine cannot detect class names constructed at runtime.1 The v3 bg-gradient-to-* pattern is replaced by bg-linear-to-r, bg-radial, and bg-conic, with direction and type handled by separate utility segments.2

Color stops attach via from-*, via-*, and to-* modifiers, which accept any color token in your palette. The oklch interpolation mode attaches with the /oklch modifier on the gradient type class, enabling perceptual color space blending without writing any custom CSS. For dynamic gradients where stop colors change based on props or state, inline style with a CSS custom property is more practical than constructing class names dynamically.

Gradient utilities in Tailwind v4

Tailwind v4 introduces a cleaner gradient API. Where v3 required bg-gradient-to-r with color stops on separate classes, v4 treats the gradient type and direction as a single utility: bg-linear-to-r, bg-linear-to-br, bg-radial, bg-conic.2 Color stops attach with from-violet-500 via-purple-400 to-pink-500. The /oklch modifier enables OKLCH interpolation: bg-linear-to-r/oklch from-blue-500 to-orange-400 produces a vivid blend without the grey midpoint that appears in sRGB.

Direction modifiers cover all eight compass directions and form a predictable pattern. A 45-degree diagonal uses -to-br (bottom-right) or -to-tl (top-left). For precise degree values not in the preset list, use an arbitrary value with the color tokens referenced through the var() function and the component's CSS custom properties. Note the underscore escaping: Tailwind converts underscores to spaces inside arbitrary value brackets.3

Arbitrary values for non-standard gradient angles

Tailwind's preset direction modifiers cover the eight compass points, but many designs call for specific angles such as 135 degrees or 220 degrees. The arbitrary value syntax handles these by placing the full gradient function inside the bracket with underscores replacing spaces. The underscore inside square brackets converts to a space in the compiled CSS, so the output reads as a normal CSS gradient function. This pattern also works for conic and radial gradients with custom parameters that Tailwind does not expose as named utilities.

Because arbitrary values compile to plain CSS, the same bracket syntax works for every gradient function Tailwind ships. A conic or radial with a custom center uses the identical pattern as a linear angle; only the inner function name changes. Underscores still stand in for spaces, so copying a value straight from the CapyToolkit gradient builder into the brackets preserves the exact spacing the tool produced. Keeping dynamic angles in arbitrary values rather than bespoke utilities avoids scattering one-off classes across the component tree.

Dynamic gradients with props and state

Tailwind classes are static strings in source files. The JIT engine scans source files for complete class names; splitting a class across a string concatenation prevents detection and strips it from the output bundle.1 Dynamic stop colors require either a full set of pre-declared classes (one per possible color) or an inline style approach.

Using CSS custom properties for dynamic stops

The most practical pattern sets a CSS custom property on the element and references it in a fixed class. Define a base gradient class in CSS using custom properties for the start and end colors, then set those properties via the style prop in JSX. The style object assigns the custom properties the runtime color values passed as props. Consequently, the Tailwind class is static and JIT detects it, while the actual colors change at runtime through the custom properties. Furthermore, this pattern works with the browser color picker output from the CapyToolkit gradient builder: paste the CSS value into the variable, and the gradient updates live.

Gradient variants and responsive modifiers

Tailwind's modifier system works on gradient utilities the same way it works on other utilities.4 The dark: modifier switches gradient colors in dark mode: dark:from-slate-800 dark:to-slate-900. The hover: modifier changes the gradient on hover: hover:from-violet-600 hover:to-indigo-700. Responsive modifiers apply at breakpoints: md:bg-linear-to-r changes the gradient direction at the medium breakpoint.

Layered modifiers stack left to right: dark:hover:from-violet-700 is valid and applies to the dark-mode hover state.4 Building on this, Tailwind's group and peer modifiers work with gradient stops, enabling parent-state-driven gradient changes without any JavaScript. Yet complex dynamic gradients with more than two or three variable stops become unwieldy with class names alone; switch to inline CSS custom properties once the class string grows beyond three dynamic modifiers.

Building a gradient design token system for React component libraries

A gradient token system in a React component library defines gradient values as named constants rather than inline class strings. Exporting a gradients object from a shared tokens file: export const gradients = { brand: 'bg-linear-to-r from-violet-500 to-pink-500', neutral: 'bg-linear-to-r from-slate-400 to-slate-600' } gives every consuming component a single source of truth for gradient class names. Updating a gradient across the library requires changing one entry in the gradients object rather than searching all JSX files for inline class strings.

In Tailwind v4 projects, the token object stores the full class names. In CSS-first projects, the equivalent pattern stores CSS variable assignments. Combining both by defining CSS custom properties in globals.css and pairing them with matching Tailwind arbitrary-value class strings keeps the token system compatible with both Tailwind and plain CSS consumers in the same library.

Storybook integration for gradient visual regression

Adding each gradient token as a Storybook story lets you catch visual regressions across library releases. A story that renders each gradient class on a consistent background makes color and direction changes visible without manual browser testing. Configuring Chromatic or Percy to snapshot gradient stories catches unexpected rendering differences introduced by Tailwind configuration changes or dependency updates before they reach production.

Notes

Tailwind v4 gradient utilities: bg-linear-to-r (left-to-right linear), bg-linear-to-br (diagonal), bg-radial (circular radial from center), bg-conic (conic from 0deg). Direction modifiers: -to-r, -to-l, -to-t, -to-b, -to-tr, -to-tl, -to-br, -to-bl. Stop modifiers: from-{color}, via-{color}, to-{color}. OKLCH interpolation: bg-linear-to-r/oklch. Arbitrary values: bg-[linear-gradient(135deg,_#e66465,_#9198e5)]. The v3 bg-gradient-to-* classes are removed in v4.

Examples

Static hero gradient with OKLCH interpolation

<div className="bg-linear-to-r/oklch from-blue-500 to-orange-400 h-64 rounded-xl" />

Requires Tailwind v4. The /oklch modifier routes the blend through the OKLCH color space, producing a vivid midpoint instead of the grey sRGB default.

Dynamic gradient with CSS custom properties

function GradientCard({ startColor, endColor }) {
  return (
    <div
      className="h-32 rounded-lg gradient-card"
      style={{ '--from': startColor, '--to': endColor }}
    />
  );
}
// In your CSS:
// .gradient-card { background: linear-gradient(to right, var(--from), var(--to)); }

JIT detects the static class "gradient-card" and includes it. CSS custom properties handle runtime color changes without dynamic class construction.

Gradient text heading in React

<h1
  className="text-4xl font-bold bg-linear-to-r from-violet-500 to-pink-500 bg-clip-text text-transparent"
>
  Gradient Heading
</h1>

bg-clip-text is the Tailwind equivalent of background-clip: text. Combine with text-transparent to reveal the gradient through the letterforms.

Try in the tool

What this page covers

  • v4 utility rename bg-gradient-to-* (v3) becomes bg-linear-to-*, bg-radial, and bg-conic (v4)
  • Stop modifiers from-*, via-*, and to-* accept any color token in the palette
  • OKLCH interpolation the /oklch modifier avoids the grey sRGB midpoint between distant hues
  • Dynamic stops JIT cannot detect runtime-constructed class names, so dynamic colors need a CSS custom property instead

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Sepehr, "JIT friendly style variants in Tailwind CSS UI components (Part 1)," protailwind.com, accessed June 2026. https://www.protailwind.com/just-in-time-friendly-style-variants-in-tailwind-css-ui-components-part-1

  2. 2.

    Tzador, "Tailwind CSS v4: What is New," dev.to, accessed June 2026. https://dev.to/tzador/tailwind-css-v4-what-is-new-137k

  3. 3.

    Tailwind CSS, "Adding custom styles," tailwindcss.com, accessed June 2026. https://tailwindcss.com/docs/adding-custom-styles

  4. 4.

    Tailwind CSS, "Hover, focus, and other states," tailwindcss.com, accessed June 2026. https://tailwindcss.com/docs/hover-focus-and-other-states

FAQ

CSS Gradient Backgrounds in Next.js

Three distinct approaches exist for applying CSS gradient backgrounds in Next.js, and choosing among them depends on whether the gradient is shared across routes, scoped to a single component, or computed at runtime. globals.css applies gradients site-wide via class names that any component can reference.1 CSS Modules scope gradients to the component file, preventing style leakage between routes.2 The inline style prop applies gradient values directly in JSX.

Each approach has a distinct trade-off: globals.css is easiest to maintain for shared design tokens; CSS Modules are preferred for component-level encapsulation; inline style is necessary when the gradient value changes based on props or fetched data. The App Router and Pages Router handle all three approaches identically since CSS processing does not differ between them.1

globals.css and Tailwind base layer for site-wide gradient tokens

globals.css is imported once in the root layout or _app.tsx and applies globally. Adding a .hero-gradient class with a gradient background there makes it available to every component without an import. For Tailwind CSS projects, adding CSS custom property definitions to the @layer base rule integrates gradient tokens into the Tailwind token system.

Defining CSS custom properties for gradient stop colors in globals.css allows the stops to adapt to dark mode via prefers-color-scheme or a data-theme attribute. A @layer base block in globals.css defines the start and end color variables in :root, then redefines them inside @media (prefers-color-scheme: dark). Consequently, any component using a linear gradient that references those custom properties automatically adapts to the system color scheme without any JavaScript.3 Building on this, placing gradient token definitions in globals.css keeps them centralized and prevents each CSS Module from duplicating the same stop color values.

Browser support for CSS custom properties in gradient stops

All modern browsers, including the versions of Chrome, Firefox, Safari, and Edge that Next.js projects typically target, support CSS custom properties in gradient functions.3 Internet Explorer 11 does not support custom properties, but Next.js does not support IE11 either, so this is not a concern for any active Next.js project. The var() function works in linear-gradient(), radial-gradient(), and conic-gradient() in every browser that Next.js officially supports. This means any gradient that references a custom property for its stop colors adapts automatically when the custom property value changes, whether through a media query, a class toggle, or a JavaScript assignment.

Sharing gradients with composes

The composes keyword in CSS Modules allows one class to inherit rules from another.2 A shared gradient defined in a design-tokens module: .brandGradient { background: linear-gradient(...) }. A component class that composes it: .hero { composes: brandGradient from './design-tokens.module.css'; min-height: 400px; }. The hero class gains the gradient background without duplicating the declaration, and any additional properties applied to the hero class remain scoped to that component alone rather than leaking across the tree.

composes only works within CSS Modules; it cannot compose from globals.css. Class name collisions between CSS Module files are not possible, making the composes approach safe across the component tree. Because the composed class name resolves to a unique identifier at build time, the gradient declaration stays tightly coupled to the component that uses it while still being easy to update in a single place when the brand palette changes.

CSS Modules with composes for gradient reuse

CSS Modules generate locally scoped class names. A .hero class in Hero.module.css applies only to elements using styles.hero in the Hero component. This prevents the gradient from leaking into other components even if they use the same class name. The composes keyword also supports composing from multiple sources in a single declaration, so a component class can inherit a gradient from one module and a typography pattern from another without duplicating either rule.

The composes keyword in CSS Modules allows one class to inherit rules from another. A shared gradient defined in a design-tokens module: .brandGradient { background: linear-gradient(...) }. A component class that composes it: .hero { composes: brandGradient from './design-tokens.module.css'; min-height: 400px; }. Once the hero class gains the gradient background through composition, any additional CSS properties written directly on the hero class remain scoped to that component alone.

composes only works within CSS Modules; it cannot compose from globals.css. Class name collisions between CSS Module files are not possible, making the composes approach safe across the component tree. Because the composed class name resolves to a unique identifier at build time, the gradient declaration stays tightly coupled to the component that uses it while still being easy to update in a single place when the brand palette changes.

The composes keyword also supports composing from multiple sources in a single declaration, so a component class can inherit a gradient from one module and a typography pattern from another without duplicating either rule. This multi-source composition capability makes it straightforward to build a shared design-token module that defines gradients, spacing, and color tokens independently, giving teams a single source of truth that any component can reference through composition rather than through duplicated declarations.

cn() for conditional gradient switching in App Router components

The cn() function (a thin wrapper around clsx and tailwind-merge) builds conditional class strings in JSX. For Tailwind gradient utilities, cn() selects between gradient classes based on a component variant prop: cn('base-class', variant === 'primary' && 'bg-linear-to-r from-violet-500 to-pink-500', variant === 'secondary' && 'bg-linear-to-r from-slate-400 to-slate-600'). tailwind-merge inside cn() resolves conflicting Tailwind utilities correctly, ensuring that the last matching gradient class in the stylesheet order wins predictably without manual specificity management.

tailwind-merge inside cn() resolves conflicting Tailwind utilities correctly. Without tailwind-merge, two bg-linear-to-* classes from different conditions would both appear in the class string, and the last one in the stylesheet order would win unpredictably. Consequently, cn() makes conditional gradient selection safe in a Tailwind project. The deduplication behavior applies to every Tailwind utility in the class list, not just gradient classes, so cn() also prevents accidental overrides for spacing, typography, and color utilities that appear elsewhere in the same className expression.

Server Components in the App Router support cn() for class building: the class string computation happens on the server and the final string appears in the HTML response with no client JavaScript for class logic. For components that need to switch between more than three or four gradient variants, extracting the variant-to-class mapping into a constant object keeps the cn() call readable.

Gradient CSS extraction and critical CSS in Next.js production builds

Next.js extracts CSS into separate chunk files during production builds using the built-in PostCSS and Lightning CSS pipeline. CSS Modules used for gradient styles compile to scoped class names in separate chunks that Next.js loads only when the component importing that module is rendered on the current route. A gradient defined in Hero.module.css loads only when the Hero component is active, reducing the CSS payload on routes that do not include that component.

Gradient classes in globals.css load on every route because globals.css imports to the root layout. Keeping only truly shared gradient tokens in globals.css and moving component-specific gradients into CSS Modules reduces the global CSS bundle size. For gradients used only on the homepage, placing them in a page-specific CSS Module rather than globals.css removes them from the CSS payload of every other route.

Critical CSS and the above-the-fold gradient

Next.js inlines a small amount of critical CSS in the initial HTML response for App Router routes. Hero section gradients defined with CSS Modules appear in the component's CSS chunk rather than the inlined critical CSS, which can cause a brief flash of unstyled content if the chunk loads after the HTML renders. Preloading the CSS Module chunk using the next/link preload pattern, or defining the hero gradient in globals.css, prevents the flash for above-the-fold gradient sections.

Notes

CSS Modules import: import styles from './Component.module.css'. Class reference: className={styles.hero}. cn() utility for conditional gradients: import { cn } from '@/lib/utils'; className={cn(styles.base, variant === 'primary' && styles.primaryGradient)}. Inline style prop: style={{ background: 'linear-gradient(...)' }}. globals.css: add classes in app/globals.css and import it in app/layout.tsx (App Router) or pages/_app.tsx (Pages Router).

Examples

globals.css gradient class with dark mode custom property swap

/* in app/globals.css */
:root { --grad-from: oklch(0.85 0.12 250); --grad-to: oklch(0.80 0.10 300); }
@media (prefers-color-scheme: dark) {
  :root { --grad-from: oklch(0.22 0.06 250); --grad-to: oklch(0.18 0.05 300); }
}
.hero-gradient { background: linear-gradient(135deg, var(--grad-from), var(--grad-to)); }

Define the class in globals.css. Reference it in any component with className="hero-gradient". The custom property swap in the media query handles dark mode automatically.

CSS Module with scoped gradient

/* Hero.module.css */
.hero {
  background: linear-gradient(to bottom right, oklch(0.75 0.18 250), oklch(0.65 0.15 300));
  min-height: 400px;
}

/* Hero.tsx */
import styles from './Hero.module.css';
export function Hero() {
  return <section className={styles.hero}>{/* content */}</section>;
}

The gradient stays scoped to the Hero component. No other component is affected by the .hero class even if it reuses the same name in a different module.

Inline style prop for a runtime-computed gradient

export function GradientBanner({ startColor, endColor, angle = 135 }) {
  return (
    <div
      style={{
        background: `linear-gradient(${angle}deg, ${startColor}, ${endColor})`,
        height: '200px',
      }}
    >
      {/* content */}
    </div>
  );
}

The inline style prop handles runtime gradient values. startColor and endColor can come from API data, user input, or computed values. No CSS class duplication needed.

Try in the tool

What this page covers

  • globals.css shared, site-wide gradient classes, imported once in the root layout or _app.tsx
  • CSS Modules + composes component-scoped gradients that inherit from a shared design-tokens module without duplication
  • Inline style prop necessary when the gradient value changes based on props or fetched data
  • Dark mode adaptation custom properties redefined inside @media (prefers-color-scheme: dark) update the gradient with no JavaScript

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Next.js, "Getting Started: CSS," nextjs.org, accessed June 2026. https://nextjs.org/docs/app/getting-started/css

  2. 2.

    MDN Web Docs, "Using CSS custom properties," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties

  3. 3.

    CSS Modules, "Composition," github.com, accessed June 2026. https://github.com/css-modules/css-modules/blob/master/docs/composition.md

FAQ

CSS Gradients in Svelte Components

Component-scoped styles are the default in Svelte, which means CSS written inside a component's style block applies only to that component's elements and prevents gradient styles from leaking into other components or pages.1 For gradients that need to cross component boundaries, the :global() selector escapes the scoping.1 Reactive gradient declarations use $: in Svelte 4 or $derived in Svelte 5 to compute gradient strings from reactive state.2

Passing gradient stops as props and applying them via the style attribute handles dynamic runtime gradients without scoping issues. Consequently, Svelte's style system supports both static gradient design tokens and reactive gradient computation with minimal boilerplate. Building on this, the gradient builder on this page outputs CSS values ready to paste into a Svelte style block or a style attribute.

Scoped style blocks and gradient leakage prevention

Svelte compiles style block CSS with a unique class attribute (such as svelte-abc123) added to every element in the component template. This attribute scoping means a .hero rule in one component does not affect .hero elements in another component, even if they share the same class name. Adding a gradient to a component's own elements requires only writing the CSS in the style block with no imports, no module references, and no special syntax needed to keep the styles isolated.

Adding a gradient to a component's own elements requires only writing the CSS in the style block. No imports, no module references, no special syntax: div { background: linear-gradient(to right, oklch(0.75 0.18 250), oklch(0.65 0.15 300)); } applies the gradient to all div elements inside that component and nowhere else. Consequently, gradient styles stay contained to the component that defines them.

How Svelte scoping differs from CSS Modules

CSS Modules achieve scoping by generating unique class names at build time. Svelte takes a different approach: it adds a shared attribute (such as svelte-abc123) to every element in the component template and appends the same attribute to every CSS selector in the style block. The result is the same isolation, but the mechanism is attribute-based rather than class-based.

This means Svelte-scoped styles work even when elements have no class attribute at all, which is useful for styling raw HTML elements like section, header, or div without adding a class. The scoping system prevents one of the most common CSS bugs in multi-component applications: accidental style inheritance across unrelated components that share class names. Because the scoping attribute is added at compile time rather than runtime, there is zero performance cost for style isolation.

Passing gradient stops as props via the style attribute

For dynamic gradient stops that change based on component input, pass the stop colors as props and apply them through the style attribute using CSS custom properties.3 The component declares the props and sets the custom properties on the root element. In the script block, declare the props with export let in Svelte 4 or the $props() rune in Svelte 5, then set custom properties on the element using inline style with the CSS custom property names of your choice.

In the script block, declare the props with export let in Svelte 4 or the $props() rune in Svelte 5, then set custom properties on the element using inline style. In the style block, reference those properties with var() inside the gradient function so the gradient updates whenever the parent passes new values.

Yet the CSS custom property approach requires the parent to pass valid CSS color strings; the component does not validate the format. Building on this, TypeScript type annotations on the props enforce that callers pass string values, though CSS validity checking requires a separate color validation step. The CapyToolkit gradient builder outputs color strings in oklch and HEX format that paste directly into Svelte prop defaults without modification, giving you both runtime flexibility and compile-time safety.

Reactive gradient strings with $: and $derived

Reactive declarations automatically recompute when their dependencies change. In Svelte 4, $: gradient = linear-gradient(to right, ${from}, ${to}) recomputes whenever from or to changes. In Svelte 5, const gradient = $derived(...) replaces the $: syntax with a rune.2 Either approach updates the gradient string reactively from store values, props, or local state. Combining a Svelte writable store for theme colors with a reactive gradient string produces a gradient that responds to global theme changes without any additional boilerplate or manual event wiring.

Comparing reactive gradient approaches

The style attribute approach sets the full gradient string directly on the element, which is the simplest pattern for a gradient that depends on only one or two reactive values. The CSS custom property approach keeps the gradient definition in the style block and only updates the stop color variables, which scales better when multiple properties of the same gradient change independently.

Computed gradients from store values

Combining a Svelte writable store for theme colors with a reactive gradient string produces a gradient that responds to global theme changes. When the theme store updates the from and to values, the reactive declaration recomputes the gradient string, and the template re-renders with the new value. Consequently, a theme toggle that writes new color values to the store propagates automatically to every component with a reactive gradient.

Furthermore, Svelte's fine-grained reactivity means only the elements using the gradient variable re-render, which becomes especially valuable in complex dashboards where multiple gradient-driven visualizations update at different frequencies since a color change in one chart does not trigger unnecessary re-renders in sibling components. This surgical update behavior keeps the component tree stable and avoids layout thrashing even when several gradient values change in rapid succession during user interactions or animated transitions.

Notes

Svelte scoped style: Add CSS inside the <style> block of any .svelte file. Svelte auto-scopes to the component. :global() syntax: :global(.hero) { background: linear-gradient(...); } applies without scoping. Reactive gradient string in Svelte 4: $: gradient = 'linear-gradient(to right, ' + from + ', ' + to + ')'; Svelte 5: const gradient = $derived('linear-gradient(to right, ' + from + ', ' + to + ')'); Apply via style attribute: style="background: {gradient}". CSS custom property prop: define a custom property on the element via the style attribute, then reference it with var() in the style block. When using custom properties for gradient stops, the property names are arbitrary; choose names that describe the purpose, such as panel-start and panel-end for a panel component.

Examples

Static gradient in a Svelte scoped style block

<div class="hero">
  <slot />
</div>

<style>
  .hero {
    background: linear-gradient(135deg, oklch(0.75 0.18 250), oklch(0.65 0.15 300));
    min-height: 400px;
  }
</style>

The gradient stays scoped to this component. No other component's .hero elements are affected. Svelte adds a unique attribute to enforce the scope at compile time.

Reactive gradient from props in Svelte 5

<script>
  let { from = 'oklch(0.75 0.18 250)', to = 'oklch(0.65 0.15 300)' } = $props();
  const gradient = $derived(`linear-gradient(to right, ${from}, ${to})`);
</script>

<div style="background: {gradient}; height: 200px;"></div>

$derived recomputes the gradient string whenever from or to props change. The style attribute applies the computed value directly without a CSS custom property intermediary.

CSS custom property gradient with :global() for theme override

<div class="panel">
  <slot />
</div>

<style>
  .panel {
    background: linear-gradient(to bottom, var(--panel-from), var(--panel-to));
  }
  /* Allow parent to override via :global() */
  :global([data-theme="dark"]) .panel {
    --panel-from: oklch(0.18 0.04 250);
    --panel-to: oklch(0.14 0.02 250);
  }
</style>

:global() escapes Svelte scoping for the selector. The data-theme attribute on a parent element (set outside this component) changes the panel gradient via custom property inheritance.

Try in the tool

What this page covers

  • Scoped style blocks Svelte adds a unique attribute (e.g. svelte-abc123) to elements and selectors at compile time, at zero runtime cost
  • :global() selector escapes component scoping when a gradient must apply outside the component
  • Reactive syntax Svelte 4 uses $: gradient = ...; Svelte 5 replaces it with const gradient = $derived(...)
  • Dynamic stops via props pass colors as props, set them as CSS custom properties in the style attribute, reference with var()

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Svelte, "Scoped styles," svelte.dev, accessed June 2026. https://svelte.dev/docs/svelte/scoped-styles

  2. 2.

    Svelte, "Svelte 5 migration guide," svelte.dev, accessed June 2026. https://svelte.dev/docs/svelte/v5-migration-guide

  3. 3.

    Svelte, "Style properties," github.com, accessed June 2026. https://github.com/sveltejs/rfcs/blob/master/text/0006-style-properties.md

FAQ

CSS Gradients in Vue.js 3 Components

Vue.js 3 gives developers three distinct tools for managing CSS gradients in Single File Components, and each one addresses a different aspect of the styling workflow. Scoped styles with style scoped restrict gradient CSS to the component, preventing leakage. The v-bind() function in CSS (Vue 3.2+) binds a reactive JavaScript value directly into a CSS property value.1 The Composition API's computed() function assembles complex gradient strings from multiple reactive refs.

Together these features give Vue developers complete control over both static and dynamic gradients without global class name management. Consequently, gradient colors defined in JavaScript can drive CSS directly without template string interpolation in the style attribute. Building on this, the gradient builder on this page outputs CSS gradient values that paste directly into Vue style blocks or computed() string templates.

Static gradient in a scoped style block

Style scoped restricts CSS to the current component by adding a unique data attribute to both the HTML elements and the CSS selectors at compile time. A gradient in a scoped style block: .hero { background: linear-gradient(to right, oklch(0.75 0.18 250), oklch(0.65 0.15 300)); } applies only to .hero elements inside this component's template.2 Scoped styles prevent gradient declarations from affecting sibling or parent components, which is especially useful when multiple components define a .hero or .card class with different gradient values.

Scoped styles prevent gradient declarations from affecting sibling or parent components. This is especially useful when multiple components define a .hero or .card class with different gradient values: each component's gradient stays independent. Consequently, scoped styles are the recommended approach for component-level gradient design in Vue. Building on this, the gradient builder outputs a CSS value that pastes directly into a scoped style rule without any Vue-specific modification, so the gradient syntax is identical to plain CSS and works across frameworks.

Scoped style limitations and deep selectors

The scoped attribute adds a data attribute to every element in the component template. Child components rendered inside the parent do not receive the parent's scoped attribute on their root element, which means parent scoped styles cannot target child component roots directly. The deep selector (:deep() or the older ::v-deep) overrides this limitation: :deep(.child-class) { background: linear-gradient(...) } applies the gradient to elements inside child components.2 Use deep selectors sparingly since they partially break the encapsulation that scoped styles provide.

Building on this, the gradient builder outputs a CSS value that pastes directly into a scoped style rule without any Vue-specific modification; the gradient syntax is identical to plain CSS and works across frameworks so the same gradient value can be shared across a multi-framework design system without any framework-specific adaptation or string manipulation.

v-bind() in CSS for reactive stop colors

Vue 3.2 introduced v-bind() inside <style> blocks, allowing JavaScript reactive values to appear directly in CSS property values. Declare a reactive ref for each dynamic stop: const fromColor = ref('oklch(0.75 0.18 250)'). In the style block: .hero { background: linear-gradient(to right, v-bind(fromColor), oklch(0.65 0.15 300)); }. When fromColor updates in the script, Vue injects the new value into the CSS custom property that v-bind() creates internally, and the gradient updates reactively without any template-level style binding.1

When fromColor updates in the script, Vue injects the new value into the CSS custom property that v-bind() creates internally. The gradient updates reactively without any template-level style binding. Vue compiles v-bind() into a hashed CSS custom property applied to the component's root element via inline styles, and the value updates whenever the bound ref changes. This per-instance scoping means that two instances of the same component can display different gradient colors simultaneously without interfering with each other, which is particularly useful for reusable card or banner components that accept different color props in different contexts on the same page.

computed() gradient string for multi-stop gradients

The computed() function in the Composition API builds a derived value that updates when any of its reactive dependencies change. For a complex multi-stop gradient, assembling the string in computed() keeps the template clean and makes the gradient logic testable in isolation.3 Wrapping the computed gradient in a composable (useGradient) makes the pattern reusable across multiple components with different initial stop values, and the composable can return both the computed gradient string and setter functions for updating colors programmatically.

Computed gradient with multiple refs

const from = ref('oklch(0.75 0.18 250)'); const via = ref('oklch(0.70 0.16 275)'); const to = ref('oklch(0.65 0.15 300)'); const gradient = computed(() => 'linear-gradient(to right, ' + from.value + ', ' + via.value + ', ' + to.value + ')'). Apply it in the template: :style="{ background: gradient.value }". Consequently, any update to from, via, or to causes gradient to recompute and the element style to update. Building on this, wrapping the computed gradient in a composable (useGradient) makes the pattern reusable across multiple components with different initial stop values. The composable can accept default stop colors as parameters and return both the computed gradient string and setter functions, giving consuming components a clean API for updating gradient colors programmatically while keeping the string assembly logic centralized and testable.

Animating between gradient states with watch() and CSS transitions

Vue's Transition component handles CSS class-based animations when elements enter or leave the DOM, but it does not directly animate changes to background gradient values on persistent elements. For a gradient that changes state on user interaction, the watch() function in the Composition API detects when a reactive ref changes and can trigger a class swap or custom property update that CSS transitions act on.

Setting transition: background 0.4s ease on the element in the scoped style block enables CSS transitions when the background property changes. However, the CSS background-image property has a discrete animation type, so browsers cannot interpolate between two gradient values without @property.4 Consequently, transitioning between two gradient states without @property produces an abrupt switch at the animation midpoint. Assigning the gradient stop colors to typed @property registered properties and transitioning those values instead produces smooth gradient state transitions.5

Using class switching for discrete gradient states

For a small set of gradient states such as two or three variants, switching CSS classes is a simpler pattern than animating individual stop values. Define .gradient-default and .gradient-active in the scoped style block with different stop colors. Use Vue's :class binding to toggle between them and add CSS transition: background 0.3s ease on the element. The transition fires on class change, but without @property it snaps rather than blends between the gradient values.

Notes

Scoped style: <style scoped>. v-bind() in CSS: const color = ref('oklch(0.7 0.2 250)'); then in the style block: background: linear-gradient(to right, v-bind(color), blue). computed() gradient: const gradient = computed(() => 'linear-gradient(to right, ' + from.value + ', ' + to.value + ')') applied via :style="{ background: gradient }". Vue 3.2+ required for CSS v-bind(). All Vue 3 versions support computed() and :style.

Examples

Static gradient in a scoped Vue SFC style block

<template>
  <div class="hero"></div>
</template>

<style scoped>
.hero {
  background: linear-gradient(135deg, oklch(0.75 0.18 250), oklch(0.65 0.15 300));
  min-height: 400px;
}
</style>

The scoped attribute restricts the gradient to this component's .hero elements. No other component is affected even if it uses the .hero class name.

Reactive gradient stop with v-bind() in CSS

<script setup>
import { ref } from 'vue';
const fromColor = ref('oklch(0.75 0.18 250)');
const toColor = ref('oklch(0.65 0.15 300)');
</script>

<template>
  <div class="banner"></div>
</template>

<style scoped>
.banner {
  background: linear-gradient(to right, v-bind(fromColor), v-bind(toColor));
  height: 200px;
}
</style>

v-bind() in the style block links the reactive refs directly to CSS. Updating fromColor.value in the script causes the gradient to rerender without any template-level style binding.

computed() gradient string applied via :style

<script setup>
import { ref, computed } from 'vue';
const from = ref('oklch(0.75 0.18 250)');
const to = ref('oklch(0.55 0.18 300)');
const angle = ref(135);
const gradient = computed(
  () => `linear-gradient(${angle.value}deg, ${from.value}, ${to.value})`
);
</script>

<template>
  <div :style="{ background: gradient }"></div>
</template>

computed() assembles the full gradient string from multiple reactive sources. :style applies it to the element. Any ref update triggers gradient recomputation and DOM update.

Try in the tool

What this page covers

  • <style scoped> adds a unique data attribute to both elements and selectors at compile time, isolating the gradient to the component
  • :deep() selector needed to target a child component's root element, since scoped attributes don't propagate to children
  • v-bind() in CSS Vue 3.2+ only; injects a reactive ref directly into a CSS property via a hashed custom property
  • computed() gradient strings assembles a multi-stop gradient from several refs, ideal for a reusable useGradient composable

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Evan You, "Vue 3.2 Released!," blog.vuejs.org, August 2021. https://blog.vuejs.org/posts/vue-3-2

  2. 2.

    Vue.js, "SFC CSS Features," vuejs.org, accessed June 2026. https://vuejs.org/api/sfc-css-features

  3. 3.

    Vue.js, "packages/reactivity/src/computed.ts," github.com, accessed June 2026. https://github.com/vuejs/core/blob/main/packages/reactivity/src/computed.ts

  4. 4.

    MDN Web Docs, "background-image," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/background-image

  5. 5.

    MDN Web Docs, "Using the CSS properties and values API," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/API/CSS_Properties_and_Values_API/guide

FAQ

CSS Gradients in styled-components

Styled-components uses tagged template literals to write CSS directly in JavaScript. A gradient background applies the same way as in a plain CSS file, inside the backtick string of a styled component.1 The css helper extracts reusable gradient fragments to a shared file, making the same gradient importable across multiple components without duplication.2 ThemeProvider integration passes theme color tokens as stop values, enabling gradients that adapt to the active theme without prop drilling.3 Props-based gradient switching conditionally changes the gradient based on the component's runtime props.1 Consequently, styled-components supports every CSS gradient use case while adding JavaScript-level composition and type safety when combined with TypeScript. Building on this, the gradient builder on this page outputs CSS gradient values that paste directly into the backtick string of any styled component without modification.

Inline gradient in a styled component

A gradient background in a styled component uses the same CSS syntax as a plain stylesheet, inside the template literal string. The template literal interpolation ${ } embeds JavaScript expressions, which enables theme token references and prop-based values inline with the CSS. A static gradient definition compiles to a class at runtime, and the gradient can be inspected in the browser DevTools under a generated class name that is a deterministic hash of the template literal content.4

A static gradient: const Hero = styled.div'background: linear-gradient(135deg, oklch(0.75 0.18 250), oklch(0.65 0.15 300)); min-height: 400px;'. The backtick string is valid CSS and styled-components compiles it to a class at runtime. Consequently, the gradient can be inspected in the browser DevTools under a generated class name. Building on this, styled-components generates unique class names per component definition, so two Hero components with different gradients in their template literals produce two distinct class names without conflict. The class names are deterministic hashes of the template literal content, meaning identical gradient definitions across different component files share the same generated class, which deduplicates the CSS output in the final bundle.

The css helper for reusable gradient fragments

The css helper from styled-components creates a fragment of CSS that can be imported and composed into multiple styled components. A gradient fragment file defines the gradient once and exports it: const brandGradient = css'background: linear-gradient(to right, oklch(0.75 0.18 250), oklch(0.65 0.15 300));'. Components import it and spread it into their template literal. The css helper enables nested interpolations with theme token references and conditional expressions inside the fragment, which still resolve correctly when the fragment is composed into a parent template literal at build time.2

Why not a plain string

Plain template literal strings work for simple gradient values. Yet the css helper enables nested interpolations: theme token references and conditional expressions inside the fragment still resolve correctly when the fragment is composed into a parent template literal. Without the css helper, nested interpolations in a plain string fail to process. Furthermore, the css helper provides TypeScript inference for styled-components' theme type, so theme property access inside the fragment benefits from type checking. Consequently, prefer the css helper whenever the gradient fragment contains any interpolated JavaScript expression. For gradient fragments that are entirely static with no theme or prop references, a plain const string works just as well and avoids the minimal overhead of the css helper wrapper, keeping the code as simple as possible for the straightforward cases.

Because the css helper is required as soon as a fragment contains any interpolation, the simplest mental model is to default to css for every shared fragment and reserve a plain const only for throwaway inline values. That keeps one import pattern across the reusable gradients instead of mixing two styles. CapyToolkit's gradient builder outputs a plain CSS value, so pasting it into either a css fragment or a plain const string works without adjustment.

ThemeProvider and props-based gradient switching

ThemeProvider wraps the component tree and passes a theme object through React context. Styled components access the theme via the ({ theme }) => theme.property interpolation pattern inside the template literal. Gradient stop colors from the theme: const Hero = styled.div'background: linear-gradient(to right, ${ ({ theme }) => theme.colors.gradFrom }, ${ ({ theme }) => theme.colors.gradTo });'. A component variant prop changes the gradient based on the prop value, and styled-components regenerates the class on each distinct prop combination and caches the result for subsequent renders.3

Props-based conditional gradient

A component variant prop changes the gradient based on the prop value: const Button = styled.button'background: ${ ({ variant }) => variant === "primary" ? "linear-gradient(to right, oklch(0.75 0.18 250), oklch(0.65 0.15 300))" : "linear-gradient(to right, oklch(0.5 0.05 0), oklch(0.45 0.04 0))" };'. Styled-components regenerates the class on each distinct prop combination and caches the result. Yet generating many distinct gradient variations through props creates many class names; for a large number of gradient options, CSS custom properties with ThemeProvider tokens scale better than individual class generation. A hybrid approach works well in practice: use props-based switching for a small set of named variants like primary and secondary, then fall back to CSS custom property overrides for fully dynamic colors that come from user input or API data at runtime.

Server-side rendering of gradient styles and class hydration

Server-side rendering with styled-components requires the ServerStyleSheet API to collect styles from the component tree into the initial HTML response. Without this step, the server renders HTML with component class names but no corresponding CSS, causing a flash of unstyled gradient content until the client bundle loads and generates the classes. Wrapping the SSR render in ServerStyleSheet.collectStyles() and injecting the result with StyleSheetManager ensures gradient CSS is present in the HTML from the first byte.5

Next.js users integrating styled-components with the App Router add the styled-components registry pattern from the Next.js documentation. This pattern wraps the root layout in a client component that collects server-rendered styles and injects them before the server HTML flushes. Gradient classes defined in styled components then appear in the server HTML without a separate CSS file or flash of unstyled content.

Class deduplication across multiple renders

styled-components deduplicates class names across the component tree during both client and server rendering. Two Hero components with the same gradient template literal generate the same hash and share one class name in the compiled stylesheet.4 The deduplication applies to both static gradients and ThemeProvider-resolved gradients, as long as the resolved CSS string is identical. Prop-based gradients that produce different resolved strings generate separate classes, each cached independently for subsequent renders.

Notes

Template literal syntax: const Hero = styled.divbackground: linear-gradient(to right, ${theme.colors.from}, ${theme.colors.to});. The css helper: import { css } from 'styled-components'; const gradientFragment = cssbackground: linear-gradient(...);. ThemeProvider token access: ${({ theme }) => theme.colors.primary}. Props-based switching: ${({ variant }) => variant === 'primary' ? 'linear-gradient(...)' : 'linear-gradient(...)'}. TypeScript theme type: declare module 'styled-components' { export interface DefaultTheme { colors: { from: string; to: string; } } }.

Examples

Styled component with inline gradient

import styled from 'styled-components';

const Hero = styled.div`
  background: linear-gradient(
    135deg,
    oklch(0.75 0.18 250),
    oklch(0.65 0.15 300)
  );
  min-height: 400px;
`;

The gradient CSS is identical to plain CSS. Paste the gradient builder output directly into the backtick string. Styled-components compiles it to a unique class at runtime.

Reusable gradient with the css helper

import { css } from 'styled-components';

export const brandGradient = css`
  background: linear-gradient(to right, ${({ theme }) => theme.colors.from}, ${({ theme }) => theme.colors.to});
`;

// In another file:
import styled from 'styled-components';
import { brandGradient } from './gradients';

const Card = styled.div`
  ${brandGradient}
  border-radius: 12px;
  padding: 24px;
`;

The css helper preserves nested interpolations. Theme token references inside brandGradient resolve correctly when spread into the Card template literal.

Props-based gradient switching with TypeScript

import styled from 'styled-components';

interface ButtonProps { variant: 'primary' | 'secondary'; }

const Button = styled.button<ButtonProps>`
  background: ${({ variant }) =>
    variant === 'primary'
      ? 'linear-gradient(to right, oklch(0.75 0.18 250), oklch(0.65 0.15 300))'
      : 'linear-gradient(to right, oklch(0.5 0.05 0), oklch(0.45 0.04 0))'
  };
  padding: 12px 24px;
  border: none;
  cursor: pointer;
`;

TypeScript generics on the styled component enforce valid variant values. Styled-components creates separate class names for each variant and caches them for subsequent renders.

Try in the tool

What this page covers

  • Template literal syntax gradient CSS written directly inside a styled.div backtick string, compiled to a hashed class at runtime
  • css helper required as soon as a gradient fragment contains theme or prop interpolation; a plain string suffices for fully static values
  • ThemeProvider tokens \${({ theme }) => theme.colors.x} pulls gradient stops from the active theme via React context
  • Props-based switching styled-components regenerates and caches a class per distinct prop combination

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    styled-components, "The Basics," styled-components.com, accessed June 2026. https://styled-components.com/docs/basics

  2. 2.

    styled-components, "css," github.com, accessed June 2026. https://github.com/styled-components/styled-components-website/blob/main/sections/api/helpers/css.mdx

  3. 3.

    styled-components, "Advanced Usage," styled-components.com, accessed June 2026. https://styled-components.com/docs/advanced

  4. 4.

    Eugene Gluhotorenko, "How styled-components works: A deep dive under the hood," joshwcomeau.com, August 2018. https://www.joshwcomeau.com/react/demystifying-styled-components/

  5. 5.

    styled-components, "ServerStyleSheet.tsx," github.com, accessed June 2026. https://github.com/styled-components/styled-components/blob/main/packages/styled-components/src/models/ServerStyleSheet.tsx

FAQ

CSS Gradient Mixins and Functions in SCSS

SCSS mixins and functions make CSS gradients reusable and configurable. A @mixin encapsulates the full gradient declaration with configurable parameters for stop colors and direction.1 A @function computes stop positions mathematically, enabling programmatic even distribution of N stops across the gradient axis.2 An @each loop generates a set of gradient classes from a color map, producing an entire palette of gradient utilities from a single declaration block.3 Output uses either direct background-image declarations or CSS custom property assignments, depending on whether the gradient needs JavaScript-level interactivity at runtime.4 Consequently, SCSS-based gradient systems scale from a single reusable mixin to a full design-system gradient API. Building on this, compiling SCSS to CSS with custom property output gives the benefits of SCSS authoring while keeping the runtime flexibility of CSS variables for theme switching.

Basic @mixin and @include usage

A gradient mixin with default direction parameter reduces gradient declarations to a single @include line, which simplifies stylesheet maintenance across a large codebase. Declare the mixin with parameters for the stop colors and an optional direction: @mixin gradient($from, $to, $direction: to right). Inside the mixin body: background-image: linear-gradient($direction, $from, $to). Call it with @include gradient(oklch(0.75 0.18 250), oklch(0.65 0.15 300)) for the default direction, or @include gradient(oklch(0.75 0.18 250), oklch(0.65 0.15 300), 135deg) for a custom angle. Adding additional parameters extends the mixin without breaking existing call sites, so a $via parameter for a midpoint stop defaults to null and the mixin conditionally includes it only when provided. This default-null pattern keeps the public mixin signature stable even as the internal feature set grows.

Adding additional parameters extends the mixin without breaking existing call sites. A $via parameter for a midpoint stop defaults to null; the mixin conditionally includes it only when provided.1 Consequently, the mixin interface stays clean for simple two-stop calls while supporting three-stop gradients when needed. Each optional parameter has a sensible default so that the simplest @include call works without any configuration, while advanced use cases can opt in to additional behavior without breaking existing call sites across the codebase.

This extensibility pattern is what makes SCSS mixins a practical alternative to utility class systems for gradients, because a single mixin call can express an arbitrarily complex gradient without the stylesheet author needing to remember class name conventions for every combination of direction and stop count, and modifying the mixin definition automatically propagates the change to every call site.

Default parameters and mixin extensibility

SCSS default parameter values mean any call site that omits a parameter automatically gets the intended default behavior, reducing the chance of inconsistency across the codebase. A well-designed gradient mixin starts with the two required color parameters and adds optional parameters for direction, via stop, and custom property emission. Each new optional parameter added after the initial release preserves backward compatibility, because existing call sites that omit the new parameter fall back to the default without any change to their source code.

Each optional parameter has a sensible default so that the simplest @include call works without any configuration, while advanced use cases can opt in to additional behavior. This pattern keeps the mixin backward-compatible as the design system grows. When a new gradient requirement emerges, extending the mixin with additional optional parameters means existing @include calls continue to work without modification while new call sites can opt into the expanded feature set.

@function for calculated stop positions

SCSS @function computes values that SCSS expressions cannot express directly. For a multi-stop gradient where N colors must distribute evenly, a function calculates each stop's percentage position from its index and the total count. Using sass:math for division, the function returns a calculated percentage for any stop index, enabling programmatic even distribution of N stops across the gradient axis without manual position calculations.

Using sass:math for division: @function stop-position($index, $total) { @return math.div($index - 1, $total - 1) * 100%; }.5 Calling it for a five-stop gradient: stop-position(1, 5) returns 0%, stop-position(3, 5) returns 50%, stop-position(5, 5) returns 100%. These computed percentages replace the manual position values that would otherwise need to be calculated and hardcoded for each stop in the gradient declaration, which becomes impractical for palettes with more than three or four colors.

Yet SCSS @function cannot accept a variable-length argument list for the color values and loop through them in a single expression; the gradient construction still requires a list variable.2 This means the function handles the math while a separate @each loop assembles the final gradient declaration from the computed positions. Building on this, combining @function position calculation with a @each loop that builds the stop list enables a fully programmatic multi-stop gradient assembly from a SCSS color list. You can take this further by defining a master color palette map and a @mixin that accepts a palette name, looks up the colors automatically, and generates the complete gradient with evenly distributed stops, eliminating the need to manually specify stop positions for each new gradient variant.

@each loop for palette gradient variant generation

A SCSS map pairs gradient names with stop color lists. An @each loop iterates the map and outputs one gradient class per entry. This generates an entire set of gradient utilities from a single loop declaration. Adding a new gradient to the map generates a new class automatically on the next SCSS compile without modifying the loop, and extending the map with a third stop value requires only updating the stop list.

Map and loop pattern

$gradients: (primary: (oklch(0.75 0.18 250), oklch(0.65 0.15 300)), secondary: (oklch(0.6 0.1 100), oklch(0.5 0.08 80))); @each $name, $stops in $gradients { .gradient-#{ $name } { background-image: linear-gradient(to right, $stops...); } }. The ... spread operator passes the list items as positional arguments to linear-gradient(). Consequently, adding a new gradient to the map generates a new class automatically on the next SCSS compile without modifying the loop. Furthermore, extending the map with a third stop value requires only updating the stop list; the loop and the gradient function handle the additional stop without any change to the loop body.

This pattern scales to an arbitrary number of gradient variants: adding a new entry to the map with a new name and color list produces a new utility class on the next SCSS compile, making it trivial for the design team to expand the gradient system without touching the loop logic or understanding the underlying SCSS iteration mechanics.

Generating CSS custom property output from SCSS gradient mixins

SCSS mixins can output CSS custom property declarations alongside direct background-image rules, giving consuming components a choice between applying the gradient directly or receiving the stop colors as runtime-accessible variables. A mixin that outputs both assigns the SCSS variables to named custom properties using interpolation, then references those properties in the gradient background-image declaration.

The #{} interpolation is required when assigning a SCSS variable to a CSS custom property. Without interpolation, SCSS parses the assignment as a SCSS variable declaration rather than a CSS property and does not emit it to the compiled output.4 After compilation, the result contains both the custom property declarations and the gradient background-image declaration using those properties. JavaScript can then read those custom properties with getComputedStyle() or override them dynamically on the element.

Conditional mixin output for design token systems

Adding a $emit-vars parameter to the mixin lets call sites opt in to the custom property output: the mixin conditionally emits the custom properties only when the parameter is true. Components that need JavaScript interactivity pass true as the fourth argument; components that do not need runtime override call the mixin without the parameter, keeping the compiled CSS minimal across the codebase.

Notes

Basic mixin: @mixin gradient($from, $to, $direction: to right) { background-image: linear-gradient($direction, $from, $to); }. Usage: @include gradient(oklch(0.75 0.18 250), oklch(0.65 0.15 300), 135deg). @function for stop positions: @function stop-position($index, $total) { @return math.div($index - 1, $total - 1) * 100%; }. @each loop: @each $name, $stops in $gradient-map { .gradient-#{$name} { background-image: linear-gradient(to right, $stops...); } }. CSS custom property output: a mixin can emit named custom properties for the start and stop colors using #{} interpolation to assign SCSS variables to CSS variable names.

Examples

Basic gradient mixin definition and usage

@use 'sass:math';

@mixin gradient($from, $to, $direction: to right) {
  background-image: linear-gradient($direction, $from, $to);
}

.hero {
  @include gradient(oklch(0.75 0.18 250), oklch(0.65 0.15 300), 135deg);
  min-height: 400px;
}

.button-primary {
  @include gradient(oklch(0.7 0.22 250), oklch(0.6 0.2 280));
  padding: 12px 24px;
}

The mixin encapsulates the gradient declaration. @include calls produce the background-image property in the compiled CSS. The $direction parameter defaults to to right, requiring only the stop colors for horizontal gradients.

@function for even stop position calculation

@use 'sass:math';

@function stop-pos($index, $total) {
  @return math.div($index - 1, $total - 1) * 100%;
}

$palette: oklch(0.8 0.15 250), oklch(0.7 0.18 270), oklch(0.6 0.2 290), oklch(0.5 0.18 310);

.spectrum {
  background-image: linear-gradient(
    to right,
    nth($palette, 1) #{stop-pos(1, 4)},
    nth($palette, 2) #{stop-pos(2, 4)},
    nth($palette, 3) #{stop-pos(3, 4)},
    nth($palette, 4) #{stop-pos(4, 4)}
  );
}

stop-pos() computes even percentage positions for any number of stops. Changing the palette list length requires only updating the $total argument in each nth() call.

@each loop for a palette of gradient utility classes

$gradients: (
  "primary":   (oklch(0.75 0.18 250), oklch(0.65 0.15 300)),
  "secondary": (oklch(0.6 0.1 100),   oklch(0.5 0.08 80)),
  "warm":      (oklch(0.8 0.18 50),   oklch(0.7 0.2 20)),
);

@each $name, $stops in $gradients {
  .gradient-#{$name} {
    background-image: linear-gradient(to right, $stops...);
  }
}

The @each loop generates .gradient-primary, .gradient-secondary, and .gradient-warm from the map. Adding a new entry to $gradients produces a new class automatically on the next compile.

Try in the tool

What to look for

  • math.div($index - 1, $total - 1) * 100%
  • 0%
  • 50%
  • 100%

Optional mixin parameters (like a $via midpoint stop) should default to null so existing @include call sites keep working unchanged as the mixin grows.

Verify with the CSS Gradient Builder tool.

Try it in the tool ↑
Sources
  1. 1.

    Sass, "@mixin and @include," sass-lang.com, accessed June 2026. https://sass-lang.com/documentation/at-rules/mixin/

  2. 2.

    Sass, "@function," sass-lang.com, accessed June 2026. https://sass-lang.com/documentation/at-rules/function/

  3. 3.

    Mike Street, "Sass @each Loops," digitalocean.com, May 2016. https://www.digitalocean.com/community/tutorials/sass-each-loops

  4. 4.

    Rembert Designs, "Understanding Interpolation in SASS," dev.to, accessed June 2026. https://dev.to/rembertdesigns/understanding-interpolation-in-sass-ebo

  5. 5.

    Lisanne van Vliet, "The Power of Sass," medium.com, accessed June 2026. https://medium.com/@lisannevanvliet/the-power-of-sass-fc735f97a808

FAQ