Gradient generator

Linear, radial, conic and mesh in OKLCH, with text contrast measured at the worst pixel.

Press space for a new gradient · lock a stop to keep it

Ship the version people can read

Every pixel under this paragraph was measured, not guessed. Move the stops and watch the figure change.

In short

Press space for a new gradient, lock the stops you like and drag the handles to move them; kind, angle, blend and grain are in Settings. The colours blend in OKLCH, so two saturated colours do not pass through grey on the way, and older browsers get a fallback that follows the same path. The chips under the gradient give the text on it at its worst pixel, not the average, and the banding counted in 8-bit steps; a gradient Space makes passes both, grain added where it would band, unless a locked colour rules that out. Copy CSS from the bar, or export custom properties, Tailwind v4 or, for a mesh, SVG.

What CSS gradient generator is

A tool that writes a CSS gradient — a smooth blend between colour stops along a line, around a centre or across several points — and hands you the code. This one blends in OKLCH, measures the text you will set on the gradient at its least legible pixel, and counts the 8-bit bands it will paint before you copy anything.

What this tool does that others do not

  • OKLCH blending, with a fallback that follows the same path

    The CSS colour module, level 4, keeps gradients written in hex in sRGB for compatibility, so two colours from opposite sides of the hue circle meet in grey. The tool writes in oklch, and for Chrome before 111, Safari before 16.2 and Firefox before 127 it bakes the same path into extra hex stops behind an @supports test.

    #2563eb to #f97316 keeps 29% of its chroma at the midpoint in sRGB and all of it in OKLCH. The fallback for that pair takes 12 stops and stays within ΔE2000 0.89 of the real path.

  • Text contrast at the worst pixel, not the average

    Each piece of text on the preview is measured against every pixel behind it. The lowest WCAG 2 ratio and the lowest APCA Lc are reported with where they occur, the average is shown only to say how far it misleads, and a failing line comes with the lightest scrim that would fix it.

    White text over #1e3a8a to #fde68a averages 3.82:1 and ranges from 10.36:1 down to 1.25:1 at the yellow end.

  • Banding counted before it ships

    The tool counts how many times the 8-bit colour changes along the gradient’s real path and how wide the widest band is, at the preview size and across a 1440-pixel hero. Grain at a few per cent dithers the bands in every renderer, including the ones that do not dither gradients themselves.

    #0f172a to #1e293b across 1440px changes colour 50 times, leaving bands up to 72px wide. Grain at 3% adds 366 bytes of CSS.

  • Mesh gradients as layered CSS or SVG

    Each mesh point is one elliptical radial layer over a base colour, so a mesh is plain CSS any browser paints, and the same layers export as an SVG for design tools. The byte count is printed under every export tab.

    The three-point Aurora preset is 170 bytes of gradient, 234 bytes as a complete CSS rule.

How to use it

  1. Press space, or start from a preset

    Space makes a new gradient around any stops you have locked, and each one carries its text colour at 4.5:1 over every pixel unless a locked colour makes that impossible, which the Text chip then reports. Settings holds the kind (linear, radial, conic or mesh) and ten measured presets.

  2. Set the stops, then the blend

    Pick each colour on the gradient and drag its handle; lock a stop to keep it through the next Space. In Settings, leave the blend on OKLCH unless you want the old sRGB look, and switch the hue path to Longer to travel the long way round the colour wheel.

  3. Read the worst pixel

    Open the Text chip under the gradient: each text box lists its worst ratio and where it is. Mark it on the preview, then fix it with the suggested text colour, a scrim, or a lighter or darker stop near that point.

  4. Dither, then export

    If the Bands chip says stripes, add 3% grain. Copy the CSS from the bar, or use Export for custom properties, a Tailwind v4 utility or an inline style; a mesh can also be saved as SVG.

Worked examples

Real input, real figures.

A blue-to-orange hero that turned muddy

You give it

linear-gradient(90deg, #2563eb, #f97316), written in hex as most gradients are.

You get

In sRGB the midpoint is #8f6b81, a greyed mauve at OKLCH chroma 0.055 against 0.215 and 0.187 at the ends. With in oklch the middle keeps its chroma, and the export adds a 12-stop fallback for older browsers.

A headline that passes on average

You give it

White 44px headline across #1e3a8a to #fde68a.

You get

The average ratio is 3.82:1, which a checker sampling one colour might call large-text safe. The worst pixel is 1.25:1 over the yellow end, so part of the headline is unreadable; the readout names it and gives the scrim that lifts it to 3:1.

Against a typical gradient generator

AspectdsynA typical CSS gradient generator
Blending spaceOKLCH or OKLab, with an sRGB fallback for older browserssRGB, so distant hues meet in grey
Text on the gradientWorst-pixel WCAG 2 ratio and APCA Lc for each text boxNot measured
BandingSteps and widest band counted, with grain to ditherNot mentioned
MeshLayered CSS or SVG, bytes shownA separate tool, often image export only
ExportCSS, custom properties, Tailwind v4, inline style, SVGCSS, sometimes with a solid fallback colour

Guide

What it will not do

  • No shader meshes. A mesh here is stacked radial gradients, which is what CSS can paint; warped WebGL meshes need an image export instead.
  • Contrast is measured over each text box rather than the letter shapes, so the figure is conservative: the worst pixel may sit between words.
  • Colours are measured as an sRGB screen shows them. On a wide-gamut display an OKLCH blend can look more vivid than the figures describe.
  • No animated gradients: a moving background changes the contrast under the text every frame, which a single figure cannot describe.

Why hex gradients go grey in the middle

CSS Color 4 makes Oklab the default space for blending colours, with one exception kept for old pages: when every stop is a legacy sRGB colour — a hex code, a name, rgb() or hsl() — the browser must blend in gamma-encoded sRGB. Nearly every gradient on the web is written in hex, so nearly every gradient takes the straight line through the RGB cube, and between colours on opposite sides of the hue circle that line passes near grey. Blue #2563eb to orange #f97316 keeps 29% of its chroma at the midpoint in sRGB and all of it in OKLCH.

Writing in oklch after the direction opts out of the exception. Chrome and Edge have supported it since version 111, Safari since 16.2 and Firefox since 127 (June 2024). For anything older, the export carries the same path baked into extra hex stops, behind an @supports test rather than a second declaration — a custom property cannot fall back by cascade, because an unsupported value in one is accepted when declared and only fails where it is used.

Banding, and which browsers hide it

A gradient that changes slowly has fewer 8-bit colours than it has pixels, so each colour repeats across a band. The slate hero #0f172a to #1e293b changes colour 50 times across 1440 pixels, leaving bands up to 72 pixels wide. Whether you see them depends on the renderer. Each row below was measured on 8 October 2026 by screenshotting that gradient at 1440 × 40 and counting identical pixels along three rows.

RendererDithering, measuredin oklch from
Chromium (Chrome, Edge)Dithers: rows differ, widest run 3–48px depending on the row111
WebKit (Safari)Dithers: rows differ, widest run 8–20px16.2
FirefoxNot measured here; a bug to dither has been open for years127
Canvas, image exports, videoWhatever the renderer does; assume no dither—

Grain at 2–4% does the dithering itself, in every renderer, for about 360 bytes of CSS. Above about 8% it stops being a dither and becomes a visible texture.

What a popular preset list measures

To see how often the problems above occur in practice, the tool's engine was run over all 382 presets in uiGradients (MIT licence), painted the way its own CSS paints them: hex stops, evenly spaced, left to right.

MeasuredPresetsNote
Lose over half their chroma through the middle (grey dead zone)38 of 382All 38 keep it in OKLCH
Cannot carry 4.5:1 text in white or black anywhere on them146 of 382Median scrim to fix: 31% black
Pass 4.5:1 on average while failing at the worst pixel146 of 382The average is the figure most checkers give
Bands 8px or wider across a 1440px hero, undithered325 of 38268 of them 24px or wider

Every preset in this tool passes all three checks: its text colour clears 4.5:1 over every pixel, it keeps its chroma through the middle, and where it would band it carries grain. A test fails the build if one stops passing. For colour work that is not a gradient, the contrast checker and the ramp and token generator use the same colour engine.