Originally posted on devinduct.
Introduction
CSS variables are an extremely useful feature and they enable us to do things we were compensating with SASS. They are defined by the CSS authors and contain a specific values that can be reused wherever needed within our project. They use a custom notation and are accessed by using the var
function.
Declaration
A CSS variable can be declared on any element, and we declare one like so:
div {
--primary-color: green;
}
They are accessed by using the var
function:
div {
color: var(--primary-color);
}
An Example Use Case
Let's define a use case where we define the following CSS rules to our HTML:
.card-wrapper {
padding: 1rem;
display: flex;
flex-direction: column
}
.card {
border: 1px solid #0068d8;
margin-bottom: 0.5rem
}
.card-title {
padding: 1rem;
background-color: #007bff;
color: #fff
}
.card-body {
padding: 1rem;
background-color: #76b8ff;
color: #fff
}
and apply them to the following HTML:
<section class="card-wrapper">
<article class="card">
<header class="card-title">
Card 1
</header>
<section class="card-body">
Lorem ipsum dolor sit amet...
</section>
</article>
<article class="card">
<header class="card-title">
Card 2
</header>
<section class="card-body">
Lorem ipsum dolor sit amet...
</section>
</article>
<article class="card">
<header class="card-title">
Card 3
</header>
<section class="card-body">
Lorem ipsum dolor sit amet...
</section>
</article>
</section>
Can you spot the repetitions? After analysing our CSS we come to a conclusion that the following properties are duplicates:
- padding
- color
Therefore, we can make an advantage of the new CSS feature. We can use CSS variables to define the padding and color for our elements. By declaring them on a :root
pseudo class we can overcome this problem and reuse both padding
and color
wherever we need to. Our updated CSS looks like this:
:root {
--primary-color: #fff;
--primary-padding: 1rem
}
.card-wrapper {
padding: var(--primary-padding);
display: flex;
flex-direction: column
}
.card {
border: 1px solid #0068d8;
margin-bottom: 0.5rem
}
.card-title {
padding: var(--primary-padding);
background-color: #007bff;
color: var(--primary-color)
}
.card-body {
padding: var(--primary-padding);
background-color: #76b8ff;
color: var(--primary-color)
}
The HTML remains the same, and after opening the page in your browser, the result should be the same.
Inheritance
CSS variables can inherit its value like any other CSS property. If no value is set for it on an element, the value of its parent is used. Let's update our CSS to reflect this:
:root {
--primary-color: #fff;
--primary-padding: 1rem
}
.card-wrapper {
padding: var(--primary-padding);
display: flex;
flex-direction: column
}
.card {
border: 1px solid #0068d8;
margin-bottom: 0.5rem
}
.card-title {
--primary-padding: 1.5rem;
--primary-color: red;
padding: var(--primary-padding);
background-color: #007bff;
color: var(--primary-color)
}
.card-body {
padding: var(--primary-padding);
background-color: #76b8ff;
color: var(--primary-color)
}
Now, if we run the page in the browser, the result should be that each card header has its padding
and color
set to 1.5rem
and red
, respectively, instead of the values defined on the :root
pseudo class. This means that all elements have inherited their color and padding from the parent (in this case the :root
pseudo class) except of the card header.
Default (fallback) Values
CSS variables do support the default or fallback values, meaning that we can define what value we want to use if the custom CSS variable fails. This is done by passing the second parameter to the var
function. The given value will be used as a substitute when the CSS variable is not defined. Let's update the CSS to reflect this:
:root {
/*--primary-color: #fff; a fallback value is used instead of this variable*/
--primary-padding: 1rem
}
.card-wrapper {
padding: var(--primary-padding, 1rem);
display: flex;
flex-direction: column
}
.card {
border: 1px solid #0068d8;
margin-bottom: 0.5rem
}
.card-title {
--primary-padding: 1.5rem;
--primary-color: red;
padding: var(--primary-padding, 1rem);
background-color: #007bff;
color: var(--primary-color, #fff)
}
.card-body {
padding: var(--primary-padding, 1rem);
background-color: #76b8ff;
color: var(--primary-color, #fff)
}
The output should, again, be the same since we've defined the white
color as the fallback value. Play around with it a little bit to see the effect.
Invalid Values
If the CSS variable has been defined with an invalid value the browser will set the initial or the parent value for the given property.
First, it will check if the property is inheritable, if it is, it will take the value from the parent (if the element has one). If the element doesn't have a parent, the default initial value for the property will be used. Let's update the card wrapper and title classes to reflect this behaviour:
...
.card-wrapper {
padding: var(--primary-padding);
display: flex;
flex-direction: column;
color: orange /* assign a new color here */
}
.card-title {
--primary-padding: 1.5rem;
--primary-color: 1px; /* assign an invalid value here */
padding: var(--primary-padding, 1rem);
background-color: #007bff;
color: var(--primary-color, #fff)
}
...
Now, if we run the page in the browser, card headers will have a color changed to orange
Dynamic Usage (JavaScript)
CSS variables can be accessed from the JavaScript dynamically. We can get or set its value like for any other property:
const el = document.querySelector(':root');
// get the value
const padding = el.style.getPropertyValue('--primary-padding');
// set the value
el.style.setProperty('--primary-padding', '3rem');
Comming Up
In the next article about CSS variables I will cover a more useful example and create a simple theme switcher. Make sure to check out for the updates on my blog :)
Further Reading
Check out the browser compatibility
Check out this article
Top comments (12)
Nice post. But you can't use the main element inside of the article element. The spec says: "A hierarchically correct main element is one whose ancestor elements are limited to html, body, div, form without an accessible name, and autonomous custom elements. Each main element must be a hierarchically correct main element."
html.spec.whatwg.org/multipage/gro...
Please update your example. Thank you
Hi, glad you liked it.
I have this information in my head, not sure from where, but back when HTML5 was released the
main
element was allowed insidearticle
, or not? The idea was that eacharticle
could have itsheader
,main
andfooter
section...I wasn't aware of this update...if it was an update in the first place :)If the
main
element wasn't allowed inside thearticle
element from the very beginning, thank you for clearing this out...I had the wrong information.I've updated the article.
I think that it was from the beginning because the main element always contained unique content. Thank you!
This is a great to-the-point post. My only problem with CSS variables (custom properties) is the fallback (specifically for IE 11 usage). If you have to apply a second parameter, it defeats the purpose of the variable itself. For instance, if you ever have to update the value of the variable, you have to update every instance of the var function throughout your code -- its not so automagic. Yes, you can find and replace, but still a hassle if your are updating more than one css variable. This is the only reason I'm sticking with sass vars. I wish there was a way to add a fallback in the declaration rather than the var function. I know it sounds silly since the value is basically there, but declaring the value within the function seems just as silly.
You can try this polyfill, it just works:
github.com/nuxodin/ie11CustomPrope...
Thank you. I agree with the point you made here. Definitely it has more sense to define the fallback value next to the variable itself. It is easier to maintain as you pointed out.
Sweet and short, thanks for sharing.
Thanks. No problem
Good article. Dynamic usage is very clear. Thanks!
No problem. I'm glad I could help.
This is great! I could've used this today :)
Thank you very much! I'm glad I could help :)