Guide 03

Gradients & glassmorphism

Translucency is a colour problem disguised as an effect. Get the blur radius, the tint opacity and the border highlight wrong and your frosted panel becomes unreadable mud — on some devices only.

Gradients are interpolation, and interpolation has a colour space

A two-stop gradient is a series of intermediate colours calculated between your endpoints. The path those intermediates take depends on the space the browser interpolates in. The legacy default is sRGB, and sRGB is where gradients go grey: a blue-to-yellow ramp passes through a desaturated middle that looks like dishwater.

CSS Color Level 4 lets you name the space — linear-gradient(in oklch, ...) — and the difference is immediate. OKLCH keeps perceived lightness even across the ramp and takes a shorter, more saturated hue path. Supply an sRGB gradient first and override it with the OKLCH version in the same rule; browsers that do not understand the newer syntax keep the fallback.

.panel-gradient {
  background: linear-gradient(135deg, #7C5CFF 0%, #25E8C8 100%);
  background: linear-gradient(135deg in oklch, #7C5CFF 0%, #25E8C8 100%);
}

Three gradient shapes worth keeping

  • The 135° brand ramp — primary to accent, used on hero panels and the one primary button that needs to be loud. Two stops only; three-stop brand gradients date fast.
  • The surface wash — surface colour to surface colour at two different lightness steps, at 180°, with less than 6% lightness difference. Invisible as a gradient, but it stops a large card looking flat.
  • The radial glow — an accent at low alpha in a radial gradient behind content, used to imply a light source. Keep it under 20% alpha or it will lift your text contrast off its calculated value.

Banding, and how to kill it

Banding is the visible stepping you get across a wide, low-contrast gradient on 8-bit displays. The cause is simple: not enough discrete values exist between your two endpoints to cover the pixel distance smoothly. Three reliable fixes, in order of preference:

  1. Add a very faint noise layer over the gradient — an inline SVG feTurbulence at 2–4% opacity as a background layer. It dithers the steps away and costs nothing at runtime.
  2. Add intermediate stops. Going from two stops to five along the same path gives the renderer more anchors and shortens each interpolation run.
  3. Increase the contrast between endpoints, or reduce the physical length of the gradient. Banding scales with distance.

The glassmorphism recipe

A frosted panel is four values working together. Change one and you must re-check the others.

Glass panel parameters over a dark ground
LayerTypical valueWhat it does
Tint fillrgba(255,255,255,0.05–0.09)Lifts the panel off the page; too high and it stops reading as glass
Backdrop blurblur(14px–22px)Destroys the detail behind so text stays legible
Saturation boostsaturate(1.2–1.6)Compensates for the desaturation blurring causes
Edge highlight1px border at 10–18% whiteDefines the panel boundary where the blur alone cannot
.glass {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(18px) saturate(1.4);
  color: #EEF0F8;
}

On a light ground the same structure inverts: the tint becomes white at higher alpha (12–20%) and the edge highlight becomes a dark hairline, because a white border on a white-ish backdrop is invisible.

The readability trap

Contrast tools measure two solid colours. A translucent panel has no single background colour — it has whatever is behind it at that moment, which may be a photograph, a gradient or another panel. Scrolling content underneath changes the effective background continuously.

The only safe approach is to test against the worst case. Composite your tint over the lightest pixel that can appear behind the panel, and then over the darkest, and confirm your text clears 4.5:1 in both. If it cannot, raise the tint alpha until it does. That usually means glass panels carrying body text sit at a higher alpha than the decorative ones — which is correct, even though it makes them look slightly less glassy.

Performance and fallbacks

Pros: depth without heavy shadows, a sense of layering that helps users track modal hierarchy, and a look that pairs naturally with vivid generated accents.

Cons: backdrop-filter forces the compositor to re-render the region behind the element. Several large blurred panels animating at once will drop frames on mid-range mobile hardware. Blur also interacts badly with sticky elements and long scrolling lists.

Mitigate with three rules. Keep the number of simultaneous blurred surfaces in single figures. Never animate backdrop-filter itself — animate opacity or transform on a pre-blurred layer instead. And ship a fallback via @supports not (backdrop-filter: blur(1px)) that swaps to a solid surface token at the same lightness, so the layout and contrast survive unchanged. The generator emits that solid equivalent alongside every glass recipe for exactly this reason — see light and dark variations for how the surface ramp is built.