Chrome is currently prototyping CSS Functions, which is very exciting!
~
⚠️ This post is about an upcoming CSS feature. You can’t use it … yet.
This feature is currently being prototyped in Chrome Canary and can be tested in Chrome Canary with the Experimental Web Platform Features flag enabled.
~
Chrome is currently prototyping CSS Functions from the css-mixins-1
specification.
A custom function can be thought of as an advanced custom property, which instead of being substituted by a single fixed value, computes its substitution value based on function parameters and the value of custom properties at the point it’s invoked.
Here’s a very simple example (taken from the spec) that should give you an idea of what a custom function looks like:
@function --negate(--value) {
result: calc(-1 * var(--value));
}
You invoke the function like by calling directly – no need for var()
or the like – and can use it anywhere a value is accepted. For example:
:root {
padding: --negate(1px); /* = -1px */
}
The implementation in Chrome Canary is currently incomplete and there is no shipping date set, but you can already try out the WIP-implementation by enabling the Experimental Web Platform Features flag.
~
I’m very excited about this upcoming feature, as it will open up a lot of possibilities, way more impactful than that --negate
example from the spec.
For example: a limitation of the CSS light-dark()
function is that it only works with <color>
values. Thanks to Custom Functions you can write your own --light-dark()
that works with any value.
@function --light-dark(--light, --dark) {
result: var(--light);
@media (prefers-color-scheme: dark) {
result: var(--dark);
}
}
If you are visiting the site in the dark mode, the --dark
value will be returned. Otherwise the --light
value gets returned.
For example, you can use this --light-dark()
to have different font-weight
– something Robin suggests on doing:
:root {
color-scheme: light dark;
font-family: "Literata", serif;
color: light-dark(#333, #e4e4e4);
background-color: light-dark(aliceblue, #333);
font-weight: --light-dark(500, 300);
}
Here’s a live demo that uses that code (and which also changes the font-size
and some border
-related properties along with it):
💁♂️ Note that the custom --light-dark()
is not an exact copy of light-dark()
. The built-in light-dark()
can return different values based on the used color-scheme
of an element whereas --light-dark()
relies on the global light/dark preference. Being able to respond to used values is not covered by @function
itself. For that we’d also need the CSS if()
function, which is also in the making (but not really ready for testing, yet). Container queries inside the function also work – to take the parent context into account – but that is currently not prototyped yet.
UPDATE 2025.03.18: In https://www.bram.us/2025/02/18/css-at-function-and-css-if/ I combine @function
and if()
to create a custom --light-dark()
that behaves exactly like the built-in light-dark()
.
🏎️ Also note that for <color>
values I am still using the built-in light-dark()
here, but could have used my custom one as well. I have a hunch that the built-in version performs faster, but that would need a proper benchmark.
~
In this post I’ve limited myself to only a basic-ish example, without covering too much details. Not mentioned for example are default values for the function parameters and how to specify the types for any of those. For that, you can dig into the spec. Note that the spec still has a lot of moving parts, as the spec gets influenced by findings from the prototype Chrome is building.
To follow along with Chrome’s progress of the prototype, subscribe to crbug/325504770 by hitting the star next to its title.
~
Spread the word
Feel free to reshare one of the following posts on social media to help spread the word:
~
🔥 Like what you see? Want to stay in the loop? Here's how: