DEV Community

Cover image for Very very custom properties
Tim Carry for Doctolib Engineering

Posted on

Very very custom properties

At the latest ParisWeb conference, Gaël Poupard gave a very interesting talk about CSS Custom properties. His talk was in french, but you’ll find here a recap in english of most of the clever uses of CSS properties he shared with us.

CSS custom properties are CSS properties that live in user-land; they are not part of any official specification and you can create as many as you want, with any silly name you’d like and put anything as their value. The only constraint is that they must start with a double dash (like --myLittlePony: pink). They work like any other CSS properties: they are inherited from parent elements, and can be manipulated from JavaScript.

They are often called CSS Variables, but this is not their official name. Technically they are only variables when they are read through the var() method. var(--myLittlePony) will evaluate to pink, so you can do background-color: var(--myLittlePony). var() also accepts an optional second argument as a fallback value so var(--myLittlePony, ‘black’) will evaluate to black if you didn’t define --myLittlePony.

Live variables

Conceptually, they are pretty similar to variables you might have in your SCSS/LESS preprocessor, but even more powerful they are part of the official spec and you can access and modify them live in the browser.

If you define --myLittlePony on your <body>, all your elements will be able to reference it. Your buttons can use background-color: var(--myLittlePony), your links can have color: var(--myLittlePony), etc. Actually, all your color palette could be defined as CSS custom properties instead of SCSS variables. And because they are live, you could access them with your dev tools or plain JavaScript, change their values, and have all your elements change colors instantly.

Using them as arguments

Arithmetic operations can also be applied using calc(). font-size: calc(12px * var(--fontScale)) for example will allow you to scale your font based on the --fontScale custom property. Set it to 1 for the default one, and to 0.8 or 1.2 to implement some kind of zooming/dezooming function. You could also chose to implement a different default --fontScale when based on the viewport size.

The same logic could be applied to margins, paddings or even colors through the use of the hsl() function and some custom --hue or --light properties. Applications are virtually limitless here.

Using them as feature switches

A specific application of the above principle is to use pseudo-boolean values (0 and 1) to switch some features on or off. For example, if we have animation: bounce calc(var(--enableAnimation) * 1s) infinite and --enableAnimation set to 1, it will play normally. But if we set --enableAnimation: 0 then the whole duration is evaluated to 0, disabling it.

This simple feature switch principle can be applied to disable shadows on low-end devices or animation in our screenshot testing pipeline.

Embedded fallback mechanism

The fallback value of the var() method can actually accept another call to var(), creating a deep nested system of fallback values.


Styles for input elements and form buttons are notoriously difficult to write and maintain as they have to deal with a plethora of conflicting states: hover, disabled, focus, valid, invalid, pending, clicked, primary, secondary, etc. Some of them are mutually exclusive while others can be stacked on top of each other.
Instead of writing more and more specific selectors to handle all the cases and overwriting rules, the nested fallback mechanism of the var() method can be used instead and might offer an easier alternative.

Bonus side effect

One last interesting thing to note is that Shadow DOM nodes inherits CSS custom properties from their parents. This means that you can pass CSS custom properties to a Shadow DOM node, and it can then use them to style its content. This would act as some sort of API between the Shadow DOM node and the outside world.


Top comments (0)