Quickie Dev #5: Using variables… in CSS?

Because styling is as a much a science as it is an art!

This article is also available on Medium.

Frontend tech has grown amazingly for the past decade. As more and more JS frameworks popped around, as everyone jumped at the chance of making the next ground-breaking SPA, as the frontend world applauded SASS and SCSS preprocessors, browsers have silently but continuously improved, too. So much so that, today, you can do pretty incredible things with only native HTML 5 and CSS 3.

Be it to add all the nice transitions we’re now used to, various neumorphism or glassmorphism effects or even to make animations and graphics only with basic divs, CSS3 is a very powerful tool that allows us to create cross-browsers interactive and engaging experiences.

A full-HTML/CSS weather app icon, as shown in A. Walicki’s tutorial – https://www.albertwalicki.com/unique-weather-app-images

But then, I’m a programmer. So my first reaction is always to say that HTML is not a programming language and that CSS is just about changing the background color. To be honest, that’s true – meaning that HTML and CSS aren’t full-fledged multi-paradigm codified standalone programming languages like C, Python or Golang. They need a browser to interpret them and produce a usable result. On the other hand, with their latest iterations, both HTML and CSS have added lots of utilities that make them more and more dev-friendly to use. In particular, CSS has eventually introduced the notion of custom properties, or CSS variables.

Warning: this feature is marked as “experimental” on the MDN docs. Also, IE doesn’t support it. I’m one of those programmers who accepts to drop the IE compatibility, especially now that every “broad audience user” who uses a PC will most likely be using Microsoft Edge… but keep this in mind if you have specific compatibility requirements.

Why use CSS variables?

Easing the inter-teams communication

As in any other programming language, variables in CSS are an efficient to associate a value to a recognizable name. This is particularly important when you’re working in a team with UI/UX designers, and if you need to discuss the various colors, spacings or any visual feature in the app. Rather than shouting an RGB color code throughout the open-space, you can more easily exchange written out values and have a clear “mapping” that you, as a developer, can maintain without a sweat. Deciding on “the primary color of the background for the home section” and naming it --home-bg-color-primary in your stylesheet makes it way easier to insure everyone’s talking about the same thing.

CSS variables are a powerful way of giving clear recognizable names to your colors, spacings, paddings, etc.!

Reusability is key

In my opinion, though, the real benefit of CSS variables is reusability. When I did CSS before, I’d regularly copy the same background color over and over again into half my classes. Whenever I decided I wanted to change this color to be a little bluer, I had to work through a whole bunch of “find & replace” while making sure nothing was broken.

Thanks to variables, the value corresponding to the key is defined in one place and one place only. In other words, if you decide to change the background color, you’ll only need to change it there, and everything will automatically update!

Scoping, theming, making design systems

Variables are also useful for proper scoping and very helpful when you’re working on a design system. I won’t go into the details here but, roughly speaking, a design system is a library of reusable components and rules that you can apply throughout your app so it is all consistent and results in an intuitive, coherent and well-thought user experience.

Those components can be the colors or the margins you use, the font styles and what type of content they apply to, the grid layouts you fit your UI elements in, the icon set your designers picked, etc. Today, as lots of front dev teams use component-driven frameworks (like ReactJS, VueJS or AngularJS for example), the design system can even encompass the creation of a lib of code components: the coders and the designers work together on defining a list of reusable interface components with all their usage rules and allowed interactions.

For example, such a design system can end up defining a Button as follows:

  • it has a padding of 8 pixels
  • it uses a font size of 14px
  • it’s on a black background and has a white text
  • it has rounded corners
  • whenever the Button is used, it should be placed inside grid layouts with spacings of 16 pixels

A design system usually completely specifies UI elements so they can later be implemented into the app without further headaches!

You see that the more complex the UI component is, the more rules (and interactions) you may need to define – but once that’s done, you can – in theory – implement the app quicker because you don’t need to go and bug the designers about all these rules repetitively 😉

Note: this hierarchy of complexibility is especially important in Brad Frost’s atomic design where you categorize your elements as atoms (very simple components), molecules (more complex components) or organisms (the most complex UI components).

Something crucial when creating a design system is to define your design tokens clearly and with a neat scope. Basically, that’s the difference between saying “that’s my light blue color” and “that’s the color of the call-to-action button on my profile page”. The former is just a variable, whereas the later has context and a tangible relationship to your product: it’s a token.

CSS variables can be very interesting for this “multi-layers” scoping. Consider the following list of variables (ignore the -- prefix and the var()  for now, I’ll talk about this in a sec):

--blue-primary: #1580f2;
--black: #000000;

--text-color: var(--black);

--contacts-bg-color: var(--blue-primary);
--contacts-text-color: var(--text-color);

See how they are gradually more and more “precise” in terms of UI space? The big advantage is that if we were to change the primary blue, all variables depending on it would change too – but if we wanted to introduce a specific text color for the “contacts” section, we could do so without having to go through all of our classes again. It’s great for centralizing your design system decisions in one place, and then re-exploding them in your stylesheets.

Note that this scoping can also be used for theming (it’s part of the reusability thing I was mentioning before): if you have several apps that are all using the exact same layouts and only differ in a few variables such as the color palette, then you can easily use this separation between variables and tokens to build the “themable” app (with tokens) and then modify your variables when you go from one project to the other. For example, you could say that for Project A, your primary color (which is then passed on to all your background colors) is red, while in Project B it’s green; and all you’d have to change is the “primary” variable of your stylesheet! 🙂

How to use CSS variables?

The -- prefix and the var() function

When you want to define a custom property in CSS, you need to prefix it with two dashes: --. This declares the value in your stylesheet and maps it to the name you gave it. Then, you can use the var() function to “call” the variable and get its value. Using the var() function injects the value into your CSS, meaning that your CSS variables can even be parts of CSS values:

--padding-horizontal: 8px;
--padding-vertical: 4px;
/* ... */

#my-div {
  padding:
    var(--padding-vertical)
    var(--padding-horizontal);
  /* equivalent to: "padding: 4px 8px;" /*
}

The :root element

As I said earlier, CSS variables are a nice way to group things – but where can you put document-level info in a stylesheet? An obvious answer would be to use the html tag since, by definition, it’s always there (and unique) in any HTML document. However, it’s not recommended because of specificity and overrides (basically, the variables you define could be overwritten by more “powerful” elements).

The :root section is invisible, it’s at the same level as the html tag but it has a higher specificity. So a common way of adding custom properties to a project is to directly place the variables in the :root section of the doc (say in your main.css file, or even in a dedicated variables.css that you import first in your HTML):

:root {
  --blue-primary: #1580f2;
  --black: #000000;

  --text-color: var(--black);

  --contacts-bg-color: var(--blue-primary);
  --contacts-text-color: var(--text-color);
}

And tadaa! You can now access these variables anywhere you want in your CSS code, re-using the var() function:

#contacts-section {
  background-color: var(--contacts-bg-color);
  color: var(--contacts-text-color);
}

Bonus: you can even re-define some tokens locally if need be! For example, say you have a “small” and a “large” padding, then you can do the following:

:root {
  --padding-small: 8px;
  --padding-large: 32px;
}

li {
  /* a normal li has a padding of 8px */
  --li-padding: var(--padding-small);
  padding: var(--li-padding) 0px;
}
li.spaced {
  /* a spaced li has a padding of 32px */
  --li-padding: var(--padding-large);
}

Conventions! Conventions everywhere!

Of course, your stylesheet will only be as organized as you make it. In other words: CSS variables are not a silver bullet and for all of this power to truly show, you have to decide on some conventions to follow. Identifying variables quickly by name is only possible if you named them right; inheriting a variable through several scopes can be a pain if you can’t catch instantly which scope you’re currently in.

There is no absolute rule for this. What I usually like to do (and that I’ve seen other people do, too), is:

  • to show the context in the variable name: in the previous examples, my “contacts” section scope was prefixed with --contacts-*
  • to clearly tell what property the variable is for: --bg-color--text-color--padding (or even --padding-top)… those are all self-explanatory names that I can directly relate to the right CSS property when I actually use them in my components
  • put two dashes before state variations: --bg-color--normal--bg-color--hover--bg-color--disabled

Conclusion

CSS variables are a really cool trick for building scalable, maintainable and customizable web apps. They allow you to define design systems in concert with designers and to speak the same language cross-teams. In particular, they simplify the contextualization of design tokens and help with scoping and theming. They are widely supported (except on IE…) and super-easy to use!

What about you? Do you have any CSS-variabilization tips and tricks? 🙂

Leave a Reply

Your email address will not be published.