DEV Community

Cover image for CSS variables: scoping
Brian Neville-O'Neill for LogRocket

Posted on • Originally published at blog.logrocket.com on

CSS variables: scoping

Written by Chidume Nnamdi✏️

We can declare variables in CSS in the same way we declare variables in other programming languages.

We declare a variable in CSS assigned with a value. The variable can then be consumed in CSS rulesets.

In JavaScript, for example, we declare variables using the let, var and const keywords:

let nine = 9;
var ten = 10;
var eight = 8;
Enter fullscreen mode Exit fullscreen mode

Then, we consume them by referring to their variable names:

>> nine
9
>>  ten
10
>> eight 
8
Enter fullscreen mode Exit fullscreen mode

We can also do this in CSS. To declare a variable in CSS, we use this format:

--varName
Enter fullscreen mode Exit fullscreen mode

In CSS, it’ll look like this:

body {
    --mainColor: limegreen;
}
Enter fullscreen mode Exit fullscreen mode

Let’s say we declare a variable with the name --mainColor and a value of limegreen. This variable holds a color name.

To consume variables in CSS, we use var():

var(--varName);
Enter fullscreen mode Exit fullscreen mode

var will retrieve the value of the variable passed to it, and replace itself with the value:

body {
    --mainColor: limegreen;
}

div {
    color: var(--mainColor);
}
Enter fullscreen mode Exit fullscreen mode

Here, div consumes the mainColor variable using the var function. The var will retrieve the value limegreen from --mainColor and replace itself with limegreen, the value of the --mainColor variable.

So, the color of the text node in the div element will be lime green. In other words, the CSS code would translate to this:

body {
    --mainColor: limegreen;
}

div {
    color: limegreen;
}
Enter fullscreen mode Exit fullscreen mode

Scoping

The place in the CSS hierarchy where you declare a CSS variable will determine its level of visibility throughout the lower levels of the hierarchy.

A CSS variable used throughout the entire page is declared in the ::root pseudo selector, or in the html selector.

This is because all the elements on our page are enclosed in one HTML element, so CSS variables declared in the HTML element or in its ::root selector will be visible for consumption from its children.

::root is a pseudo selector attached to the root of the HTML element in a document. In an RSS document, the ::root element is attached to the RSS element.

Generally, a CSS variable is only visible to child elements of the parent element it is declared in.

Now, we have this:

<body>
    <div>Div 1</div>
    <div>Div 2</div>
</body>
Enter fullscreen mode Exit fullscreen mode

The body element is parent to its child elements div, Div 1, and Div 2.

So this is feasible:

body {
    --bgColor: limegreen;
}

div {
    background: var(--bgColor);
}
Enter fullscreen mode Exit fullscreen mode

The CSS variable bgColor is declared in the body element and consumed in a div to set the background of div elements to lime green.

This would be visible to our two div elements because they are child elements to the body element, which is where the CSS variable it consumed is declared.

Now, if the reverse was true:

body {
    background: var(--bgColor);
}

div {
    --bgColor: limegreen;
}

<body>
    <div>Div 1</div>
    <div>Div 2</div>
</body>
Enter fullscreen mode Exit fullscreen mode

The --bgColor variable is declared in the div element and consumed in the body element. Now, the body element is above the div element, so the --bgColor variable won’t be visible to the body element. As a result, the background of the body element won’t turn to lime green.

The --bgColor variable in this case will be visible to elements beneath the div elements:

body {
    background: var(--bgColor);
}

div {
    --bgColor: limegreen;
}

p {
    background: var(--bgColor);
}

<body>
    <div>Div 1
        <p>Paragraph 1</p>
    </div>
    <div>Div 2
        <p>Paragraph 2</p>
    </div>
</body>
Enter fullscreen mode Exit fullscreen mode

The --bgColor variable will be visible to the p elements because they are child elements of div.

We can see here that the visibility of CSS variables depends on a parent-child relationship. A child element is within the scope of its parent element, so it can use the CSS variable declarations in the parent scope.

Theming is done with CSS variables, and mostly the theming propagates the entire DOM tree, so the CSS variables are usually set in the html element or in the ::root element. This is because no element is placed outside the HTML element:

::root {
    --bgColor: lightcoral;
    --mainColor: limegreen;
    --borderColor: seagreen;
}
Enter fullscreen mode Exit fullscreen mode

In rare cases when we want to theme a portion of the DOM tree or to theme a DOM branch using CSS variables, we’ll set the CSS variables in the root element of the DOM branch so it will propagate down the branch tree.

<html>
    <body>
        <div class="branch">
            <p>
                Paragraph 1
            </p>
        </div>
        <p>
            Paragraph 2
        </p>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode
.branch {
    --brBgColor: palevioletred;
    --brMainColor: blueviolet;
}

p {
    background: var(--brBgColor);
    color: var(--brMainColor);
}
Enter fullscreen mode Exit fullscreen mode

The CSS variables declared in the div.branch will be visible to the p element Paragraph 1, because it is a child element of the div with class name branch. The background color and text color will be painted palevioletred and blueviolet colors, respectively. The p element Paragraph 2 will not be affected with the styling because the CSS variables --brBgColor --brMainColor will not be visible to it.

This is because it is not a child element of the div.branch.

So, this is scoping at work in CSS variables.

Global Scope

CSS variables declared in the ::root selector are said to be in the Global scope. This means that they can be accessed anywhere in the CSSOM.

Why does it work?

Like we learned earlier, the ::root selector is attached at the root of the document. All elements in the web document are under the root document, so CSS variables declared in the ::root will cascade down to all levels if the document. Alternatively, the declared CSS variables will be visible to all elements in the document.

Local Scope

Just like in the ::root selector, CSS variables can also be declared in all levels of the CSSOM hierarchy or a particular selector.

Now, CSS variables declared in these levels or in a selector are only visible or scoped locally to the selector and its child nodes.

Hoisting

CSS Variables are hoisted and they are moved on the top of the CSSOM before rendering the styles of respective HTML elements in the browser.

Just like in JavaScript, CSS Variables can be hoisted. This means that CSS variables can be used before they are declared.

var num2 = 90

var add = num1 + num2

var num1 = 10

log(add)
Enter fullscreen mode Exit fullscreen mode

In the above example, the num1 variable is being used before it is declared. That means that num1 was hoisted. Upon running the code, add will log 100. Despite being used first before declaration, JavaScript was able to get the value and perform the operation.

The same is also true in CSS variables:

body {
    background-color: var(--bgColor);
}

::root {
    --bgColor: rgb(221, 221, 221);
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the CSS variable --bgColor was used before it was declared in the ::root pseudo-selector. And the code works perfectly fine!

So, CSS variables can be accessed first and declared later. This makes CSS variables a very powerful feature.

Checking for Support

CSS variables are widely supported in major browsers, though support may be lacking in older versions of Chrome and Firefox. Support for CSS variables in IE and Edge are presently underway.

Because not all browsers support CSS variables, we can detect CSS variable feature support using the @supports.

@supports(--bgColor: rgb(221, 221, 221)) {

}
Enter fullscreen mode Exit fullscreen mode

The second option is to set a fallback value:

::root {
    --primaryColor: blue;
}

button {
    color: var(--primaryColor);
    color: blue;
}
Enter fullscreen mode Exit fullscreen mode

We set up a --primaryColor variable in the ::root selector with color blue. However, we’re not sure the browser our CSS runs on supports CSS variables. To make the code work, we added a fallback value in the button selector. This makes sure the button uses our primary color in browsers that don’t support CSS variables.

Advantages of using CSS variables

Theming

CSS Variables scoping improves the way we add and modify themes in our CSS. With CSS variables, theming in CSS won’t require extra stylesheets with different themes. Instead, all you need to do is update the CSS variables.

Styling

Leveraging CSS Variable scope improves the size, specificity, & semantics of our stylesheets

Let’s say we have a button:

button {
    padding: 10px 5px;
}
Enter fullscreen mode Exit fullscreen mode

With different button styles:

.btn-danger {
    background-color: orange;
}

.btn-success {
    background-color: lightblue;
}
Enter fullscreen mode Exit fullscreen mode

With CSS variables, we don’t have to define background-color in every button style.

button {
    --btnBgColor: blue;
    padding: 10px 5px;
    background-color: var(--btnBgColor);
}

.btn-danger {
    --btnBgColor: orange;
}

.btn-success {
    --btnBgColor: lightblue;
}
Enter fullscreen mode Exit fullscreen mode

We simply assign the --btnBgColor new values in every button style. We no longer need to override the base styles.

See? CSS Variables are very powerful, and the scoping feature makes them an ideal tool for clean, modular design systems.

Conclusion

We just treated a potential bug in CSS today.

Just as we have scoping in JavaScript and other languages, the same is true in CSS variables. We have seen one major application of CSS variables: theming. There are lots more it can offer us.

If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email, or DM me.


Is your frontend hogging your users' CPU?

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.

Alt Text

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. 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 apps — Start monitoring for free.


The post CSS variables: scoping appeared first on LogRocket Blog.

Discussion (0)