Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Using CSS Custom Properties (Variables) with JS

by kirupa   |   filed under JavaScript 101

When setting CSS property values via JavaScript, especially the really complex ones, you will often find yourself wrestling with strings:

var myCircle = document.querySelector("#myCircle");
setTranslate(50, 75, myCircle);

// Old approach
function setTranslate(xPos, yPos, el) {
  el.style.transform = "translate3d(" + xPos + ", " + yPos + "px, 0)";
}

// Slightly better ES6-based old approach
function setTranslate(xPos, yPos, el) {
  el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
}

Don’t get me wrong. I love a good string manipulation here and there, but you have to admit that something like the above is a bit frustrating to generate and prone to all sorts of errors if you aren’t careful. The more complex the value you are trying to set, the worse everything is. Besides, there is also something just really odd-looking about having giant strings of CSS embedded inside your JavaScript.

In this article, we are going to look at something kwown as CSS Custom Properties (aka CSS Variables) that will greatly simplify how we specify a complex value by getting us out of the string generation business. Let's see how we can do that!

Onwards!

What are CSS Custom Properties / Variables?

One of the big recent additions to the CSS language is this thing known as CSS Custom Properties, commonly also referred to as CSS Variables. What CSS custom properties allow you to do is pretty neat. Inside a style rule, you can specify a custom property name and initialize it to whatever value you want:

#container {
  --myAlign: center;

  width: 100%;
  height: 350px;
  background-color: #0099FF;
  display: flex;
  align-items: var(--myAlign);
  justify-content: var(--myAlign);
}

This custom property can then be used elsewhere in your CSS where you can specify it instead of specifying the inline value directly:

#container {
  --myAlign: center;

  width: 100%;
  height: 350px;
  background-color: #0099FF;
  display: flex;
  align-items: var(--myAlign);
  justify-content: var(--myAlign);
}

Notice in this example that the value for the align-items and justify-content properties isn't specified directly. It is inferred from the custom --myAlign property name instead.

Just like with variables you would use in JavaScript, you now have a single location where the value is being specified. If you change the value of our custom myAlign property, any uses of it will use the new value instead. This is all pretty consistent with how variables work. The CSS-specific behavior has to do with scope. The custom property you define follows typical CSS cascading rules, so where you specify the property is important to determine whether the property’s value can be used. If you wish to declare some custom CSS properties globally, you can specify them in the body selector or go one level higher with the root selector instead:

:root {
  --logoColor: "#333";
  --headerColor: "green";
  --avatarWidth: 150px;
}

If you aren't familiar with the root selector, it roughly translates to styles being applied at the html tag level.

Now, to modify the values of a custom CSS property using JavaScript, there isn’t anything special you have to do. The tried and tested setProperty method that you have used in the past will still work:

myLogo.style.setProperty("--logoColor", "#505168");

In the above code, we are setting our --logoColor custom property to a dark shade of purple. Because we are setting this value inline on our myLogo element, it will have a higher specificity and override whatever value we had earlier in the root style rule. It goes without saying that any CSS property values that rely on the value of --logoColor will automatically update to reflect the new value.

If you are wishing to read a custom CSS property value, there are a few minor extra steps you need to follow. Let's say that you have a custom property called themeColor set on the body selector:

body {
  --themeColor: "blue";
}

The way you would read this property and its value is by using getComputedStyle:

var bodyStyle = getComputedStyle(document.body);
var theme = bodyStyle.getPropertyValue("--themeColor");
alert(theme);

This seems pretty easy, right? Well, you probably know that whenever a question like that is asked the answer is never a straighforward yes. You would think the value we would ultimately get is "blue". What we would actually get is " blue". Ignore the quotation marks, but notice the leading space. This is because the original behavior as defined by the W3C (only for custom properties) is to read the space value between --themeColor: "blue" as a valid entry. That behavior is due to change in the near future to ignore the leading space. This means that if you are using a conditional to check for the custom property value, you need to account for both cases with and without a leading space:

if ((theme == "blue") || (theme == " blue")) {
  // do something
}

You can read more about this strange behavior in this forum post and related Twitter discussion.

Setting Complex Values Easily

This ability for us to modify a custom CSS property value via JavaScript and have all uses of that property update is a massive win. We no longer have to specify our values using string manipulation logic like we saw at the beginning of this tutorial.

Continuing our transform example, in the CSS variable-based world, we can specify the values for the horizontal and vertical values as follows:

#myCircle {
  --xPos: 0px;
  --yPos: 0px;
  
  width: 100px;
  height: 100px;
  transform: translate3d(var(--xPos), var(--yPos), 0);
}

To update these values, our JavaScript can just be the following:

var myCircle = document.querySelector("#myCircle");
setTranslate(50, 75, myCircle);

function setTranslate(xPos, yPos, el) {
  el.style.setProperty("--xPos", xPos + "px");
  el.style.setProperty("--yPos", yPos + "px");
}

Notice that we no longer have to generate a massive string, worry about escaping the characters at the right places, and so on. We just update the value of the custom CSS properties directly, and that updating takes care of everything else! Another benefit we get from this approach is this: if we ever wanted to get even more complex with our transform property, we can totally do so without having to make changes in JavaScript:

#myCircle {
  --xPos: 0px;
  --yPos: 0px;
  
  width: 100px;
  height: 100px;
  background-color: #FFF;
  border: 10px solid #0066CC;
  border-radius: 50%;
  transform: translate3d(var(--xPos), var(--yPos), 0)
              rotate(30deg)
              scale3d(1.5, 1.5, 0);
}

We added the rotate and scale3d functions, but it doesn't modify what our JS does. Now, you may feel the transform property is an easy one that doesn't effectively highlight the benefits. If so, feast your eyes on the following:

#container {
  --stripeColor: #1F505B;
  
  width: 100%;
  height: 400px;
  background-color: #3891A6;
}
#container.stripes {
  background-image: 
    repeating-linear-gradient(
      -45deg, 
      var(--stripeColor), 
      var(--stripeColor) 20px,
      #3891A6 20px,
      #3891A6 40px
    );
  background-size: 200% 200%;
  animation: slide 5s linear infinite;
}

What we have here is a fairly simple linear-gradient function for our background-image property. Despite being simple, it is already six lines. Imagine having to set the stripe color without being able to rely on the --stripeColor custom property? Would you really want to generate that full string inside some JavaScript in this case?

CSS custom properties make setting complex values via JavaScript extremely simple. As long as we don’t modify our custom CSS property name or the kind of value it expects, we can do whatever we want inside the style rule without having to ensure our JavaScript is in-sync with the changes. This is something we couldn't do in the old world where we had to generate the full complex value in JavaScript even if we only needed to modify one small part of it or, worst-case, change the shape of the value entirely like going from a linear-gradient to a radial-gradient!

Conclusion

Before we had CSS custom properties (aka CSS variables), we still had the ability to set and modify complex CSS property values. It just wasn’t fun or easy, especially for really complex values like the kind you commonly run into with layout, animation/transition, backgrounds (especially involving gradients), transforms, and more. While the motivations for creating CSS custom properties may not have been to make our lives easier when setting values via JavaScript, it indirectly did end up having that effect! I certainly ain't complaining.

If you want a simple visual that summarizes all of this that you can share, check out the attached image from my following tweet:

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--