This note has an interactive demo. Try it in the original
CSS finally has a function that does exactly one thing: take a color and change how transparent it is. alpha(from var(--brand) / 40%) is your brand color at 40% opacity, whatever format --brand was written in. It works in Chrome 152, Firefox 155 and Safari 27, and it has been Baseline since September 14, 2026. In the demo above, a single variable feeds a glass card, a progress bar, the borders and a six-step scale. Pick a color, drag the slider, and the whole thing follows.
Why this needed its own function
If you keep a design system with tokens, you know the problem. You define --brand once, and then you need the same color at 10% for a background, 30% for a border, 50% for a glow. For years the options were keeping a separate hand-written rgba() for each one (and forgetting to update them when the brand changes), or reaching for heavier tools:
-
Relative color syntax (
rgb(from var(--brand) r g b / 40%)) works, but you have to choose a color function and spell out every channel. Withrgb(),hsl()orhwb()the output is serialized ascolor(srgb …), according to MDN. -
color-mix()withtransparent(color-mix(in oklab, var(--brand) 40%, transparent)) also works, but now you're picking an interpolation space just to make something see-through.
alpha() skips both decisions. According to the spec, it refers to an origin color and "only change[s] the alpha channel". The color components stay as they are, and "the result of this function is in the color space of the origin color".
How it works
The grammar is short. This is the one line in CSS Color Module Level 5:
alpha() = alpha(from <color> / [<alpha-value> | none])
Two things to know:
- After
fromgoes any color: a hex value,oklch(), a named color, orvar(--brand). - After the slash goes the new alpha. Inside it, the keyword
alphameans the origin color's current alpha as a number (1.0 is 100%), so you can work relative to it.
This is the pattern the demo uses, minus the slider plumbing:
:root {
--brand: oklch(62% 0.24 300);
}
.glass {
/* same color, fixed opacity */
background: alpha(from var(--brand) / 16%);
border: 1px solid alpha(from var(--brand) / 57%);
box-shadow: 0 18px 50px -18px alpha(from var(--brand) / 47%);
}
.ghost {
/* half of whatever alpha the origin already had */
background: alpha(from var(--brand) / calc(alpha * 0.5));
}
Unlike oklch(from …) or rgb(from …), alpha() doesn't expose the channel keywords (l, c, h, r, g, b). MDN is explicit that it's "solely concerned with the alpha channel". Use it when that's all you want to change, and keep relative color syntax for when you want a lighter or more saturated variant.
Seeing the color space difference
The bottom panel of the demo runs getComputedStyle() on the same color written three ways. In Chromium 153, with oklch(62% 0.24 300) as the origin and 42% opacity, I get:
alpha(from … / 42%) → oklch(0.62 0.24 300 / 0.42)
rgb(from … r g b / 42%) → color(srgb 0.626103 0.325981 0.99362 / 0.42)
color-mix(in oklab, … 42%, transparent) → oklab(0.62 0.12 -0.207846 / 0.42)
On screen, all three look the same. But only alpha() gives you back what you put in: an OKLCh color stays OKLCh. That matters if you inspect tokens in DevTools or feed computed values into other color math. With a hex origin, the same call in Chromium returns rgba(255, 82, 0, 0.4), which is still the sRGB the hex value came from.
Browser support and gotchas
- Support: Chrome and Edge 152, Firefox 155, Safari 27 (desktop and iOS), according to MDN's browser-compat-data. That makes it Baseline "newly available" from September 14, 2026, so a lot of browsers in use today still won't understand it.
-
Fallback: the classic trick doesn't work here. Normally you write a fallback declaration first, and if the browser can't parse the second one, it ignores it. But
alpha(from var(--brand) / 16%)containsvar(), so the browser can't check it until computed-value time. When it fails then, MDN explains that "the declaration is not ignored": the property falls back to its initial or inherited value. I tested the same pattern in Chromium with a made-up function:background: red; background: notafunction(from var(--brand) / 16%)ends up transparent, not red. So use@supports.color-mix()has been Baseline since May 2023, which makes it a safe plan B. This is what the demo does:
.glass { background: alpha(from var(--brand) / 16%); }
@supports not (color: alpha(from red / 50%)) {
.glass { background: color-mix(in oklab, var(--brand) 16%, transparent); }
}
The demo checks the same thing in JavaScript with CSS.supports('color', 'alpha(from red / 50%)') and shows a notice when it falls back.
-
Tokens resolve where they're declared. If you write
--brand-soft: alpha(from var(--brand) / 20%)on:rootand then override--brandinside.teal,var(--brand-soft)in that section still uses the root color. I checked this in Chromium: the token stayed orange, while the samealpha()written directly on the element turned teal. Either declare the derived tokens on the element where--brandchanges, or usealpha()inline. -
It's
from, not a comma.alpha(red, 50%)is invalid. Thefromkeyword and the slash are both required.
What to keep an eye on
- "Widely available" status. Baseline "newly available" means every engine has shipped it, not that every user has updated. Keep the fallback for a while.
-
Design tools catching up. Token pipelines and design apps that export
rgba()copies of every shade could export a single color plusalpha()calls. -
CSS Color 5 as a whole.
alpha()is new in Level 5, alongside relative color syntax andcolor-mix(). It's the smallest function of the set, and probably the one you'll write most often.
Sources
- alpha() CSS function, MDN
- CSS Color Module Level 5: Relative Alpha Colors
- Using relative colors, MDN
- CSS error handling: invalid custom properties, MDN
Originally published at richi.codes.
Top comments (0)