Over time, we’ve been used to utilizing CSS pre-processors like Sass for a use case like making use of opacity to a pre-defined shade. At this time, we’ve a brand new manner to try this and extra with CSS relative colours. On this article, I purpose to shed the sunshine on that and introduce the way it works together with many sensible examples.
Let’s dive in.
The issue: Take 1
If I offer you a hex shade in CSS, how are you going to modify the opacity of that shade?
:root {
--brand-color: #9c3ce7;
}
One of many choices to try this is by utilizing a CSS pre-processor like Sass.
$brand-color: #9c3ce7;
.component {
background-color: rgba($brand-color, 0.5);
}
That works, however is there a vanilla CSS manner to try this?
:root {
--brand-opacity: 1;
--brand-color: rgb(156 60 231 / var(--brand-opacity));
}
.component {
--brand-opacity: 0.5;
background-color: var(--brand-color);
}
It really works, however it’s not one of the best resolution. We have to embrace the rgb()
model of every shade within the CSS.
Let’s preserve that in thoughts and discover one other downside.
The issue: Take 2
When utilizing colours in CSS, we frequently want a lighter or a darker variation of a predominant shade. When utilizing absolute values like a hex or an rgb shade house, it’s not attainable to edit the colour till we open a shade picker.
Whereas utilizing absolutely the shade values work, it’s limiting and time consuming. Let’s see an instance. We have now a major button. On hover, it ought to develop into barely darker.
With absolutely the colours in CSS, we would do one thing like this:
.btn {
--btn-bg-color: #9c3ce7;
--btn-bg-color-hover: #9c3ce7;
background-color: var(--btn-bg-color);
}
.btn:hover {
background-color: var(--btn-bg-color-hover);
}
The darker colours have been picked by utilizing a shade picker within the browser or a design device like Figma. Here’s a demo that showcases that. Attempt to change the colour and see.
Whereas that works, it’s a course of that requires extra effort and time even when we solely have just a few colours to work with.
One other instance is the place we would want transparency of a shade. This has loads of use-cases, to say just a few:
- Border shade for the button
- The shadow for the button wrapper

Let’s take the border shade for instance and discover how we used to do it in CSS.
Utilizing vanilla CSS
Utilizing the rgba()
shade features and tweaking the alpha worth.
.button {
border-color: rgba(0, 0, 0, 0.1);
}
Utilizing a CSS pre-proccessor
Utilizing Sass rgba()
perform to generate the rgba()
values of a specified shade in hex
format.
.button {
border-color: rgba(#000, 0.1);
}
.button {
border-color: rgba(0, 0, 0, 0.1);
}
Whereas each of the above strategies work, issues can get a bit extra difficult when you have a shade aside from black or white.
Meet CSS Relative Colours
Relative colours
Baseline 2024 newly accessible
Supported in Chrome: sure.
Supported in Edge: sure.
Supported in Firefox: sure.
Supported in Safari: sure.
Baseline 2024 newly accessible
Supported in Chrome: sure.
Supported in Edge: sure.
Supported in Firefox: sure.
Supported in Safari: sure.
Since September 2024 this function works throughout the newest
gadgets and browser variations. This function may not work in older
gadgets or browsers.
In CSS, we will now generate a shade that’s relative to a different shade. How does it work? Let’s discover out.
Let’s discover how the syntax works. To make use of relative colours, we have to specify the next:
- Colour perform
- Origin shade
- Channels
- Alpha (Elective)
color-function(from origin-color channel1 channel2 channel3 / alpha(non-obligatory))
For instance, if we need to have a black shade with 10%
transparency, we do the next:
.button {
border-color: rgb(from #000 r g b / 0.1);
}
Let’s be taught concerning the syntax within the following part.
CSS Relative Colours Syntax
At first, it’d look a bit bizarre or unclear, however that’s okay. You’re right here to be taught. I constructed the next interactive visible that helps in matching the syntax vs the precise CSS.
Attempt to hover on the syntax and discover it your self.
rgb(from#000rgb/0.1)
In plain phrases, it’s like saying:
Get the rgb values from `#000` and alter the alpha to `0.1`
Within the following instance, we’ve a button with a clear border shade. See how we will modify the colour by utilizing relative colours.
Attempt to enhance or lower the alpha worth to see the way it works.
Get me an RGB shade from the hex one and set its alpha to 0.10%.
color-function(from origin channel1 channel2 channel3 / alpha)
Utilizing relative colours will make our job a lot simpler and extra easy.
Altering the colour channels
We will use calc()
with relative colours to switch any of the colour channels.
HSL Colours
That is an instance of making a shade scale utilizing hsl()
shade perform. All we have to change is the lightness channel.
See the next CSS. What does the calc(l + 10)
do? It takes the present lightness channel worth and provides 10
to it. It will make the colour lighter.
.color-item {
background-color: hsl(from #9333ea h s calc(l + 10));
}
We will do the other and make the colour darker by subtracting 10
from the lightness channel worth.
background-color: hsl(from #9333ea h s calc(l - 10));
See the next demo:
LCH Colours
Whereas we will get relative colours with hsl()
, in some circumstances, the perceived shade doesn’t look good to the human eye.
Within the following demo, we’ve two colours with the identical lightness however a special hue.
Discover how the inexperienced shade appears to be like too brilliant in comparison with the purple, despite the fact that they’ve the identical lightness.
hsl(from var(–brand) h s l)
hsl(from var(–brand) calc(h + 90) s l)
If we use the new-ish lch()
shade house, the inexperienced appears to be like related in brightness in comparison with the purple one.
lch(from var(–brand) l c h)
lch(from var(–brand) calc(h + 90) c h)
It’s because the lch()
shade house is designed for human notion, not screens. With that in thoughts, we will use relative colours with lch()
to get colours which might be extra constant.
In a real-world instance, this downside can shine much more once we need to keep the identical distinction ratio for accessibility.
There’s additionally a shade house known as oklch(), it solves just a few issues of lch(). If you wish to be taught extra, right here is an article on Evil Martians. All through this text, I’ll use oklch()
extra.
I’ll share just a few examples later on this article.
A notice on the computed shade
When utilizing relative colours, the computed shade for hsl()
stays within the sRGB
shade house. However for brand new shade areas like lch()
and oklch()
, the computed shade stays of their shade house.
Check out the next instance:
Utilizing color-mix() perform
color-mix()
Baseline 2023 newly accessible
Supported in Chrome: sure.
Supported in Edge: sure.
Supported in Firefox: sure.
Supported in Safari: sure.
Baseline 2023 newly accessible
Supported in Chrome: sure.
Supported in Edge: sure.
Supported in Firefox: sure.
Supported in Safari: sure.
Since Might 2023 this function works throughout the newest
gadgets and browser variations. This function may not work in older
gadgets or browsers.
Because the assist for relative colours is comparatively new, we will use color-mix()
as a fallback to:
- Regulate the opacity of a shade
- Generate shade shades
Regulate the opacity with color-mix()
Within the following instance, I’m utilizing color-mix()
to regulate the opacity of a shade.
.button {
border-color: color-mix(in srgb, #000 10%, clear);
}
Test the next demo and play with the opacity enter.
Combine 10% black with transparency.
color-mix(technique, color1 share, color2, share)
Shades of a shade with color-mix()
On this instance, I used color-mix()
to get a darker variation of a shade. We have to combine the colour with a share of black.
.button:hover {
background-color: color-mix(in srgb, #8136ba, black 10%);
}
Take a look at the next interactive demo.
Combine the button shade with 10% black to create a darker hover state.
color-mix(in srgb, color1, color2 share)
Fallback with @helps
I’m wonderful with both color-mix()
or relative colours. On the finish, it is determined by the use-case and the browser assist for our supposed viewers.
We will use a function question to fallback to color-mix()
. Right here is an instance that fallbacks to color-mix()
if relative colours aren’t supported.
.button {
border-color: color-mix(in srgb, #000 10%, clear);
@helps (shade: rgb(from #000 r g b / 0.1)) {
border-color: rgb(from #000 r g b / 0.1);
}
}
Now that you understand how the syntax works and how you can fallback with color-mix()
, let’s discover some use-cases.
Use circumstances for Relative Colours
Altering opacity or alpha
Button border
Usually, we have to apply opacity to a shade. That is pretty easy with relative colours.
.button {
border-color: rgba(from #000 r g b / 0.1);
}
Within the following instance, the “Cancel” button has a translucent border shade. It could actually work with totally different background parts.
We will change the border shade to white, too. Within the following variation:
- Modified the border shade to white
- Modified the alpha to
0.2
Dynamic gradient
A typical UI impact is taking the dominant shade of a picture, then making use of it as a gradient.
On this instance, I’m utilizing a package deal known as Quick Common Colour to get the dominant shade, then I exploit it with relative shade to scale back its opacity/alpha worth.
Play with the alpha worth within the following demo, or change the picture.
Get me an RGB shade from the hex one and set its alpha to 0.95.
Change the picture
Extra espresso, much less depresso.
Right here is the CSS:
.hero {
--color: #706f71;
--gradient-fade: rgb(from var(--color) r g b / 0.95);
&:after {
background-image: linear-gradient(
120deg,
var(--gradient-fade) 45%,
clear
);
}
}
I really like this. No want for Sass anymore.
In 2016, I wrote about utilizing rgba()
colours for CSS Tips, and now, I wish to revisit this subject however with relative colours as a substitute.
Within the following demo, I constructed a Trello-like header with a dynamic background shade:
- for the button and inputs
- for the underside header
Shadows
on this instance, I’ve parts with totally different background colours, and I need to apply a coloured shadow to them. I can use relative colours to generate the shadow shade dynamically.
Right here is the HTML, every merchandise has its background shade set by way of a CSS variable.
<div class="gadgets">
<div class="merchandise" model="--bg: #fff;"></div>
<div class="merchandise" model="--bg: #7678ed"></div>
<div class="merchandise" model="--bg: #3d348b"></div>
</div>
In CSS, I used that variable in one other CSS variable to generate the shadow shade. Have a look:
.merchandise {
--shadow-color: rgb(from var(--bg) r g b / 0.85);
box-shadow:
0 5px 10px 0 var(--shadow-color),
0 15px 30px 0 var(--shadow-color);
}
Right here is an interactive demo. Attempt to change the alpha worth to see the way it works.
box-shadow: rgb(from var(–bg) r g b / 0.85)
–bg: #7678ed
–bg: #f7b801
–bg: #f35b04
Background overlay
On this instance, we’ve a modal with a backdrop to separate the modal content material from the web page. To do that, I used relative colours.
.modal {
background-color: rgb(from #000 r g b / 0.1);
}
Right here is the interactive demo. Attempt to change the alpha worth to see the way it works.
Delete account
Are you certain you need to delete your account? This motion can’t be undone.
Dividers
On this instance, I’ve a modal with a border and a line that separates its sections. I used relative colours to get the colour of the border and the road.
Right here is the CSS:
:root {
--outline-color: #222;
--outline-alpha: 0.15;
--outline: rgb(from var(--outline-color) r g b / var(--outline-alpha));
}
.darkish {
--outline-color: #f3f5f7;
--outline-alpha: 0.2;
}
.modal {
border: 1px strong var(--outline);
}
.separator {
background-color: var(--outline);
}
Right here is an interactive demo that showcases the above CSS:
- Attempt to modify the alpha worth
- Change from mild to darkish mode
–outline-color: rgb(from #f3f5f7 r g b / 0.20)
Bio
Designer. Creator of Debugging CSS. Lifelong Learner.
Hyperlink
https://ishadeed.com/
Darker shade
With the hsl()
shade perform, we will simply generate a lighter shade by utilizing the l
channel. On this instance, we’ve a button that I need to make darker on hover.
What’s nice about relative colours is that we will use CSS calc()
to switch any of the colour channels.
For instance, this snippet exhibits modifying the lightness channel to make the colour darker.
.button:hover {
background-color: hsl(from #9c3ce7 h s calc(l - 20));
}
See the next demo and attempt to change the lightness worth.
Get me an HSL shade from the hex one and cut back its lightness by 20.
Computed HSL:
What’s highly effective about that is that we don’t must know the hsl()
values of the unique shade. We will simply move the hex worth because the origin shade and let the browser do the remaining.
Lighter shade
Much like the earlier instance, we will use relative colours to generate a lighter shade. On this instance, I simply modified the minus to a plus.
Get me an HSL shade from the hex one and enhance its lightness by 20.
Computed HSL:
Constant distinction ratio
Say we’ve a listing of tags. Their brightness is similar however the hue is totally different. We will use relative colours to generate them for us, however there may be an fascinating element. Let’s discover out.
When utilizing hsl()
colours, the distinction ratio isn’t constant. The reason being that the lightness in HSL isn’t perceptually uniform. Which means, two colours with the identical lightness can seem totally different to the human eye.
Here’s a demo with HSL colours:
HSL colours
Meat❌ N/A
Fish❌ N/A
Rice❌ N/A
Eggs❌ N/A
Milk❌ N/A
Cake❌ N/A
Tea❌ N/A
Nuts❌ N/A
Distinction ratio for AA
Nevertheless, within the oklch()
shade house, the lightness is perceptually uniform. Which means colours with the identical lightness may have (nearly) the identical distinction ratio.
I did a check to verify the AA distinction ratio for every.
OKLCH colours
Meat❌ N/A
Fish❌ N/A
Rice❌ N/A
Eggs❌ N/A
Milk❌ N/A
Cake❌ N/A
Tea❌ N/A
Nuts❌ N/A
Distinction ratio for AA
Discover how the distinction ratio is considerably higher than the HSL colours.
Higher white various
What if we might use a textual content shade based mostly on the background shade? We will do this with relative colours by altering the lightness and chroma values.
Right here is an instance:
.tag {
shade: oklch(from var(--brand-1) 1 0.1 calc(h + 90));
background-color: oklch(from var(--brand-1) l c calc(h + 90));
}
This may yield fascinating outcomes. The textual content shade will look extra associated to the background shade.
Within the demo beneath, attempt to:
- Change the white checkbox on or off
- Change between
oklch()
andhsl()
The important thing findings from this demo are:
- Distinction ratio isn’t constant when utilizing
hsl()
- When utilizing
oklch()
, the ratio is in line with and with out the customized textual content shade
Grey variant with oklch()
In oklch()
shade house, we will grayscale a shade by setting the chroma to 0
. On this instance, I wished to model a disabled button.
First, I created a CSS variable for the chroma and used that within the oklch()
shade perform. If the button is disabled, I set the chroma to 0
.
.btn {
--chroma: c;
background-color: oklch(from var(--brand-1) l var(--chroma) h);
}
.btn[disabled] {
--chroma: 0;
cursor: not-allowed;
}
See the next demo:
.btn {
--chroma: c;
background-color: oklch(from var(--brand-1) l var(--chroma) h);
}
.btn:hover {
--chroma: 0.5;
}
.btn[disabled] {
--chroma: 0;
cursor: not-allowed;
}
Whereas that works, we would must have a a lot lighter grey shade. To realize that, we will play with the lightness worth.
.btn[disabled] {
--chroma: 0;
background-color: oklch(from var(--brand-1) calc(l + 0.25) var(--chroma) h);
}
A number of variants of a shade
I lately noticed the brand new TechCrunch design and I noticed a UI impact that I discovered fascinating. Have a look:

There are a number of variants of the principle inexperienced shade. We will create them dynamically utilizing relative colours. Here’s a demo. Attempt to change the hue worth and see what occurs.
E-newsletter
That is some random content material.
That is some random content material.
That is some random content material.
That is some random content material.
.separator {
--h-value: 0;
background-color: oklch(from var(--primary-color) 0.45 c h);
}
.separator:after {
background-color: oklch(
from var(--primary-color) calc(l + 0.07) calc(c + 0.05) h
);
}
.separator:earlier than {
background-color: oklch(
from var(--primary-color) calc(l + 0.4) calc(c * 0.4) h
);
}
I performed with the values of lightness and chroma to get the colour wanted. What’s nice about utilizing oklch()
is that the lightness might be constant it doesn’t matter what the hue worth is.
Complementary shade
On this instance (impressed by TechCrunch), I used relative colours to get the complementary shade of the principle shade.
.part {
--sep-primary-color: oklch(from #5631ea l c calc(var(--h-value)));
--secondary-color: oklch(
from var(--sep-primary-color) calc(l + 0.8) calc(c - 0.03) calc(h + 210)
);
&:after {
background-color: var(--secondary-color);
}
}
.sectionTitleIcon {
background-color: var(--secondary-color);
svg {
shade: var(--sep-primary-color);
}
}
Here’s a side-by-side comparability of utilizing oklch()
and hsl()
.
oklch()
Most
Widespread
- Microsoft ramps up AI efforts to compete with OpenAI
- Sign President calls out agentic AI safety points
- Google co-founder Larry Web page launches new AI startup
- Scale AI beneath investigation by US Division of Labor
- ChatGPT on macOS can now straight edit code
- Mistral provides API to transform PDFs to AI-ready Markdown
hsl()
Most
Widespread
- Microsoft ramps up AI efforts to compete with OpenAI
- Sign President calls out agentic AI safety points
- Google co-founder Larry Web page launches new AI startup
- Scale AI beneath investigation by US Division of Labor
- ChatGPT on macOS can now straight edit code
- Mistral provides API to transform PDFs to AI-ready Markdown
Utilizing oklch()
supplies a way more constant colours and distinction ratio. Discover how the colours in hsl()
are generally too brilliant and the textual content is tough to learn.
Not solely that however utilizing hsl()
could cause an issue known as shade vibration.
Colour vibration is a visible flickering impact that occurs when utilizing two extremely saturated colours on prime of one another.
I included a screenshot of the issue beneath. Please notice that the screenshot would possibly trigger discomfort or visible pressure
Colour Vibration

Conclusion
CSS relative colours present us with a brand new method to generate colours dynamically. It’s nonetheless comparatively new, however that shouldn’t cease you from making an attempt it as we speak. I even haven’t explored all of the uses-cases but (e.g: shade palettes), and most popular to concentrate on probably the most sensible ones that I personally discover helpful.
I hope that you just loved this text. Thanks for studying.
Additional assets
A number of hyperlinks helped to reply some questions whereas I used to be researching the article’s subject.