DEV Community

Cover image for CSS alpha(): one brand color, every transparency
richi.codes
richi.codes

Posted on Originally published at richi.codes

CSS alpha(): one brand color, every transparency

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. With rgb(), hsl() or hwb() the output is serialized as color(srgb …), according to MDN.
  • color-mix() with transparent (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])
Enter fullscreen mode Exit fullscreen mode

Two things to know:

  1. After from goes any color: a hex value, oklch(), a named color, or var(--brand).
  2. After the slash goes the new alpha. Inside it, the keyword alpha means 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));
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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%) contains var(), 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); }
}
Enter fullscreen mode Exit fullscreen mode

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 :root and then override --brand inside .teal, var(--brand-soft) in that section still uses the root color. I checked this in Chromium: the token stayed orange, while the same alpha() written directly on the element turned teal. Either declare the derived tokens on the element where --brand changes, or use alpha() inline.
  • It's from, not a comma. alpha(red, 50%) is invalid. The from keyword 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 plus alpha() calls.
  • CSS Color 5 as a whole. alpha() is new in Level 5, alongside relative color syntax and color-mix(). It's the smallest function of the set, and probably the one you'll write most often.

Sources


Originally published at richi.codes.

Top comments (0)