Fallback Strategy for Browsers Without color-mix()
Ship color-mix() safely with progressive enhancement: a static fallback color first, then the modern declaration. Covers @supports detection too.
Detailed Explanation
Shipping color-mix() in Production
Browser support for color-mix() is excellent in 2025 (95%+ of global traffic) but a handful of older Safari and embedded WebView users will still miss out. The two safe patterns:
1. Static fallback first
.button {
background: #5a3899; /* fallback */
background: color-mix(in oklch, red, blue); /* modern */
}
Browsers parse declarations in order. Older engines accept the static
hex and ignore the unknown function below. Modern engines accept both,
and the cascade picks the later one. No JS, no @supports block
needed.
2. @supports for branching layouts
If your fallback is structurally different (e.g., you want to hide an element entirely when color-mix is unsupported):
@supports (background: color-mix(in oklch, red, blue)) {
.button { background: color-mix(in oklch, var(--brand), black 12%); }
}
@supports not (background: color-mix(in oklch, red, blue)) {
.button { background: var(--brand-700); }
}
3. Fallback inside a custom property
If you build palettes via custom properties, set the fallback as the property's initial value:
@property --brand-hover {
syntax: "<color>";
inherits: true;
initial-value: #1d4ed8; /* hand-tuned fallback */
}
:root {
--brand-hover: color-mix(in oklch, var(--brand), black 18%);
}
Browsers without color-mix support fall back to the registered
initial-value.
Detect at runtime if needed
CSS.supports("color", "color-mix(in oklch, red, blue)") returns a
boolean. Use sparingly — declarative fallbacks are almost always
simpler than feature-detected JS branches.
Use Case
Progressive enhancement for any production site that already supports older browsers. Lets you adopt color-mix() today without breaking the long tail of legacy WebView users.