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.

Sky, sun and horizon
Static Sunset
Hearth
Ember
Nova
Solstice
Corona
Kiln
in srgb
in oklab
in oklch

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 top puts 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.

PaletteHorizonLow skyMid skyTop
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%, then transparent 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

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.json
components/hero.tsx
import { 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.