Add Opacity to an Existing Color

Say your brand color is the orange #f06d06. Now you need that color but with some opacity on it.

One option is the 8-digit hex code. I find it hard to know which additional two characters are needed, but it’s possible.

Hex CodeNotes
#f06d06Original Orange
#f06d0640Orange @ 25% opacity
#f06d0680Orange @ 50% opacity
#f06d06BFOrange @ 75% opacity

I’m more attracted to the relative color syntax myself. The deal with that is you use the color models function name with the from keyword, which destructures the color for you to mess with in that color model.

.add-opacity {
  /* original color */
  background: #f06d06;

  background: rgb(from #f06d06 r g b / 50%);
  background: hsl(from #f06d06 h s l / 50%);
  background: oklch(from #f06d06 l c h / 50%);
} Code language: CSS (css)

Above, I’m taking that brand hex color and applying 50% transparency. The result is the same in all the color models. That reads nicely to me. Plus it opens the door to using calc() on the individual parts of the color to mess with them if you want.

Unfortunately, the relative color syntax only works in Safari as I write.

So what are your options that work a bit more right-now-ish?

Well, one thing to consider is… not using hex codes 😬. That same orange is about hsl(26deg 95% 48%). That’s easier to apply alpha to. You do: hsl(26deg 95% 48% / 50%).

In practical terms, something like:

html {
  --brandColorHSL: 26deg 95% 48%;
  --brandColor: hsl(var(--brandColorHSL));
}

.use-with-opacity {
  background: hsl(var(--brandColorHSL) / 50%);
}Code language: CSS (css)

But if you’re going to switch, I’d say it’s worth getting into HDR colors! You can even use them quite safely with @supports:

html {
  --brandColorParts: 26deg 95% 48%;
  --brandColor: hsl(var(--brandColorParts));
}
@supports (color: oklch(75% 0.2 55.5 / 1)) {
  html {
    --brandColorParts: 75% 0.2 55.5;
    --brandColor: oklch(var(--brandColorParts));
  }
}Code language: CSS (css)

That’s getting somewhere if you ask me! You might wanna make opacity variants up-front so you don’t need to sprinkle your code with @supports variations. But hey β€” you are staying ahead of the curve! You don’t wanna get caught with a drab-looking SDR-color website, right? Hash tag in this economy.

But we’re not done here. There is another way to take any color and apply some opacity to it. Una blogged about this recently in Using color-mix() to create opacity variants. Conceptually, this is pretty simple: you take a color you already have and mix it with transparent, and that’s it. So like:

.use-with-opacity {
  background: color-mix(in srgb, #f06d06, transparent 50%);
}Code language: CSS (css)

This color-mix() stuff works great, and the support is way better (no Firefox though).

This got me thinking though… why srgb? Aren’t the newer color models just as capable or better at this? Well, it gets weird. Would you think all of these turn out the same?

background: color-mix(in srgb, #f06d06, transparent 50%);
background: color-mix(in oklch, #f06d06, transparent 50%);
background: color-mix(in hsl, #f06d06, transparent 50%);Code language: CSS (css)

I would, as transparent has no color, it would just apply the alpha and that’s that.

That would be wrong.

Test Pen

The thing is, transparent does have a color: it’s black. So depending on the literal shape and structure of a color model, mixing a color toward black has pretty big implications.

See how in the OKLCH example above, it goes a bit red-pinkish? I’m not sure I have this exactly right, but I’m guessing that in the OKLCH color model, pulling that orange toward where black gets positioned in the model pulls it through Mt. Red just enough it picks up that hue.

via OKLCH.com

To me that kinda sucks? I wish transparent didn’t have a hue, somehow, ✨magically✨. It seems to me the best result for mixing with transparency is to do it in the srgb color space.

If you can remember that 00 is the 8-digit hex code version of 0% transparency, you can “fix” this in OKLCH (and I imagine other spaces) by doing like…

background: color-mix(in oklch, #f06d06, #f06d0600 50%);Code language: CSS (css)

So there you aren’t mixing with transparent black, you’re mixing with a transparent version of the same color. Seems like extra work for nothing when you can just mix in srgb though.

This same problem has been around for ages when dealing with gradients. Doing a gradient from color-to-transparent used to be risky because of the black-ness of transparent. The trick was to use the same color, like…

.bad {
  background: linear-gradient(red, transparent);
}
.good {
  background: linear-gradient(rgb(255 0 0), rgb(255 0 0 / 0));
}Code language: CSS (css)

Safari on iOS 10 definitely had the black problem:

I don’t know when this happened, but this stopped being a problem with gradients. Both the gradients above render the same now and in the way you’d expect across browsers. Maybe there is some behind-the-scenes magic to “fix” it?? If there is magic involved, maybe some of that magic could sprinkle its way over to color-mix().

🀘

CodePen

I work on CodePen! I'd highly suggest you have a PRO account on CodePen, as it buys you private Pens, media uploads, realtime collaboration, and more.

Get CodePen PRO

One response to “Add Opacity to an Existing Color”

  1. Hey Chris, I really enjoyed reading your article.

    I think there is another way to create opacity variants in the newer color models. I discovered it through Adam Argyle’s article on CSS color-mix().

    The last rule is slightly less obvious; what happens if percentages are supplied for both colors and they don’t add up to 100%?

    color-mix(in lch, purple 20%, plum 20%)

    This combination of a color-mix() results in transparency, 40% transparency. When ratios don’t add up to 100%, then the resulting mix won’t be opaque. Neither of the colors will be fully mixed.

    Here’s a test pen

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top ⬆️