Creating Systems with CSS Custom Properties

Originally published on 20 January, 2021 by Jacob Stordahl

What are CSS Custom Properties? and why use them?

CSS custom properties, also known as CSS variables, allow the creation of custom CSS values. They provide many things and, in my opinion, should be used whenever possible. For example, my most common use case for Custom Properties is to create design systems that keep my application's styles cohesive & quick to modify. Let's look at one of the most likely use cases you'll come across: color.

  /* we (almost) always want to declare our variables on the :root selector */
  /* this allows our variables to be available anywhere in our website or app */
  :root {
    --white: #fff;
    --black: #1f1f1f;
    --red: #ed6051;
    --background: var(--white);
  }

  html {
    background: var(--background);
    color: var(--black);
  }
 
  a:hover {
    background: var(--red);
  }

The first step in the above example is to declare our Custom properties; we do this by prepending the -- before the property name. Then we assign that new property a value - in this case all of our values are colors, but this value can be any valid CSS property value. These custom property values can also be other custom properties, like you see with our variable --background. Also, to reiterate, usually declaring your custom properties on the root element of the DOM node tree is best practice. It keeps all of your variables in the same place so they're easier to manage, and then they are also accessible to any child node in the DOM tree, thus they are available anywhere in our site or app.

Now that we've created our custom properties, we just need to use them in our CSS. In the above example, we're using our --background variable to set the background of our <html> element as well as the --black variable to set the font color. Lastly, we're adding a --red background on any links when they're hovered over. Remember that the custom property is the value following the -- & does NOT include the var() function. This function is simply the method that is rendering our custom property, but is not explicitly a part of said custom property. Now let's look at how we can use custom properties to create efficient styles.

Methodology for creating systems with CSS Custom Properties

  :root {
    --border: 2px solid var(--black);
    --radius: 15px;
    --shadow: 3px 3px 5px var(--black);
  }

  button, input, select, text-area {
    border: var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
  }

A great example of the benefit of custom properties is styling input elements. Often times button, input, select, & text-area elements have similar styles so they visually match in the browser. By declaring custom properties for border, border radius, & box shadow, we can style all of these elements at once. However, the real benefit to custom properties is, if we ever have a need to change one of these properties for every form element on our website, we just need to edit one line of code. The great thing about custom properties is there are so many unique and interesting ways to use them in your CSS; the only limit is your imagination! To give you some ideas, I'd like to show some of the use cases I choose to utilize custom properties.

Technical Examples

Easy Themeing

Custom properties can be used to create themes simply by creating a class that redifines any custom properties effected by the theme. Here's the simplest example of this concept...

  :root {
    --background: #fff;
    --fontColor: #1f1f1f;
  }

  .dark-mode {
    --background: #1f1f1f;
    --fontColor: #fff;
  }

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

The last piece of this simple dark mode is to use Javascript to toggle the dark-mode class onto the body element.

Typography Systems

Using custom properties with typography removes a lot of the headache of typography in CSS...

  :root {
    --fontColor: #1f1f1f;

    /* Heading (h1) */
    --headingFont: 'Helvetica Neue', sans-serif;
    --headingSize: 3rem;
    --headingWeight: 700;

    /* Sub-heading (h3) */
    --subHeadingFont: 'Open Sans', sans-serif;
    --subHeadingSize: 1.5rem;
    --subHeadingWeight: 500;

    /* Body (p) */
    --bodyFont: 'Open Sans', sans-serif;
    --bodySize: .9rem;
    --bodyWeight: 300;
  }

  body {
    font-size: var(--bodySize);
    color: var(--fontColor);
  }

  h1 {
    font-family: var(--headingFont);
    font-size: var(--headingSize);
    font-weight: var(--headingWeight);
  }

  h3 {
    font-family: var(--subHeadingFont);
    font-size: var(--subHeadingSize);
    font-weight: var(--subHeadingWeight);
  }

  p {
    font-family: var(--bodyFont);
    font-size: var(--bodySize);
    font-weight: var(--bodyWeight);
  }

In this example, we're declaring three custom properties for our main heading (h1), a subheading (h3), and our body content (p). the properties we have defined for each of these elements are the font family, font size, & font-weight. Using this type of system keeps your code readable, intuitive, and super flexible. Let's say we want to decrease the heading size on mobile; all we need to do is redefine our custom property with a media query...

  @media screen and (max-width: 600px) {
    --headingSize: 2.5rem;
  }

These examples illustrate the benefits of using CSS Custom Properties but we're only scratching the surface here. If you'd like to go more indepth with CSS custom properties & CSS design systems in general, checkout this course by Scott Tolinski. Thanks for reading, check back soon for more web dev content!

© 2021 Jacob Stordahl | built with Steel