Depth and texture
- Layer multiple shadows for realistic soft depth effects.
- Use
filter: drop-shadow()instead ofbox-shadowfor non-rectangular shapes or transparent PNGs. - Use
mix-blend-modeandbackground-blend-modefor lighting overlays (limit scope withisolation: isolate)
.hero {
background-image: url('texture.png'), linear-gradient(to bottom, #fff, #eee);
background-blend-mode: soft-light;
}
Gradients and color-mix()
Use in oklch or in oklab to explicitly specify the interpolation color space for gradients or color-mix().
in oklchpreserves chroma better, but can more easily get out of device gamut, especially for bigger differences between colorsin oklabstays in gamut more easily (assuming in-gamut endpoints) but can create washed out desaturated colors in the middle, especially when interpolating between opposite hues.- DON'T use
in srgbunless you have a specific reason to do so (e.g. you are building a color picker that needs to interpolate in srgb).
Fallback
Some pre-2024 browsers do not support gradient color interpolation space. To support these browsers, use the token only when its usage is safe by defining a variable:
:root {
--in-oklab: ;
--in-oklch: ;
}
@supports (linear-gradient(in oklab, white, black)) {
:root {
--in-oklab: in oklab;
--in-oklch: in oklch;
}
}
Then use like:
.card {
background: linear-gradient(to bottom var(--in-oklab), var(--accent-color), var(--darker));
}
- Important: If you use this technique, make sure there is always a non-empty gradient preamble without it, otherwise it will be a syntax error in older browsers.
- You do NOT need this for
color-mix(). If a browser supportscolor-mix(), it also supports itsin <color-space>argument.
Patterns
Many patterns can be created via CSS gradients + hard stops, and these can be more flexible and performant than SVGs or external images as they can have access to CSS variables and lengths from the surrounding context.
You don't need to repeat the position twice — just use 0 or 0% and gradient fixup will auto-adjust it.
Examples below.
Vertical stripes of 1em width each:
background: linear-gradient(to right, var(--color-1) 50%, var(--color-2) 0) 0 / 2em;
Diagonal stripes of 1em width each:
background: repeating-linear-gradient(-45deg, var(--color-1) 0 1em, var(--color-2) 0 2em);
Checkerboard pattern with 1em squares:
background: repeating-conic-gradient(var(--color-1) 0 25%, var(--color-2) 0 50%) 0 / 2em 2em;
Polka dot with .5em radius dots spaced 2em apart (horizontally/vertically — multiply by sqrt(2) for diagonal distance):
--distance: 2em;
--radius: .5em;
--polka: radial-gradient(circle, var(--color-1) var(--radius), transparent calc(var(--radius) + 1px));
background: var(--polka) 0 0, var(--polka) var(--distance) var(--distance) var(--color-2);
background-size: calc(var(--distance) * 2) calc(var(--distance) * 2);
Simple pie chart:
.pie {
--p: 80%;
width: 60px;
aspect-ratio: 1;
border-radius: 50%;
background: conic-gradient(var(--color-1) var(--p), transparent 0%) var(--color-2);
}
Important: When using gradients to render charts, ensure there is a textual fallback for screen readers. MANDATORY: You MUST provide a semantic data table as an accessible alternative, as detailed in Accessibility Coding Guidelines under the alternate text and media guidelines.