Sunset gradients in CSS: palettes, hex codes and a setting sun
Sunset gradient palettes with hex codes, CSS that keeps the middle from going muddy, and an animated sun.
Updated
A sunset gradient runs from pale gold at the horizon, through orange and red, to violet and a dark blue-purple overhead. In CSS that is one linear-gradient drawn from the bottom up. Add in oklch so the colours between orange and violet pass through pink and magenta, as the real sky does, instead of a dull brown-grey:
.sunset {
background: linear-gradient(
to top in oklch,
#ffd9a8,
#f58c3d 22%,
#d23e4c 45%,
#6a1f6e 75%,
#1d1240
);
}Everything below is plain CSS rendered in your browser: a full scene with a sun and a horizon, seven sunset palettes, and the same two colours blended three ways.
import type { ReactNode } from "react";
/**
* The sunset recipes from the sunset gradient blog post, all plain CSS. The
* palettes are the colours of the free sunset presets, ordered from the
* horizon up, so each swatch reads as a sky.
*/
const palettes: readonly { name: string; colors: readonly string[] }[] = [
{
name: "Static Sunset",
colors: ["#ffd9a8", "#f58c3d", "#d23e4c", "#6a1f6e"],
},
{ name: "Hearth", colors: ["#fff0dc", "#ff8a3a", "#9c2f14", "#2a0c07"] },
{ name: "Ember", colors: ["#ffe9cc", "#fe9e50", "#d6472e", "#5a0d22"] },
{ name: "Nova", colors: ["#fffaf2", "#ffd07a", "#ff6a3d", "#3a0a1a"] },
{ name: "Solstice", colors: ["#fff6dc", "#ffc85a", "#e0782a", "#5a2410"] },
{ name: "Corona", colors: ["#fff6e6", "#ffc47b", "#c9543a", "#2a1430"] },
{ name: "Kiln", colors: ["#ffe9c7", "#ff9a3a", "#e2340c", "#5c0a02"] },
];
const blends = ["srgb", "oklab", "oklch"] as const;
const skyStops = "#ffd9a8, #f58c3d 22%, #d23e4c 45%, #6a1f6e 75%, #1d1240";
const sceneLayers = (sky: string) =>
[
"linear-gradient(to top, #1a0b24 0 22%, transparent 22%)",
"radial-gradient(circle at 50% 78%, #fff3d6 0 6%, rgb(255 200 120 / 0.55) 9%, transparent 30%)",
sky,
].join(", ");
const label = "relative text-sm font-semibold text-white drop-shadow";
/**
* 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>
);
}
export function SunsetGradientsDemo() {
return (
<div className="grid w-full gap-3 bg-neutral-950 p-4">
<Swatch
className="flex h-56 items-end rounded-lg p-3"
fallback={sceneLayers(`linear-gradient(to top, ${skyStops})`)}
modern={sceneLayers(`linear-gradient(to top in oklch, ${skyStops})`)}
>
<span className={label}>Sky, sun and horizon</span>
</Swatch>
<div className="grid gap-3 sm:grid-cols-2">
{palettes.map((palette) => (
<Swatch
key={palette.name}
className="flex h-24 items-start rounded-lg p-3"
fallback={`linear-gradient(to top, ${palette.colors.join(", ")})`}
modern={`linear-gradient(to top in oklch, ${palette.colors.join(", ")})`}
>
<span className={label}>{palette.name}</span>
</Swatch>
))}
</div>
<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, #f58c3d, #2b1a6e)"
modern={`linear-gradient(to right in ${space}, #f58c3d, #2b1a6e)`}
>
<span className={label}>in {space}</span>
</Swatch>
))}
</div>
</div>
);
}
Why the colours go in that order
When the sun is low, its light crosses far more air to reach you than at noon. Air scatters short blue wavelengths much more strongly than long red ones, so most of the blue is scattered out along the way, and what arrives near the horizon is yellow, orange and red. Higher up, you see light that took a shorter path, so the sky there keeps more blue and violet. A sunset gradient copies that: warmest and lightest at the bottom, coolest and darkest at the top.
Two things follow for CSS:
- Draw it bottom up.
to topputs the first colour at the horizon, so the stops read in the order you see them. - Keep the light at the bottom. A gradient with gold at the top looks like a sunrise turned upside down, or like fire. If you need the warm end at the top for a layout reason, it reads better as a glow than as a sky.
Sunset palettes with hex codes
These are the colours of the seven free sunset presets on gradients.sh, listed from the horizon up. Each row works as-is in the gradient above: replace the four colours and keep in oklch.
| Palette | Horizon | Low sky | Mid sky | Top |
|---|---|---|---|---|
| Static Sunset | #ffd9a8 | #f58c3d | #d23e4c | #6a1f6e |
| Hearth | #fff0dc | #ff8a3a | #9c2f14 | #2a0c07 |
| Ember | #ffe9cc | #fe9e50 | #d6472e | #5a0d22 |
| Nova | #fffaf2 | #ffd07a | #ff6a3d | #3a0a1a |
| Solstice | #fff6dc | #ffc85a | #e0782a | #5a2410 |
| Corona | #fff6e6 | #ffc47b | #c9543a | #2a1430 |
| Kiln | #ffe9c7 | #ff9a3a | #e2340c | #5c0a02 |
Static Sunset and Corona end in purple, so they read as dusk. Hearth, Ember and Kiln end in deep red-brown, which reads as the last minutes of light or a fire. Nova and Solstice stay gold for longer, closer to golden hour.
Keep the middle from going muddy
Orange and indigo sit on almost opposite sides of the colour wheel. A plain linear-gradient blends them in sRGB, and the halfway colour between #f58c3d and #2b1a6e comes out as #905356, a dusty mauve. OKLab, which is usually the fix for grey midpoints, does worse here: its halfway colour is #875a69, greyer still. Both blend straight through the middle of the colour space, where colour is weakest.
in oklch blends hue around the wheel instead. Between orange (hue about 54°) and indigo (about 284°), the shorter way round passes through red and magenta, and the halfway colour is #a33f76, nearly as saturated as the two ends. Measured as OKLCH chroma, the sRGB midpoint is 0.082, the OKLab one 0.063 and the OKLCH one 0.146. The three bars at the bottom of the demo show the difference.
in oklch needs Chrome or Edge 111, Safari 16.2 or Firefox 127. Older browsers drop the whole declaration, so put a plain fallback first:
.sunset {
background: linear-gradient(
to top,
#ffd9a8,
#f58c3d 22%,
#d23e4c 45%,
#6a1f6e 75%,
#1d1240
);
background: linear-gradient(
to top in oklch,
#ffd9a8,
#f58c3d 22%,
#d23e4c 45%,
#6a1f6e 75%,
#1d1240
);
}The fallback still looks fine because the palette already has a red step between orange and violet. Without that step, an sRGB sunset goes brown.
A sun and a horizon
A sky alone looks like a wallpaper. A sun and a dark band for the land or the sea make it a scene. Both are more background layers, stacked on top of the sky. The first layer listed is drawn on top, so the horizon goes first and covers the lower half of the sun:
.sunset-scene {
background:
linear-gradient(to top, #1a0b24 0 22%, transparent 22%),
radial-gradient(
circle at 50% 78%,
#fff3d6 0 6%,
rgb(255 200 120 / 0.55) 9%,
transparent 30%
),
linear-gradient(
to top in oklch,
#ffd9a8,
#f58c3d 22%,
#d23e4c 45%,
#6a1f6e 75%,
#1d1240
);
}- The horizon is a gradient with two stops at the same position (
0 22%, thentransparent 22%), which draws a hard edge instead of a fade. - The sun is a radial gradient centred on the horizon line, at 78% from the top. The solid disc ends at 6%, a soft halo fades out by 30%, and the 9% stop with 55% opacity is the bright ring just outside the disc.
- The sky is the gradient from the top of this post.
Animate the sun setting
To make the sun sink, register its height as a typed custom property with @property. The browser can then interpolate it, which it can't do with a plain custom property or with a gradient itself:
@property --sun-y {
syntax: "<percentage>";
inherits: false;
initial-value: 62%;
}
.sunset-animated {
background:
linear-gradient(to top, #1a0b24 0 22%, transparent 22%),
radial-gradient(
circle at 50% var(--sun-y),
#fff3d6 0 6%,
rgb(255 200 120 / 0.55) 9%,
transparent 30%
),
linear-gradient(
to top in oklch,
#ffd9a8,
#f58c3d 22%,
#d23e4c 45%,
#6a1f6e 75%,
#1d1240
);
animation: sunset 12s ease-in-out infinite alternate;
}
@keyframes sunset {
to {
--sun-y: 86%;
}
}
@media (prefers-reduced-motion: reduce) {
.sunset-animated {
animation: none;
}
}alternate plays the animation forward and then backward, so the sun sets and rises again with no jump. At 86% the sun's centre sits below the 78% horizon line, so it slips behind the land.
This repaints the background on every frame, because the gradient itself changes. On a hero section that is fine on most machines. For a full-page background that runs for minutes, read CSS gradient vs WebGL gradient, which measures what these repaints cost.
The same sky in Tailwind
Tailwind v4 takes the interpolation space after the direction, so the sunset sky is one class list:
<div
class="bg-linear-to-t/oklch from-[#ffd9a8] via-[#d23e4c] to-[#1d1240]"
></div>Tailwind writes the sRGB fallback for you here (checked with Tailwind 4.3.3): the utility sets to top and switches to to top in oklch inside an @supports check. That's three stops. For all five, use an arbitrary value, which has no fallback:
<div
class="bg-[linear-gradient(to_top_in_oklch,#ffd9a8,#f58c3d_22%,#d23e4c_45%,#6a1f6e_75%,#1d1240)]"
></div>Tailwind animated gradients covers looping a gradient with Tailwind classes.
A sunset that moves on its own
CSS can move one sun along one line. A live sky, where the glow swells and the light drifts as if through haze, needs a shader. The First Light collection has eleven of them, skies that ramp from ink to cream with a glow rising over the horizon. Hearth is the free one:
Hearth, live
import { GradientBackground } from "@/components/gradient-surface";
import { kindDefaults, mergeParams } from "@/lib/gradient-engine";
// The free Hearth preset: the rise shader, a warm glow coming up over a
// dark horizon.
const hearth = mergeParams(kindDefaults("rise"), {
background: "#050303",
blur: 0.5,
colors: ["#fff0dc", "#ff8a3a", "#9c2f14", "#2a0c07"],
grain: 0.1,
interaction: { pointer: 0.2, scroll: 0.15 },
loopSeconds: 13,
scale: 1.35,
seed: 10,
speed: 1.07,
warp: 0.25,
});
export function HearthRiseDemo() {
return (
<GradientBackground
defaults={hearth}
className="flex h-[360px] items-start p-8"
>
<h2 className="text-3xl font-semibold tracking-tight text-white">
Hearth, live
</h2>
</GradientBackground>
);
}
Its four colours run from the light at the horizon to the dark sky above it, like the palette table. It draws with WebGPU where the browser supports it and WebGL2 otherwise, loops seamlessly, leans a little toward the pointer, and shows one still frame to visitors who prefer reduced motion.
Hearth 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/eyJ2IjoxLCJwcmVzZXRJZCI6ImhlYXJ0aCIsIm5hbWUiOiJIZWFydGgiLCJwIjp7ImtpbmQiOiJyaXNlIiwiYmFja2dyb3VuZCI6IiMwNTAzMDMiLCJjb2xvcnMiOlsiI2ZmZjBkYyIsIiNmZjhhM2EiLCIjOWMyZjE0IiwiIzJhMGMwNyJdLCJncmFpbiI6MC4xLCJpbnRlcmFjdGlvbiI6eyJwb2ludGVyIjowLjIsInNjcm9sbCI6MC4xNX0sImxvb3BTZWNvbmRzIjoxMywic2NhbGUiOjEuMzUsInNlZWQiOjEwLCJzcGVlZCI6MS4wNywid2FycCI6MC4yNX19.jsonimport { Hearth } from "@/components/gradients/hearth";
export function Hero() {
return (
<section className="relative isolate min-h-[560px]">
<Hearth className="absolute inset-0 -z-10" />
{/* Your content sits on top. */}
</section>
);
}The other six palettes in the table are free presets too, each with its own look: Static Sunset smears one beam of light across film grain, Ember is a single arc low in the frame, Nova and Solstice are beams and rings of light, Corona is light escaping round a dark disc, and Kiln is a dithered pixel glow.
To use any of them as an image or a video, open it in the studio and export. A free account exports PNGs up to 1920px wide and 10-second MP4 loops up to 1080p60. Hearth's own loop is 13 seconds, so set Loop to 10 seconds or less before a free MP4 export, or the video cuts the loop short.
Related
- Metallic gradients in CSS uses the same idea, colour stops placed where the light falls, for chrome and gold.
- How to add film grain to a gradient adds texture to any sky above, which also hides banding in the dark top.
- Gradients that follow light and dark mode shows how to swap a palette per theme with CSS variables.
- Pastel gradients in CSS has six pale palettes with hex codes, and why a blend between two pastels goes grey.
- Blue gradients in CSS has seven blue palettes with hex codes, and what blue into orange looks like in each colour space.
- Purple gradients in CSS has seven purple palettes with hex codes, and why violet into yellow goes muddy in sRGB.
- Green gradients in CSS has eight green palettes with hex codes, and why red into green turns to mud in sRGB.
- Red gradients in CSS has eight red palettes with hex codes, including a red-to-orange Fire ramp.