DEV Community

Cover image for CSS Variables
Samantha Ming
Samantha Ming

Posted on โ€ข Edited on

CSS Variables

Alt Text

Move over Sass, we have #CSS variables! I still love Sass of course. But itโ€™s great native CSS supports this. No need for a preprocessor & no more compiling ๐Ÿฅณ

Global scope will allow you to access the variable everywhere. Local scope will just be within your specific selector ๐Ÿ‘



:root {
  /* 1. Defining in Global Scope */
  --color: red;

  /* 2. Available in all selector*/
  color: var(--color); /* red */
}

.local {
  /* 1. Defining in Local Scope */
  --color: blue;

  /* 2. Scoped to the .local class only */
  color: var(--color); /* blue */
}


Enter fullscreen mode Exit fullscreen mode

Story time...

It's been awhile since I had story time, so let's me share the journey of a discontent css developer who finally stopped being so angry ๐Ÿ˜‚

Yucky Duplicated CSS Code

Back in the days, I remember having to make a lot of duplicate styling...



.subtitle {
  color: #999;
}
.subtext {
  color: #999;
}


Enter fullscreen mode Exit fullscreen mode

This was always annoying to me! Because I knew the concept of variables from other programming language. So I always cringe when I see this duplication.

Sass to the rescue ๐Ÿฆธโ€โ™€๏ธ

And then I discovered Sass. Yay, I can finally utilize variables!



$secondary-color: #999;

.subtitle {
  color: $secondary-color;
}
.subtext {
  color: $secondary-color;
}


Enter fullscreen mode Exit fullscreen mode

This was all great. But then you have to deal with installing Sass to your project and the overhead of dealing with a preprocessor and compiling. And for a tiny a project, the effort of setting up Sass just doesn't make sense. So back to being a complain-y developer and tweeting a firestorm of how CSS is annoying...cause that's why Twitter was created right, it's a place for angry developers to vent ๐Ÿ˜…

Out of the way Sass, CSS is my hero ๐Ÿ’ช

CSS finally had enough of me sending it bad energy. So it introduced CSS variables. Now I can set a variable natively. No preprocessor, no extra installation, just plain CSS out of the box. Beautiful isn't it ๐Ÿ˜



:root {
  --secondary-color: #999;
}
.subtitle {
  color: var(--secondary-color);
}
.subtext {
  color: var(--secondary-color);
}


Enter fullscreen mode Exit fullscreen mode

Can I complain about the awkward syntax ๐Ÿค” Sure...but gratitude is the way to happiness. So I'm going to soak in this variable heaven for a bit...until the next story time ๐Ÿ˜‚

CSS Variable Syntax

1. Declaring a CSS variable

You prefix your variable name with 2 dashes, --.



--variable-name: some-value;


Enter fullscreen mode Exit fullscreen mode

2. Using the CSS variable

And to use it. You use pass your variable name into var().



css-property: var(--variable-name);


Enter fullscreen mode Exit fullscreen mode

Scopes in CSS Variable

You can declare your CSS variable in the global scope, meaning it can be available throughout your entire app. Or you can just set the CSS variable in the local scope, where it's only available in the specific selector.

Global Scope

To declare it in the global scope, you first set your definition inside :root {}.



:root {
  --primary-color: #000;
}

h1 {
  color: var(--primary-color);
}


Enter fullscreen mode Exit fullscreen mode

Local Scope

To declare it in the local scope, you just define your variable inside your element block. And that variable is only available within that selector. If you tried to use is somewhere, it won't have any effect.



h2 {
  --h2-color: #999;
  color: var(--h2-color);
}

h3 {
  /* No Effect */
  color: var(--h2-color);
}


Enter fullscreen mode Exit fullscreen mode

Examples

Let's go over some different ways you can have some fun defining CSS variables โœ…

You can set multiple value



:root {
  --margin: 10px 20px;
}

div {
  margin: var(--margin);
}


Enter fullscreen mode Exit fullscreen mode

You can build up values

This is handy when used it with calc(). Are you sensing some cool dynamic sizing application with this. Ya, my spidy senses are tingling too! ๐Ÿ•ท



:root {
  --value: 5;
}

div {
  padding: calc(var(--value) * 1px); /* 5px */
}


Enter fullscreen mode Exit fullscreen mode

โš ๏ธNote: You can not concatenate a unitless value with a unit. So this won't work:



:root {
  --value: 5;
}

div {
  padding: var(--value) px; /* โŒ will not give you 5px */
}


Enter fullscreen mode Exit fullscreen mode

โ˜๏ธIf you're trying to build up values, you need to use css calc() โœ…

You can reference variable in another definition



:root {
  --border-width: 10px;
  --border: var(--border-width) solid #000;
}

div {
  border: var(--border);
}


Enter fullscreen mode Exit fullscreen mode

After some testing, I found the order doesn't matter. You can reference a latter variable in an earlier definition. But coming from JavaScript where you should always use the variable after it's been defined, this seems a bit off to me ๐Ÿ˜…



/* This also works */
:root {
  --border: var(--border-width) solid #000;
  --border-width: 10px;
}


Enter fullscreen mode Exit fullscreen mode

Overriding CSS Variable Values

The great thing is you can override the global value, so can set something unique to a specific context.



:root {
  --default-color: pink;
}

.content {
  /* Override the global css variable */
  --default-color: green;
}

p {
  color: var(--default-color);
}


Enter fullscreen mode Exit fullscreen mode


<p>Default "Pink" color</p>

<div class="content">
  <p>Overridden "Green" color</p>
</div>


Enter fullscreen mode Exit fullscreen mode

Setting Fallback Value

What happens when you use a variable that has never been assigned ๐Ÿค”



p {
  color: var(--color);
}


Enter fullscreen mode Exit fullscreen mode

The answer is NOTHING. Your app doesn't break. I guess that's nice. But if that seems a bit risque, you can set a Fallback Value.



p {
  color: var(--color, pink);
}


Enter fullscreen mode Exit fullscreen mode


<p>Pink color</p>


Enter fullscreen mode Exit fullscreen mode

And if you do define it, the defined color will just take over.



p {
  --color: black;
  color: var(--color, pink);
}


Enter fullscreen mode Exit fullscreen mode


<p>Black color</p>


Enter fullscreen mode Exit fullscreen mode

Using CSS Variable in JavaScript

The best thing about using CSS variable instead of Sass variable, is that you can access it in JavaScript! And you know when JS joins the party, it's going to get crazy ๐Ÿ˜†

Let's go over the basics usage and see how we can retrieve and set our CSS variables.

Retrieve from inline style

If you set your CSS variable via inline style.



<p style="--color: red"></p>


Enter fullscreen mode Exit fullscreen mode

Then you retrieve your value with getPropertyValue():



// Get our <p> element
const element = document.querySelector('p');

// Retrieve our CSS variable "--color"
element.style.getPropertyValue('--color'); // 'red'


Enter fullscreen mode Exit fullscreen mode

Retrieve from inline or css file

If you set your CSS variable in your style tag or an external CSS file.



<style>
  p {
    --color: red;
  }
</style>


Enter fullscreen mode Exit fullscreen mode

Then you need to use this first getComputedStyle():



// Get our <p> element
const element = document.querySelector('p');

// Retrieve our CSS variable "--color"
getComputedStyle(element).getPropertyValue('--color'); // 'red'


Enter fullscreen mode Exit fullscreen mode

And yes, you can also use this way to retrieve your inline style as well:



<p style="--color: red"></p>

Enter fullscreen mode Exit fullscreen mode


// Get our <p> element
const element = document.querySelector('p');

// Retrieve our CSS variable "--color"
getComputedStyle(element).getPropertyValue('--color'); // 'red'

Enter fullscreen mode Exit fullscreen mode




Setting a CSS Variable with JS

And to adjust your CSS variable, you just use setProperty.



// Get our <p> element
const element = document.querySelector('p');

// Set our "--color" variable to be "blue"
element.style.setProperty('--color', 'blue');

Enter fullscreen mode Exit fullscreen mode




Simple Example

Let's put everything together and look at a simple example. Here we want to check our text color when the text is clicked.



<p style="--color:red; color: var(--color)"></p>

Enter fullscreen mode Exit fullscreen mode


// Get our <p> element
const element = document.querySelector('p');

// On click, change text from "red" to "blue"
element.addEventListener('click', function() {
element.style.setProperty('--color', 'blue');
});

Enter fullscreen mode Exit fullscreen mode




Retrieve Global CSS Variable from JavaScript

If you set your CSS variable in the global scope. To retrieve the value in JavaScript, you will have to go with the getComputedStyle route, but instead of passing the element, you just pass in document.documentElement. So something like this:



:root {
--color: red;
}

Enter fullscreen mode Exit fullscreen mode


getComputedStyle(document.documentElement).getPropertyValue('--color'); // "red"

Enter fullscreen mode Exit fullscreen mode




Browser Support

Beside party ๐Ÿ’ฉ Internet Explorer, the rest of the browsers are all game!

Community Input

@lisovskyvlad: IE11 :( There's JS polyfill but on our experience, it makes ie11 so slow, it's hardly possible to use :(

@phillipkent: Still always wonder why can't we just assume IE11 users are clearly okay with a terrible experience? Lol

โ˜๏ธThis is my favorite response ever. I'm definitely going to use this whenever I get asked to fix something in IE. I'm a huge advocate of accoutability. But this is the only time I'll say, "It's you, not me" ๐Ÿ˜‚

@lfdn: Some neat things you can do with css variables is easily control font sizes, spacings, colors on different viewports/pages. You could even do dark mode really easily by changing some css variables.

@jhildenbiddle: Those looking for IE/legacy support for CSS custom properties (i.e. "CSS variables") can check out css-vars-ponyfill. Forgive the self promotion (I am the author), but I thought it would be helpful for those still burdened--as I was not too long ago--with IE/legacy support.

Community Examples

@dillonheadley: css vars are awesome. Here are some cool things I've been using them for. Flexible container widths without having to overwrite the rule: CodePen

@azzcatdesign: I'm totally on board with CSS variables! I've got CodePens where I worked out responsive typography, spacers, and containers using global and scoped variables plu with calc(). Even took it further with HSL colors and abstracting the separate values to local variables for button colors. You can't do the same kind of color edits in CSS with variables as you can with SCSS ie: darken(\$color, 10%) but I've found I'm not missing SCSS variables for color, just organizing differently.

Resources


Thanks for reading โค
Say Hello! Instagram | Twitter | Blog | SamanthaMing.com

Top comments (58)

Collapse
ย 
adam_cyclones profile image
Adam Crockett ๐ŸŒ€ โ€ข โ€ข Edited

Can I just add that if you use calc (or nested calc) and JavaScript update vars and CSS vars you can have it so one js update of a variable changes a lot of logic which can all be handled in the CSS. It's a very powerful workflow. In fact so much so I am writing a language like tailwind just for this workflow.

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

that's super cool! ...btw, do you mind explaining nested calc? ๐Ÿ™‚

Collapse
ย 
adam_cyclones profile image
Adam Crockett ๐ŸŒ€ โ€ข

Sure, it's not widely known or atleast it wasn't usefull to nest calc function given that before variables, values where essentially hard coded.

:root {
   --grid-colomns: 12;
}

.thing {
    width: calc(100vw / var(--grid-columns) * calc(var(--grid-columns) * 2)
}

Okay this is a terrible example but it does illustrate how CSS can become config based and calc can do most of the work.

Thread Thread
ย 
samanthaming profile image
Samantha Ming โ€ข

Oh cool!!! Thatโ€™s super neat...I got to read this a few more time to understand it ๐Ÿ˜…...thanks so much for explaining it! Love learning new things โญ๏ธโญ๏ธโญ๏ธ

Thread Thread
ย 
adam_cyclones profile image
Adam Crockett ๐ŸŒ€ โ€ข

Your not the only one, I found this out recently, didn't know calc was nestable, your most welcome.

Thread Thread
ย 
frontendprof profile image
AbdulMalik โ€ข

Can you explain what the code snippet does, please. Or can you direct me to some articles in which it is elaborated your topic please.

Thread Thread
ย 
adam_cyclones profile image
Adam Crockett ๐ŸŒ€ โ€ข โ€ข Edited

I wasn't sure what specifically you wanted to know but, AFAIK, there are no specific posts on the topic, I may have one (I have hundreds of posts so it might take some time to find).

What does it do? its a simple implementation of a 12 column grid layout normally calculated via a pre-processor, what it does is not so important, how it does it is whats interesting here.

The short story is:

CSS is calculated by the browser, you might consider this "run-time" for css, at this point a few things are possible, bellow is a broken down list of the things I have use and what they do:

vw units: 100 vw is the full viewport width as computed pixels, it is not 100% of the parent container.
css variables: --some-variable: red; stores a value, var(--some-variable) computes the value
calc(): runtime maths such as calc(100% + 20px) which is cool, but you can also just add or subtract numbers and make calculations normally computed in css pre-processors

Oh yes and lastly, I nested a calc() inside another calc to achieve complex maths

Thread Thread
ย 
frontendprof profile image
AbdulMalik โ€ข

Thank you for the reply.
But I was implying explanation of your nested calc function. It would be great if you could elaborate on that one as well please, if you don't mind of course.

Thread Thread
ย 
adam_cyclones profile image
Adam Crockett ๐ŸŒ€ โ€ข โ€ข Edited

developer.mozilla.org/en-US/docs/W...

CSS calc 'calculate' is built into CSS and can be nested, the full documention can be found above.

However nesting refers to the fact that calc exaluates to a unit so then calc(calc(2px + 4px) + 4px)

10 pixels

But this is particularly useful for complex math with variables because variables can change in media query and stateful circumstances (:hover, :focus etc)

Thread Thread
ย 
frontendprof profile image
AbdulMalik โ€ข

thanx

Collapse
ย 
nanouchkaya profile image
Claudine โ€ข โ€ข Edited

Thank you so much! I've always been a CSS advocate but since I discovered Sass and its variables and functions, I tend to neglect CSS... So, I'm excited to get back in tiny projects without preprocessor and, as you said, with JS in the party, it's going to get crazy ๐Ÿ’ฏ

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

I was that way for a long time, Sass become a must have for all my projects and I just bared with the burden of installing it even on the small apps ๐Ÿ˜“. No more! Now i can go native all the way. Very nice for sure! thumbs up to the CSS folks ๐Ÿ‘

Collapse
ย 
dillonheadley profile image
Dillon Headley โ€ข โ€ข Edited

css vars are awesome. Here's some cool things i've been using them for.

Flexible container widths without having to overwrite the rule:

:root {
  --container-sm: 60rem;
  --container-md: 110rem;
  --container-lg: 126rem;
}


.g-container {
  max-width: var(--container-width, var(--container-md));
  width: calc(100% - var(--container-gap, 2rem));
  margin-left: auto;
  margin-right: auto;
}

...

<article class="c-supportPlans g-container">
...

.c-supportPlans {
  --container-width: var(--container-xl);
  ...
}

Font styles management:

  --muli-1: 800 1.6rem/1.25 'Muli', sans-serif;
  --muli-2: 700 1.4rem/1.25 'Muli', sans-serif;
  --muli-3: 400 1.2rem/2 'Muli', sans-serif;

  --open-sans-1: 800 3.2rem/1.375 'Open Sans', sans-serif;
  --open-sans-2: 400 2.4rem/1.167 'Open Sans', sans-serif;
  --open-sans-3: 800 2rem/1.4 'Open Sans', sans-serif;

...
.c-supportPlans-plan h2 {
  font: var(--open-sans-3);
}

and declarative simple animations:

@keyframes fromTo {
  from {
    transform: var(--from);
  }
  to {
    transform: var(--to);
  }
}

.c-industriesBanner-list {
  --from: translateX(0);
  --to: translateX(calc(-100% + -3rem));
  animation: linear 20s fromTo infinite;
  display: grid;
  grid-gap: 3rem;
  gap: 3rem;
  grid-auto-flow: column;
  grid-auto-columns: 15rem;
}

codepen.io/dillonbheadley/pen/oNXNyPa

fun stuff!

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

Thanks for sharing this @dillonheadley . Awesome work! It's always so helpful to see how people implement the tech in their own project. Let me link this in the code notes ๐Ÿ‘

Collapse
ย 
dillonheadley profile image
Dillon Headley โ€ข

Here's one more to manage buttons with vars: codepen.io/dillonbheadley/pen/PoqWqxq

Collapse
ย 
dillonheadley profile image
Dillon Headley โ€ข

For sure! ๐Ÿ‘๐Ÿผ Love your posts btw!

Collapse
ย 
rodiongork profile image
Rodion Gorkovenko โ€ข

That feeling when you are developer for many years, though mostly not front-end, but doing UI chores for work or in own side-projects... And then suddenly learns such great news by lazily reading topics on DEV.

Thank you, Samantha! God bless your efforts on sharing such things to us, lazy people! :)

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

lol, awesome! so glad you found it helpful! ๐Ÿ˜†

for an even lazier experience, feel free to check out my twitter account. and you can skip reading the code notes completely ๐Ÿ˜†

I'll even include the link to make things easier ๐Ÿ˜‚ twitter.com/samantha_ming

Collapse
ย 
cristijora profile image
Cristi Jora โ€ข

Yess CSS variables ftw. It's time to slowly get rid of scss. Other then nesting and some mixins it doesn't add a bunch of value now since css gets more of these features out of the box.

Some neat things you can do with css variables is easily control font sizes, spacings, colors on different viewports/pages. You could even do dark mode really easily by changing some css variables.

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

Dark mode is a huge beneficiary of CSS variable! let me add your input to my code notes! thanks for sharing ๐Ÿ‘

Collapse
ย 
azzcatdesign profile image
Catherine Azzarello โ€ข

Thanks, Samantha!

I'm totally on board with CSS variables! I've got CodePens where I worked out responsive typography, spacers, and containers using global and scoped variables plu with calc(). Even took it further with HSL colors and abstracting the separate values to local variables for button colors. You can't do the same kind of color edits in CSS with variables as you can with SCSS ie: darken($color, 10%) but I've found I'm not missing SCSS variables for color, just organizing differently.

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

OH that's super cool! If you don't mind, can you send me the link to it? Would love to see how you organize your code with css variables ๐Ÿ˜„

Collapse
ย 
azzcatdesign profile image
Catherine Azzarello โ€ข

Sure! Layout Overlay with Container & Grid Modifiers codepen.io/azzcatdesign/pen/PoovWmp; Type Variables: Responsive Scale Ratios codepen.io/azzcatdesign/pen/yLLWgbq; Spacing Variables (Fibonacci) codepen.io/azzcatdesign/pen/YzzbNQ...; and HSL Custom Properties for States codepen.io/azzcatdesign/pen/WNNBRj...

Thread Thread
ย 
samanthaming profile image
Samantha Ming โ€ข

This is fantastic! Thanks for sharing! Let me add this to my code notes ๐Ÿ‘

Collapse
ย 
konrud profile image
Konstantin Rouda โ€ข

As far as I remember syntax in the following example is invalid:

:root {
  --value: 5;
}

div {
  padding: var(--value) px; /* 5px */
}

We can not build up values in CSS the way it depicted in the example above,
we should always combine it with the calc(), like this:

:root { --value: 5; } 
div { 
 padding: calc(var(--value) * 1px); /* 5px */ 
}  
Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

Great catch!!!! Let me fix that, thank you!!!! ๐Ÿ’ช

Collapse
ย 
islam profile image
Islam Sayed โ€ข

Your way of explaining things are funny and awesome.

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

Hahaha thanks! Finally someone actually said itโ€™s funny! I failed as a stand up comic, so Iโ€™m practicing my routine in programming articles lol ๐Ÿ˜‚

Collapse
ย 
waylonwalker profile image
Waylon Walker โ€ข

Such an amazing well written article @Samantha Ming! I ๐Ÿ’• your images and use of emoji is ๐Ÿ‘Œ.

Sadly I am in a company with crazy high IE usage. I get reports on occasion that things are broken.... Looks fine to me. $H!1! I forgot to double check IE, then am amazed at what seemingly simple things IE is missing.

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

Thank you for the kind words, glad you it helpful ๐Ÿ˜Š

IE support ๐Ÿ˜–
@jhildenbiddle shared a polyfill he wrote that might help > dev.to/jhildenbiddle/comment/lmi3

Collapse
ย 
waylonwalker profile image
Waylon Walker โ€ข

Super thanks for the polyfill!! I will consider that. I had no idea such a core css feature could be polyfilled.

For now I have mostly settled on using styled components

Collapse
ย 
samanthaming profile image
Samantha Ming โ€ข

I definitely has used CSS variable on the global level more. But again, I've mainly used it on smaller apps. But I'm sure if my app was bigger, that finer control with local variable might be handy. To complete my toolbox knowledge, I think it's good to know so I can use it when I cross that path ๐Ÿ˜Š

The interesting thing about IE users is that it's a company restriction. Definitely not necessarily lower means or developing countries, some of them are in the financial sector, doing very well. But for some reason, they're stuck using IE. (I'm pretty sure those employees wish their employers would stop using IE ๐Ÿ˜…). Hopefully, one day we can convince all these companies to stop using IE (cause when the buck stops, the support will stop as well) I think that will be a very happy day ๐Ÿ˜‚