CSS Variables + JavaScript = Win! | KIRUPA

CSS Variables + JS = Win!

by kirupa   |  filed under JS Tips and Tricks

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 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.

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:

If you have a question about this or any other topic, the easiest thing is to drop by our forums where a bunch of the friendliest people you'll ever run into will be happy to help you out!

THE KIRUPA NEWSLETTER

Get cool tips, tricks, selfies, and more...personally hand-delivered to your inbox!

( View past issues for an idea of what you've been missing out on all this time! )

GOT A QUESTION?

HOT FORUM TOPICS

Serving you freshly baked content since 1998!

Killer hosting by (mt) mediatemple

Facebook Twitter Youtube Pinterest Instagram Github
BACK TO TOP
new books - yay!!!