CSS custom properties

CSS custom properties are often referred to as “CSS variables,” although that is not necessarily their only use case.

You can find the full specification here: http://dev.w3.org/csswg/css-variables/.

CSS custom properties allow us to store information in our stylesheets that can then be utilized in that stylesheet or read/written to with JavaScript.

Let’s start with a simple use case; storing a font-family name that we can then reference more simply later in the stylesheet:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">:root {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  --MainFont: 'Helvetica Neue', Helvetica, Arial, sans-serif;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Here, we are using the :root pseudo-class to store the custom property in the document root (although you can store them inside any rule you like).

The :root pseudo-class always references the top-most parent element in a document structure. In an HTML document, this would always be the HTML tag, but for an SVG document (we will look at SVG , Using SVGs for Resolution Independence), it would reference a different element.

A custom property always begins with two dashes, then the custom name, and then, at the end of the property, just like a normal CSS property, we terminate the property with a colon.

We can reference that value elsewhere in our stylesheet with the var() notation, like so:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">.Title {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  font-family: var(--MainFont);</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

You could obviously store as many custom properties as you need in this manner. The main benefit of this approach is that you can change the value inside the variable and every rule that makes use of the variable gets the new value without having to amend them directly.

Let me show you a very simple example of using CSS custom properties with JavaScript. You can find this in example_06-11. We will make a page with the poem “If,” by Rudyard Kipling. At the bottom is a simple light/dark mode toggle button. All the button will do is toggle the value of two CSS custom properties: –background and –foreground.

Here is our CSS:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">body {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    background-color: var(--background);</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    color: var(--foreground);</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

For the curious, here is the snippet of JavaScript we are using. Essentially, it just says that if the foreground variable is #eee (nearly white), then make it #333 (a dark grey); otherwise, make it #eee. And, if the background variable is #333, make it #eee; otherwise, make it #333:

</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">var root = document.documentElement;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">var btn = document.getElementById("colorToggle");</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">btn.addEventListener("click", e => {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    root.style.setProperty("--background", getComputedStyle(root).getPropertyValue('--background') === "#333" ? "#eee" : "#333");</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    root.style.setProperty("--foreground", getComputedStyle(root).getPropertyValue('--foreground') === "#eee" ? "#333" : "#eee");</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">})</p>

<pre>

Custom properties also behave like other properties in terms of specificity. For example, we set our custom properties at the root level, but if they are redeclared closer to the element in question, the more specific value will override the first. Consider this CSS:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">:root {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  --backgroundColor: red;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">header {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  --backgroundColor: goldenrod;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

The header and any elements within it that make use of the –backgroundColor custom property will have a “goldenrod” background color, whereas other elements will have a red background.

Learn and Earn More-   Grid with CSS

Setting a fallback value

There may be a situation where you want to protect against a custom property being unavailable. You can do that by providing a fallback value. The syntax is straightforward: simply provide your fallback value after the custom property name and a comma. For example, suppose I wanted to use the –backgroundColor custom property, but default to a dark gray color if that variable was unavailable. I could write it like this:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">.my-Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">     background-color: var(--backgroundColor, #555);</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

To be safe, it makes a lot of sense to get into the habit of supplying fallback values whenever you use custom properties.

 

env() environment variables

In addition to custom properties we might make for ourselves, there are occasionally properties that can be read in from the environment we are operating in. These are called “environment variables.” The only solid example of these I’m currently aware of are the safe-area-inset properties applicable to notched mobile phones. Made famous by the iPhone X, the “safe-areas” relate to the sections at the top and bottom of the screen where areas of UI or physical buttons impinge upon the viewing area.

Environment variables are defined with the env() function. Values are passed into this function just like you would any other CSS function. For example, if we wanted to add padding to the top of our element equal to the height of the environments “safe area inset top,” we could do this:

padding-top: env(safe-area-inset-top);

If the browser has that value available and it understands the env() function, it will apply the value; otherwise, it will skip over it to the next declaration.

You can read the current specification for environment variables here: https://drafts.csswg.org/css-env-1/#env-function.

There are a few extra peculiarities to the iPhone notched screens. For a full walkthrough of dealing with the iPhone notches, read https://benfrain.com/css-environment-variables-iphonex/.

I’m continually finding new use cases for CSS custom properties. The fact that they can update “on the fly” means they are useful in all manner of scenarios. The fact that they can be easily read and written to from script elevates them far beyond what was possible with the kind of variables made popular by CSS preprocessors like Sass, Less, and so on.

Learn and Earn More-   Fluid layout, Flexbox, and responsive images in css 2

Custom properties, along with many of the features we have looked at and shall look at, cannot be guaranteed to work in every browser you might need to support. Thankfully, CSS has an elegant syntax for encapsulating code that pertains to the latest features. Let’s look at that next.

Using @supports to fork CSS

When you’re building out a responsive web design, attempting to provide a single design that works everywhere, on every device, it’s a simple fact that you’ll frequently encounter situations when features or techniques are not supported on certain devices. In these instances, you’ll likely want to create a fork in your CSS. If the browser supports a feature, provide one chunk of code; if it doesn’t, it gets different code.

This is the kind of situation that gets handled by if/else or switch statements in JavaScript. In CSS, we use the @supports at-rule.

Feature queries

The native solution to forking code in CSS is to use “feature queries,” part of the CSS Conditional Rules Module Level 3 (http://www.w3.org/TR/css3-conditional/). Support was introduced in iOS and Safari 9, Firefox 22, Edge 12, and Chrome 28.

Feature queries follow a similar syntax to media queries. Consider this:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports (flashing-sausages: lincolnshire) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  body {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    sausage-sound: sizzling;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    sausage-color: slighty-burnt;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    background-color: brown;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Here, the styles will only get applied if the browser supports the flashing-sausages property in combination with the Lincolnshire value. I’m quite confident that no browser is ever going to support a flashing-sausages: Lincolnshire property/value combination (and if they do, I want full credit), so none of the styles inside the @supports block will be applied.

Let’s consider a more practical example. How about we use Grid when browsers support it and fall back to another layout technique when they don’t? Consider this example:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports (display: grid) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  .Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    display: inline-grid;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports not (display: grid) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  .Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    display: inline-flex;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Here, we are defining one block of code for when the browser supports a feature, and another lot for when it doesn’t. This pattern is fine if the browser supports @supports (yes, I realize that is confusing), but if it doesn’t, it won’t apply any of those styles.

Learn and Earn More-   CSS MEDIA QUERIES - What can media queries test for?

If you want to cover off devices that don’t support @supports, you’re better off writing your default declarations first and then your @supports-specific one after. That way, the prior rule will be overruled if support for @supports exists, and the @supports block will be ignored if the browser doesn’t support it. Our prior example could, therefore, be reworked to:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">.Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  display: inline-flex;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports (display: grid) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  .Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    display: inline-grid;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Sorry, that explanation was tough—hopefully you got through it!

Combining conditionals

You can also combine conditionals. Let’s suppose we only wanted to apply some rules if both Flexbox and pointer: coarse were supported (in case you missed it, we covered the pointer interaction media feature back in Media Queries – Supporting Differing Viewports). Here is what that might look like:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports ((display: flex) and (pointer: coarse)) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  .Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    display: inline-flex;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Here, we have used the and keyword but we could use or as well as, or instead of it. For example, if we were happy to apply styles if those two prior property/value combinations were supported, or 3D transforms were supported:

</pre>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">@supports ((display: flex) and (pointer: coarse)) or</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  (transform: translate3d(0, 0, 0)) {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  .Item {</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">    display: inline-flex;</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">  }</p>
<p style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; white-space: normal;">}</p>

<pre>

Note the extra set of parentheses that separates the display and pointer conditional from the transform conditional.

And that really is all there is to feature queries. As with media queries, put your “default” styles first, and then your enhancement inside a feature query @supports at-rule. Remember, you can combine queries and also offer differing possibilities in which the enclosed code can apply.

 



WhatsApp chat