Green gradients in CSS: palettes, hex codes and blends that stay green
Green gradient palettes with hex codes, why bright greens rarely carry white text, and how to blend green in CSS.
Updated
A green gradient runs from a pale mint through a bright emerald to a deep forest green. In CSS it's one linear-gradient. Add in oklab so the green keeps its hue as it lightens, instead of drifting toward teal:
.green {
background: linear-gradient(to bottom, #d4f9e2, #4bdc95, #07915b, #003820);
background: linear-gradient(
to bottom in oklab,
#d4f9e2,
#4bdc95,
#07915b,
#003820
);
}The first line is the fallback for browsers that don't support in oklab. The demo shows that palette, seven more from the table below, green fading to white three ways, and red fading to green three ways. Everything in it is plain CSS.
Green #16a34a to white
Red #ef4444 to green #22c55e
import type { ReactNode } from "react";
/**
* The green recipes from the green gradient blog post, all plain CSS. The
* first six palettes were built in OKLCH and rounded to hex; the last two
* are the colours of free presets.
*/
const palettes: readonly { name: string; colors: readonly string[] }[] = [
{ name: "Mint", colors: ["#d3fdea", "#7ceabf", "#06ac7f", "#094834"] },
{ name: "Emerald", colors: ["#d4f9e2", "#4bdc95", "#07915b", "#003820"] },
{ name: "Forest", colors: ["#d7f5d7", "#68c36d", "#27762f", "#0e2b10"] },
{ name: "Lime", colors: ["#e7ffcd", "#b7f652", "#7db205", "#2f4505"] },
{ name: "Sage", colors: ["#e8f2e5", "#adc6a8", "#71886c", "#303c2d"] },
{ name: "Teal", colors: ["#d2f8f0", "#5fd5c3", "#148d7f", "#063630"] },
{ name: "Kiruna", colors: ["#dcfff4", "#55e6bd", "#0e9a7e", "#04332b"] },
{ name: "Polar", colors: ["#dcffee", "#43d9a8", "#12685d", "#0a1d30"] },
];
const blends = ["srgb", "oklab", "oklch"] as const;
const emerald = palettes[1].colors.join(", ");
const label = "relative text-sm font-semibold";
/**
* A box painted with `modern`, over `fallback` for browsers that drop
* gradients with an interpolation space, so no swatch ever renders empty.
*/
function Swatch({
className,
fallback,
modern,
children,
}: {
className: string;
fallback: string;
modern: string;
children: ReactNode;
}) {
return (
<div
className={`relative overflow-hidden ${className}`}
style={{ background: fallback }}
>
<div
aria-hidden
className="absolute inset-0"
style={{ background: modern }}
/>
{children}
</div>
);
}
function BlendRows({ from, to }: { from: string; to: string }) {
return (
<div className="grid gap-2">
{blends.map((space) => (
<Swatch
key={space}
className="flex h-10 items-center rounded-md px-3"
fallback={`linear-gradient(to right, ${from}, ${to})`}
modern={`linear-gradient(to right in ${space}, ${from}, ${to})`}
>
<span className={`${label} text-black`}>in {space}</span>
</Swatch>
))}
</div>
);
}
export function GreenGradientsDemo() {
return (
<div className="grid w-full gap-3 bg-[#010605] p-4">
<Swatch
className="flex h-48 items-end rounded-lg p-3"
fallback={`linear-gradient(to bottom, ${emerald})`}
modern={`linear-gradient(to bottom in oklab, ${emerald})`}
>
<span className={`${label} text-white`}>
Emerald, light to dark, in oklab
</span>
</Swatch>
<div className="grid gap-3 sm:grid-cols-2">
{palettes.map((palette) => (
<Swatch
key={palette.name}
className="flex h-20 items-start justify-end rounded-lg p-3"
fallback={`linear-gradient(to right, ${palette.colors.join(", ")})`}
modern={`linear-gradient(to right in oklab, ${palette.colors.join(", ")})`}
>
<span className={`${label} text-white`}>{palette.name}</span>
</Swatch>
))}
</div>
<p className="text-sm text-white/80">Green #16a34a to white</p>
<BlendRows from="#16a34a" to="#ffffff" />
<p className="text-sm text-white/80">Red #ef4444 to green #22c55e</p>
<BlendRows from="#ef4444" to="#22c55e" />
</div>
);
}
Green palettes with hex codes
Each row works as a four-stop gradient on its own, lightest to darkest. The first six were built in OKLCH, each on one hue, with lightness stepping down and chroma peaking in the middle, then rounded to hex. The last column is the hue each one measures after rounding. The last two are the colours of free presets.
| Palette | Light | Mid-light | Mid-dark | Dark | OKLCH hue |
|---|---|---|---|---|---|
| Mint | #d3fdea | #7ceabf | #06ac7f | #094834 | 166° |
| Emerald | #d4f9e2 | #4bdc95 | #07915b | #003820 | 158° |
| Forest | #d7f5d7 | #68c36d | #27762f | #0e2b10 | 145° |
| Lime | #e7ffcd | #b7f652 | #7db205 | #2f4505 | 128–129° |
| Sage | #e8f2e5 | #adc6a8 | #71886c | #303c2d | 138–140° |
| Teal | #d2f8f0 | #5fd5c3 | #148d7f | #063630 | 181–182° |
| Kiruna | #dcfff4 | #55e6bd | #0e9a7e | #04332b | 171–178° |
| Polar | #dcffee | #43d9a8 | #12685d | #0a1d30 | 165–250° |
Green spans more hues than most colour names. Around 125° to 130° it's lime, a yellow-green. Grass and forest greens sit near 145°, emerald and jade near 160°, and mint around 165°. Past 175° it turns teal, and past 200° it's cyan. Sage is the Forest hue with most of the chroma taken out, so it reads grey-green and calm.
The two presets are aurora colours. Kiruna stays in jade and mint the whole way down. Polar starts in mint and ends in a dark navy at 250°, the night sky behind the aurora.
Green is the lightest primary on screen
Your eye is most sensitive to green. The WCAG luminance formula weights it that way: pure green #00ff00 has a relative luminance of 0.715, against 0.213 for pure red and 0.072 for pure blue. Only mixes with green in them, like yellow and cyan, are lighter. That one fact explains most of what goes wrong with green gradients.
The most vivid green a screen can show is a light colour. In OKLCH, green at 145° reaches its highest sRGB chroma at a lightness of about 0.87. Violet peaks at 0.53 and blue at 0.49. So the green that looks most like "green" sits near the top of the lightness range, where violet and blue sit in the middle. To get a green dark enough for white text you have to give up a lot of its colour.
Hold lightness still and you can see it in contrast ratios. At OKLCH lightness 0.6, with as much chroma as sRGB allows, white text on green #029b29 is 3.67:1. On violet #8c53fe at the same lightness it's 4.39:1, and on blue #3a74ff 4.10:1.
Text on a green gradient
Most green gradients need dark text through the middle, where purple or blue gradients would take white.
- White text on the dark stops. White reaches at least 10.56:1 on every dark stop in the table. On the mid-dark stops it passes the 4.5:1 that WCAG asks for body text only on Forest's
#27762f(5.65:1) and Polar's#12685d(6.64:1). On Mint's#06ac7fit's 2.91:1. - Black text on the other mid-dark stops. Black reaches 5.15:1 or more on the mid-dark stops of Mint, Emerald, Lime, Sage, Teal and Kiruna. Every mid-dark stop in the table carries white or black at 4.5:1, just not the same one.
- Dark text on the light half.
#0f172a, a near-black navy, reaches at least 8.17:1 on every light and mid-light stop.
The familiar web greens follow the same rule. White on #22c55e is 2.28:1, and on #16a34a 3.30:1. CSS's named green, #008000, is the first that clears 4.5:1, at 5.14:1, and it's a dark, dull green. If a white heading has to sit on a bright green, darken that part with a scrim, or let the heading sit over the dark end.
How green blends with other colours
Rendered in Chrome 154, here's where the halfway colour of some common green gradients lands, measured as OKLCH chroma (0 is grey) and hue:
| Gradient | sRGB (default) | in oklab | in oklch |
|---|---|---|---|
#16a34a → white | #8ad1a4, hue 156° | #9bd1a4, hue 149° | #8ed3b2, hue 163° |
red #ff0000 → #00ff00 | #7f7f00, lightness 0.58 | #d0a800, chroma 0.153 | #f89400, chroma 0.170 |
#ef4444 → #22c55e | #888451, chroma 0.071 | #b49351, chroma 0.094 | #cc8b00, chroma 0.144 |
#16a34a → violet #7c3aed | #496e9b, chroma 0.083 | #597ea6, chroma 0.075 | #0090d0, chroma 0.139 |
#16a34a → blue #2457e6 | #1d7d98, chroma 0.093 | #0086a3, chroma 0.105 | #0093b1, chroma 0.111 |
#16a34a → yellow #facc15 | #88b72f, chroma 0.167 | #9fb93d, chroma 0.151 | #9dbb1a, chroma 0.172 |
Fading green to white, use in oklab. #16a34a has a hue of 149°. sRGB puts the middle at 156°, a little toward mint. OKLab lands on 149°, the same green only lighter. in oklch should too, since white has no hue and the CSS spec says the other colour's hue carries through. Chrome 154 puts it at 163°, 14° toward teal, the same way it pulls blue toward cyan and violet toward blue. The first three bars under the palettes in the demo show it.
Red to green, use in oklch. This is the classic traffic-light or heatmap gradient, and in sRGB its middle is mud. Pure red to pure green passes through #7f7f00, a dark olive, darker than either end. With the softer #ef4444 and #22c55e it's #888451, a grey khaki with almost no colour left. OKLab brightens it to mustard. OKLCH goes around the colour wheel through orange and yellow and lands on #cc8b00, an amber, which is what people expect a "warning" midpoint to look like. You can also add the yellow yourself, as a third stop.
Green into purple goes through grey-blue. Green and violet sit far apart on the wheel, so the sRGB and OKLab middles are a dull slate. OKLCH swings through blue and stays vivid at #0090d0. Write in oklch longer hue to go the other way, through yellow, orange and pink.
Green to blue and green to yellow are its neighbours, and every space keeps the middle coloured. Pick whichever looks right.
in oklab and in oklch work in Chrome and Edge 111, Safari 16.2 and Firefox 127. Older browsers drop the whole declaration, which is why the example at the top writes a plain gradient first.
Don't let red to green carry meaning alone
Colour blindness affects about 1 in 12 men and 1 in 200 women, and red-green is the most common type. For many of those readers, a red-to-green gradient is a range of similar yellow-browns. #ef4444 and #22c55e are close in lightness too, 0.64 and 0.72 in OKLCH, so lightness doesn't tell them apart either.
When a red-to-green gradient is decoration, that doesn't matter. When it encodes something, like a score bar or a heatmap, make one end clearly darker than the other, and put labels or numbers on it.
The most vivid greens are outside sRGB
Tailwind's greens push past what an sRGB screen can show. Tailwind 4.3.3 defines green-500 as oklch(72.3% 0.219 149.579), and converting it to sRGB needs a red channel below zero. green-600, green-700, emerald-500, emerald-600 and lime-400 are out of range too. green-400 is just inside.
A P3 display shows those colours as written. An sRGB screen shows the nearest colour it can, which for green-500 is about #00c950, a little duller. For a green gradient that has to look the same everywhere, use hex stops like the ones in the table, which are all inside sRGB.
Green gradients in Tailwind
Tailwind v4 already blends in OKLab. In Tailwind 4.3.3, bg-linear-to-b compiles to to bottom, then to bottom in oklab inside an @supports check, so a green that stays green is one class list:
<div
class="bg-linear-to-b from-emerald-100 via-emerald-500 to-emerald-950"
></div>For a red-to-green bar, switch to OKLCH with bg-linear-to-r/oklch, for the reasons above. For hex stops from the table, use arbitrary values such as from-[#d4f9e2].
Dark greens and banding
Dark greens band less than navy or deep purple, but the red channel barely moves. Kiruna's darkest stop, #04332b, against its background, #010605, differs by only 3 steps of 256 in red, 45 in green and 38 in blue. As a rough estimate, an 8-bit gradient between those two across a 1440px hero changes its green channel about once every 32 pixels, and its red channel 3 times in the whole width. On a dark screen those edges can show.
Grain or dither breaks them up. How to add film grain to a gradient adds an SVG noise layer in plain CSS. Ordered dithering in a shader covers the fix inside a shader.
A green gradient that moves
A CSS gradient holds still unless you animate it. Kiruna is a shader: mint and jade curtains of an aurora drifting over a near-black green ground, one of the free presets in the Evergreen collection and the Polar Night collection:
Kiruna, live
import { GradientBackground } from "@/components/gradient-surface";
import { kindDefaults, mergeParams } from "@/lib/gradient-engine";
// The free Kiruna preset: the aurora shader, mint and jade curtains of light
// over a near-black green ground.
const kiruna = mergeParams(kindDefaults("aurora"), {
background: "#010605",
blur: 0.35,
colors: ["#dcfff4", "#55e6bd", "#0e9a7e", "#04332b"],
grain: 0.16,
interaction: { pointer: 0.2, scroll: 0.2 },
loopSeconds: 16,
scale: 0.75,
seed: 106,
speed: 0.8,
warp: 0.45,
});
export function KirunaAuroraDemo() {
return (
<GradientBackground
defaults={kiruna}
className="flex h-[360px] items-start p-8"
>
<h2 className="text-3xl font-semibold tracking-tight text-white">
Kiruna, live
</h2>
</GradientBackground>
);
}
It draws with WebGPU where the browser supports it and WebGL2 otherwise, loops seamlessly every 16 seconds, adds a little grain against the banding above, and shows one still frame to visitors who prefer reduced motion.
Kiruna installs with the shadcn CLI and no account, into any React project with a components.json:
npx shadcn@latest add https://gradients.sh/r/c/eyJ2IjoxLCJwcmVzZXRJZCI6ImtpcnVuYSIsIm5hbWUiOiJLaXJ1bmEiLCJwIjp7ImtpbmQiOiJhdXJvcmEiLCJiYWNrZ3JvdW5kIjoiIzAxMDYwNSIsImNvbG9ycyI6WyIjZGNmZmY0IiwiIzU1ZTZiZCIsIiMwZTlhN2UiLCIjMDQzMzJiIl0sImdyYWluIjowLjE2LCJsb29wU2Vjb25kcyI6MTYsInNjYWxlIjowLjc1LCJzZWVkIjoxMDYsInNwZWVkIjowLjgsIndhcnAiOjAuNDV9fQ.jsonimport { Kiruna } from "@/components/gradients/kiruna";
export function Hero() {
return (
<section className="relative isolate min-h-[560px]">
<Kiruna className="absolute inset-0 -z-10" />
{/* Your content sits on top. */}
</section>
);
}The other free greens: Polar (a mint aurora fading into a navy sky), Greenhouse (mint and jade light behind glass blocks) and Riptide (teal mist). The rest of the Evergreen collection, from moss to phosphor, is Pro. Open any free preset to tune its colours. A free account exports PNGs up to 1920px wide and 10-second MP4 loops up to 1080p60. Kiruna's loop is 16 seconds, so set its Loop to 10 in the editor before a free MP4 export, or the clip stops before the loop closes.
Related
- Blue gradients in CSS has seven blue palettes, and why blue drifts purple as it fades.
- Purple gradients in CSS covers violet, the other end of the wheel from green.
- Pastel gradients in CSS covers pale mints, with the OKLCH range that makes a colour pastel.
- Aurora background in React builds the aurora effect Kiruna and Polar use.
- Red gradients in CSS covers the other end of a red-to-green bar, and why red fades to pink.