Module 4

CSS Custom Properties

What are CSS Custom Properties?

CSS Custom Properties were first proposed as an addition to the w3.org CSS Standards in 2013, and finalized in 2015 (CSS Custom Properties for Cascading Variables). It wasn't until 2017 that many browsers supported most of the standard, as described in this article by Smashing Magazine It’s Time To Start Using CSS Custom Properties.

CSS Custom Properties are like variables in CSS Preprocessors, but they have some advantages:

  • They are native to most browsers, so you can skip the preprocessor step
  • Custom Properties obey cascade rules, so you can override values based on selector specificity
  • Custom Properties can be read and changed dynamically with JavaScript

But there is still important use cases for CSS Preprocessors:

  • compatiblity with all browsers, since they are processed into standard CSS
  • ability to use functions
  • conditional logic (if statements)
  • looping

How do you use CSS Custom Properties?

A custom property is defined by preceding the variable name with 2 hypens, such as

  --aside-color: #303030;
  --aside-padding: 1em 0em 2em 0em;

A valid variable name is defined by w3.org as:

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit.

The scope of the CSS variable depends on where it is defined. There is a special scope called :root, which is the scope of the entire document. But they can also be declared in any CSS selector block, and use the same inheritance and cascade rules for other rules in that block. Custom property declarations can also occur in Media Queries, and those will be in effect when the media feature is true.

In this example, 4 variables are defined within the scope of the selector .lead-item.

  .lead-item {
    --header-color: white;
    --header-background-color: goldenrod;
    --content-color: sienna;
    --content-background-color: bisque;
  }

To use your variables, you have to use the special function var(). The var function has 2 parameters: the name of the variable, and an optional default (fallback) value. For example:

  .item .header {
    color: var(--header-color, darkgoldenrod);
    background-color: var(--header-background-color, lightyellow);
    padding: 1em 1em;
  }
  .item .content {
    color: var(--content-color, black);
    background-color: var(--content-background-color, antiquewhite);
    padding: 2em 1em;
  }
  .item h3 {
    margin: 0 0;
  }

Now here is how the scope (cascade rules) of the variable declaration works. If an element is a descendant of .item and .header, and is also a descendant of .lead-item, the variable will be substituted. But if it was not a descendant of .lead-item, the default value is used, because there is no custom property definition in scope.

Let's see this in action. Given this HTML:

  <div class="lead-item item">
    <header class="header">
      <h3>Lead Item Header</h3>
    </header>
    <div class="content">
      Main Article Text
    </div>
  </div>
  <hr>
  <div class="item">
    <header class="header">
      <h3>Another Item Header</h3>
    </header>
    <div class="content">
      Another Item Text
    </div>
  </div> 

The custom properties defined in .lead-item will be used in the first item, and the default values in the second:

Lead Item Header

Main Article Text

Another Item Header

Another Item Text

Example of the :root pseudo-selector

Now here is an example of declaring a custom property inside the :root pseudo-selector. Let's define the variable --h6-color in 2 selectors, and apply them to all <h6>'s.

  :root {
    --h6-color: darkgreen;
  }
  .introduction {
    --h6-color: purple;
  }
  h6 {
    color: var(--h6-color);
    padding: 0.25em 1em;
  }

In the HTML, all <h6>'s will be darkgreen , unless they are in a selector of .introduction, because :root applies to the entire document.

  <h6>Ordinary h6</h6>
  <h6 class="introduction">Introduction h6</h6> 

And here is the result:

Ordinary h6
Introduction h6