When most developers see the word “dynamic”, the first thing that comes to mind is JavaScript. However, most of the time, you can implement dynamic functionalities using only CSS. For example, think about implementing a responsive header with a toggle button, or affecting one div through a click or hover event on a different div or element. This is possible using only CSS.
In this article, we’ll learn how to add dynamic colors to our application using only CSS. Dynamic colors are defined by a content editor, meaning that the design system might not necessarily know about the color beforehand.
We can implement the dynamic colors feature using CSS variables, which are supported in most modern browsers, excluding Internet Explorer 11. There are several different ways that you can add and manipulate dynamic colors with CSS, in this article, we’ll explore a few:
calc()
functionLet’s get started!
If you’ve worked with colors before in CSS, you may already be familiar with creating colors using custom properties and the alpha channel. Just to jog your memory, consider the code below:
:root { --color: 255 255 0; } .selector { background-color: rgb(var(--color) / 0.5); }
The code above covers the simplest way of creating custom color properties, but this approach is flawed. It requires you to define the custom property color
in a color space that supports the alpha channel in its function, like the rgb(), rgba(), and hsla().
:
:root { --color-rgb: 255 255 0; --color-hsl: 5 30% 20%; } .selector { background-color: rgb(var(--color-rgb) / 0.5); background-color: hsl(var(--color-hsl) / 0.5); }
Therefore, you cannot allow a custom property’s color value to switch from one type to another. In this context, I’m using switch
, which is sort of like typecasting in languages like JavaScript and Python.
A great example of that impossibility would be something like the following:
:root { --color: #fa0000; } .selector { /* Trying to convert a HEX color to an RGB one doesn't work This snippet will not work. this just return a blank white background */ background-color: rgb(var(--color) / 0.5); }
With that said, having dynamic colors in CSS using HEX color values is not really possible. Even if you can specify the alpha channel for the HEX color, for example, #FA000060
, you can only do so in a declarative way. CSS doesn’t really have string concatenation, meaning you cannot dynamically specify the alpha channel:
:root { --color: #fa0000; } .selector { /* You can’t dynamically specify the alpha channel. This will still not do anything */ background-color: var(--color) + "60"; }
Trying to create dynamic colors with named colors is not advisable due to a lack of instructions. However, relative colors can create dynamic colors in a more powerful and usable way.
With relative colors, you can declare a custom property with a value of any color type, namely rgb, rgba, hsla, hsl, hex
, etc. You’ll be able to convert it to any other type on the fly.
If you ask me, this is, by far, the best way to add and manipulate dynamic colors. Below is an example of how you would do this:
/* - - - - - - - - - - - - Using hex Colors - - - - - - - - - - - - - - */ :root { --color: #fa0000; } .selector { /* can’t do this */ background-color: rgb(var(--color) / 0.5); /* can do this */ background-color: rgb(from var(--color) r g b / .5); }
Most people would attempt to follow the syntax on line 8, however, it won’t work. Instead, the correct syntax on line 11 shows how relative colors are used to make or manipulate dynamic colors. This methodology even works with the normal named colors:
/* - - - - - - - - - - - - Using Named Colors - - - - - - - - - - - - - - */ :root { --color: red; } .selector { background-color: rgb(from var(--color) r g b / .5); }
Like I mentioned earlier, this is sort of the same thing that happens with languages like JavaScript and Python when it comes to typecasting or type coercion.
calc()
functionAlthough adding dynamic colors works using the alpha channel, you are bound to realize that it has its drawbacks. Think of it this way. Transparent colors won’t always blend into white. Instead, they blend into the colors on which they sit.
Basically, you can get a lighter version of a color, but it won’t be consistent throughout your page because the color it sits on will impact its appearance. To ensure that the color it is sitting on doesn’t affect it, you’d need to use the opaque version of the lighter or darker color.
Previously, you would handle this in CSS by being specific in your custom property definition and defining all the channels you have individually:
:root { /* Define individual channels of a specific color */ --color-h: 0; --color-s: 100%; --color-l: 50%; } .selector { /* Dynamically change individual channels */ color: hsl( var(--color-h), calc(var(--color-s) - 10%), var(--color-l) ); }
However, you can already tell that your code is going to get really wordy really fast. And then again, values like HEX
are not really supported. With CSS relative colors, you can handle this using the calc()
function, and it will result in the same color as in the image above:
:root { --color: #ff0000; } .selector { color: hsl(from var(--color) h calc(s - 10%) l); }
You’ll notice a significant improvement over the wordy code.
These are just the main ways you’d add dynamic colors with pure CSS. But, in addition to these, you can always go back to manipulating color by using the good old filter percentage value.
The filter: brightness(x%)
function is used to dynamically change color by percentage. The x%
value will manipulate the color to that specific value. This method for working with colors is less popular because it affects the specific element, meaning that some parts of your page may appear brighter than others.
If you are looking to go beyond CSS, you can look into SASS and JavaScript. SASS comes with color manipulation functions right off the bat, including:
lighten() and >darken() complement() hue() mix() contrast-color()
I could try and expand further on these, but it would be outside the scope of this particular piece. That being said, for a better understanding, you can read further on SASS color functions in their documentation.
When adding color dynamically with JavaScript, you need only work with the DOM and the CSS color property. You have options like changing the color on page load, five seconds after page load, and more. You’re basically trying to create responses to events that the DOM can listen for, things like clicks, mouse scrolls, and key up or down. Really, the possibilities are endless.
You don’t actually need dynamic colors, but then again, you sort of do. It all comes down to your preference, the current task at hand, and how much code you want to write. Look at it this way, you don’t need dynamic colors if you aren’t doing anything dynamic with them. For example, think about things like dark mode or color changes with mouse movement.
You don’t need JavaScript to make your CSS colors dynamic. You need only take advantage of dynamic colors. Knowing how to create and manipulate them is a great tool to have in your CSS bag of tricks. Try it out and remember to have fun. Happy coding!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
3 Replies to "How to add dynamic colors with CSS"
I feel like some context is missing from the article. Where does the keyword `from` come from? This doesn’t seem to work, and I can’t find any reference to it in the CSS specs.
`–color: hsl(from var(–color) h calc(s – 10%) l);`
I’m pretty sure it has only been implemented on Safari as of right now. I’m not sure of the progress being made on Chrome, FireFox, etc. This appears to come from the Color Level 5 spec which is still in early development. Chrome has just released Color Level 4 and Color-Mix() from the Level 5 spec on Chrome 111, but I cannot find any status update on the remainder of Color Level 5. It appears they are still in the very early stages of development and implementation. I haven’t found any mentions of this on the roadmaps for at least the next few releases of Chrome.
Samual, great article! Thanks.
I was just about to say the same thing as @EdCharbeneau pointed out. Checking MDN first, I couldn’t find anything about relative colors.
With Adam’s comment I then found high level browser status for color module 5 on w3.org https://www.w3.org/TR/css-color-5/
and then the more specific tests for relative-color:
https://wpt.fyi/results/css/css-color/parsing/color-valid-relative-color.html
I think it would be good if it were mentioned up in the article section as long as browser support is so sparse.