CSS Custom Properties and !important

💁‍♂️ In this post, I’m taking my own spin on a post originally by Stefan Judis. Doing so, it helped me to better grasp it myself and I was also able to simplify the examples. I hope you will find it helpful too.

What happens when you set !important on a Custom Property? Stefan Judis dug into the subject and posted about it. It all starts with this interesting paragraph from the specification:

Custom properties can contain a trailing !important, but this is automatically removed from the property’s value by the CSS parser […]

As an example, take this snippet:

div {
  --color: red !important;
  background: var(--color);
}

div {
  background: yellow;
}

Under normal circumstances, red !important would win from yellow. But since !important gets removed (because it is used in a Custom Property) the snippet basically boils down to this:

div {
  background: red;
}

div {
  background: yellow;
}

As both selectors live in the same Origin and have the same Specificity, it’s “Order of Appearance” that determines the winner: the last declaration wins, i.e. background: yellow;

~

This doesn’t mean that !important is entirely discarded in combination with Custom Properties though. The modifier does get used, but only when trying to determine the actual value for that Custom Property if you have multiple competing declarations.

div {
  --color: red !important;
  background: var(--color);
}

.class {
  --color: blue;
}

Although the .class selector is more specific than the div selector, the resulting value for --color will be red. This is because the !important made it win.

~

When the resulting value gets used in the background property, the !important part will, as per spec, be removed again.

This means that when combining both snippets …

div {
  --color: red !important;
  background: var(--color);
}

.class {
  --color: blue;
}

div {
  background: yellow;
}

… the result will again be yellow because we end up in the same situation as in the first example:

  • For the Custom Properties, red !important will win from blue
  • When used, (the flattened) background: red; will lose from background: yellow; because both selectors live in the same Origin, have the same Specificity, and background: yellow; is declared later.

Here’s a CodePen demo for you to play with:

See the Pen
Custom Properties vs !important
by Bramus (@bramus)
on CodePen.

Open Props — Supercharged CSS Variables

Adam Argyle released a new project named Open Props. Similar to the aforementioned Pollen, it’s a library that offers nothing but CSS Custom Properties.

Open source CSS custom properties to help accelerate adaptive and consistent design. Available from a CDN or NPM, as CSS or Javascript.

Once included, you can use the Custom Properties the library provides: colors, margins, paddings, gradients, shadows, font properties, aspect-ratios, … even some basic animations or a conditional border-radius — they’re all there, exposed as Custom Properties!

@import "https://unpkg.com/open-props";

.card {
  border-radius: var(--radius-2);
  padding: var(--size-fluid-3);
  box-shadow: var(--shadow-2);

  &:hover {
    box-shadow: var(--shadow-3);
  }

  @media (--motionOK) {
    animation: var(--animation-fade-in);
  }
}

💡 That’s CSS Nesting you see there, soon a built-in feature.

I like this very much. Very, very much. Unlike Other Projects™, Open Props nails the “We need Design Tokens” problem by simply making them available in CSS (or via JS if that’s your thing). It doesn’t require you to set up a build pipeline, nor does it enforce you to discard CSS for a re-implementation in another language. You can keep on using what you already know: CSS.

Open Props →

~

Projects like this make me even more convinced of my statement that Custom Properties are the best thing that happened to CSS over the past 20 years.

On Using Custom Properties

On Twitter, John Allsopp recently shared that Flexbox is the greatest thing that happed to CSS. I quickly chimed in with my personal favourite. To me the single greatest addition to CSS over the past 20 years is Custom Properties. They’ve opened up a ton of possibilities, and make us CSS Authors way more productive. The way they are specced — as properties and not simply variables — is pure genius and gives them their true power.

~

💁‍♂️ I heard spec author Tab Atkins talk about them for the first time at CSS Day 2013, but it wasn’t until Lea Verou‘s talk CSS Variables: var(--subtitle); in 2016 that it really clicked for me.

~

Over at CSS-Tricks, Chris Coyier writes about the Different Degrees of Custom Property Usage: how do you provide defaults, and how far do you go when splitting things up into several parts?

Regarding the fallback part Chris sticks to what we already know: a 2nd argument to var():

el {
  border: .1em solid var(--color, hsl(200deg 15% 73%));
}

The interesting part is on splitting things up: do you provide one single hsl color, or do you split out all the individual parts?

el {
  --color: hsl(200deg 15% 73%);
  border: .1em solid var(--color);
}
el {
  --color-h: 200deg;
  --color-s: 15%;
  --color-l: 73%;
  --color-hsl: var(--color-h) var(--color-s) var(--color-l);
  --color: hsl(var(--color-hsl));
  border: .1em solid var(--color);
}

Just like Chris I’m more leaning towards using --color here because although these Custom Properties are very nice, they do come at a cost: complexity. As I’ve tweeted back in February:

Thankfully there’s DevTools’s “Computed Styles” pane to somewhat help me out there 🙂

Additionally, settings these individual props on :root and then overriding --color-h for some specific element won’t work, because --color will already have been computed before it is passed down the inheritance tree. This is what Chris calls The Big Gotcha With Custom Properties.

~

Over at her blog, Lea Verou also wrote about Custom Properties and how to provide defaults. Interestingly, Lea opts to no directly use the passed in Custom Property, but resorts to something she calls “pseudo-private custom properties” that get an extra _ prefix.

My preferred solution is what I call pseudo-private custom properties. You use a different property internally than the one you expose, which is set to the one you expose plus the fallback.

Like so:

el {
  --_color: var(--color, hsl(200deg 15% 73%));
  border: .1em solid var(--_color);
}

As an extra she also taps into Houdini’s powerful @property to register a default value. Unfortunately Houdini is only supported in Chromium at the time of writing.

🎩 Houdini, ain't that a magician?

Houdini is a set of low-level APIs that exposes parts of the CSS engine, giving developers the power to extend CSS by hooking into the styling and layout process of a browser’s rendering engine. Houdini is a group of APIs that give developers direct access to the CSS Object Model (CSSOM), enabling developers to write code the browser can parse as CSS, thereby creating new CSS features without waiting for them to be implemented natively in browsers.

It really is magic, hence it's name Houdini. I'd recommend this slidedeck and this video to get you started

~

👀 Don’t try this at home

Because the contents of Custom Properties are not parsed until they are used (using var()), you can store anything in them. That means you could abuse them to create faux Single-line Comments in CSS:

~

Dark mode in 5 minutes, with inverted lightness variables

Lea Verou shows a method to implement dark mode, not by swapping entire colors, but by simply changing their lightness

The basic idea is to use custom properties for the lightness of colors instead of the entire color. Then, in dark mode, you override these variables with 100% - lightness. This generally produces light colors for dark colors, medium colors for medium colors, and dark colors for light colors, and still allows you to define colors inline, instead of forcing you to use a variable for every single color.

For best results she also taps into LCH colors.

LCH is a much better color space for this technique, because its lightness actually means something, not just across different lightnesses of the same color, but across different hues and chromas.

Dark mode in 5 minutes, with inverted lightness variables →

Style Pseudo-elements with Javascript Using Custom Properties

Over at CSS { In Real Life }, author Michelle Barker has detailed a clever way to style pseudo-elements (such as ::before and ::after) through JavaScript.

In Javascript we have a few ways of selecting elements, but we can’t directly target pseudo-elements. […] Luckily, CSS custom properties can help.

👉 If you set a custom property on the element that “owns” the pseudo-element the pseudo-element itself can pick it up, thus enabling a way to style it.

Quick Tip: Style Pseudo-elements with Javascript Using Custom Properties →

The Power of Composition with CSS Custom Properties

Maxime Heckel, while revamping the theme on his blog:

I stumbled upon this pattern of composing CSS partial values for my HSLA colors, assign them to variables. Thanks to this, I was able to build my fairly complex theme and a full color palette that made sense for my websites by only using CSS. No ThemeProvider. No weird CSS-in-JS tools or hacks. Just plain old CSS. Moreover, it integrated perfectly with my existing React/CSS-in-JS setup and helped me declutter quite a few lines of style spaghetti code.

Ever since I learnt that you can store anything in CSS Custom Properties this is something that I too have been doing more and more. And thanks to the use of space-separated functional color notations this has become even more easy to do.

I also like the term “Partial Values” he uses there. Describes ‘m perfectly!

The Power of Composition with CSS Variables →

Make the page count of a 3D book visible using CSS Custom Properties

Michael Scharnagl:

I am currently building a book section for this site and thought it would be cool to show the books in 3D and also to make it visible how many pages a book has. In this article I would like to show you how to use CSS custom properties to adapt the thickness of a 3D book showing how many pages the book has.

Using a --page-count custom property he defines the number of pages, which is then clamped between a minimum and a maximum (--page-count-range), and finally converted to a width in pixels (--page-width). The resulting <length> is then applied onto a 3D-rotated div that makes up the thickness of the book.

html {
  --page-count: 50;
  --page-count-range: clamp(1, calc(var(--page-count) / 50), 20);
  --page-width: calc(10px * var(--page-count-range));
}

.book__wrapper::before {
  width: var(--page-width);
  transform: translateX(calc(200px - var(--page-width) / 2 - 3px)) rotateY(90deg) translateX(calc(calc(var(--page-width)) / 2))
}

.book__wrapper::after {
  transform: translateZ(calc(var(--page-width) * -1));
}

Make the page count of a 3D book visible using CSS Custom Properties →
Demo →

How to Play and Pause CSS Animations with CSS Custom Properties

Mads Stoumann, writing for CSS-Tricks, starts off with a simple idea: set a Custom Property to either playing or paused to control animation-play-state.

[data-animation] {
    /* … */
    animation-play-state: var(--animps, running);
}

/* Use a checkbox to pause animations */
[data-animation-pause]:checked ~ [data-animation] {
  --animps: paused;
}

But one of the listed use cases is great: a pure CSS slideshow that has a play/pause button:

See the Pen
<details> Play/Pause Animations
by Mads Stoumann (@stoumann)
on CodePen.

Nice one!

More use cases in the full post, including the use of an IntersectionObserver to pause animations for elements that are offscreen.

How to Play and Pause CSS Animations with CSS Custom Properties →