Lightly Poking at the CSS if() Function in ChromeĀ 137
New Post has been published on https://thedigitalinsider.com/lightly-poking-at-the-css-if-function-in-chrome-137/
Lightly Poking at the CSS if() Function in ChromeĀ 137
Weāve known it for a few weeks now, but the CSS if() function officially shipped in Chrome 137 version. Itās really fast development for a feature that the CSSWG resolved to add less than a year ago. We can typically expect this sort of thing ā especially one that is unlike anything we currently have in CSS ā to develop over a number of years before we can get our dirty hands on it. But here we are!
Iām not here to debate whetherĀ if()Ā in CSS should exist, nor do I want to answer whether CSS is a programming language;Ā Chris already did thatĀ and definitely explained how exhausting that fun little argument can be.
What I am here to do is poke atĀ if()Ā in these early days of support and explore what we know about it today at a pretty high level to get a feel for its syntax. We poke a little harder at it in another post where weāll look at a more heady real-world example.
Yes, itās already here!
Conditional statements exist everywhere in CSS. From at-rules to the parsing and matching of every statement to the DOM, CSS has always had conditionals. And, as Lea Verou put it,Ā every selector is essentially a conditional!Ā What we havenāt had, however, is a way to style an element against multiple conditions in one line, and then have it return a resultĀ conditionally.
The if()Ā function is a more advanced level of conditionals, where you can manipulate and have all your conditional statements assigned to a single property.
.element color: if(style(--theme: dark): oklch(52% 0.18 140); else: oklch(65% 0.05 220));
How does if() work?
Well before Chrome implemented the feature, back in 2021 when it was first proposed, the early syntax was like this:
<if()> = if( <container-query>, [<declaration-value>]1, 2 )
Now weāre looking at this instead:
<if()> = if( [<if-statement>: <result>]*; <if-statement>: <result> ;? )
Whereā¦
The firstĀ <if-statement>Ā represents conditions inside eitherĀ style(),Ā media(), orĀ supports()Ā wrapper functions. This allows us to write multiple if statements, as many as we may desire. Yes, you read that right. As many as we want!
The finalĀ <if-statement>Ā condition (else) is the default value when all other if statements fail.
Thatās the āeasyā way to read the syntax. This is whatās in the spec:
<if()> = if( [ <if-branch> ; ]* <if-branch> ;? ) <if-branch> = <if-condition> : <declaration-value>? <if-condition> = <boolean-expr[ <if-test> ]> | else <if-test> = supports( [ <ident> : <declaration-value> ] | <supports-condition> ) media( <media-feature> | <media-condition> ) | style( <style-query> )
A little wordy, right? So, letās look at an example to wrap our heads around it. Say we want to change an elementās padding depending on a given active color scheme. We would set an if() statement with aĀ style()Ā function inside, and that would compare a given value with something like a custom variable to output a result. All this talk sounds so complicated, so letās jump into code:
.element padding: if(style(--theme: dark): 2rem; else: 3rem);
The example above sets the padding toĀ 2remā¦Ā ifĀ theĀ --themeĀ variable is set to dark. If not, it defaults toĀ 3rem. I know, not exactly the sort of thing you might actually use the function for, but itās merely to illustrate the basic idea.
Make the syntax clean!
One thing I noticed, though, is that things can get convoluted veryĀ veryĀ fast. Imagine you have three if() statements like this:
:root --height: 12.5rem; --width: 4rem; --weight: 2rem; .element height: if( style(--height: 3rem): 14.5rem; style(--width: 7rem): 10rem; style(--weight: 100rem): 2rem; else: var(--height) );
Weāre only working with three statements and, Iāll be honest, it makes my eyes hurt with complexity. So, Iām anticipatingĀ if()Ā style patterns to be developed soon or prettier versions to adopt a formatting style for this.
For example, if I were to break things out to be more readable, I would likely do something like this:
:root --height: 12.5rem; --width: 4rem; --weight: 2rem; /* This is much cleaner, don't you think? */ .element height: if( style(--height: 3rem): 14.5rem; style(--width: 7rem): 10rem; style(--weight: 100rem): 2rem; else: var(--height) );
Much better, right? Now, you can definitely understand what is going on at a glance. Thatās just me, though. Maybe you have different ideas⦠and if you do, Iād love to see them in the comments.
Hereās a quick demo showing multiple conditionals in CSS for this animated ball to work. The width of the ball changes based on some custom variable values set. Gentle reminder that this is only supported in Chrome 137+ at the time Iām writing this:
The supports() and media() statements
Think ofĀ supports()Ā the same way you would use theĀ @supportsĀ at-rule. In fact, they work about the same, at least conceptually:
/* formal syntax for @supports */ @supports <supports-condition> <rule-list> /* formal syntax for supports() */ supports( [ <ident> : <declaration-value> ] | <supports-condition> )
The only difference here is that supports() returns a value instead of matching a block of code. But, how does this work inĀ real code?
TheĀ <ident>: <declaration-value>Ā you see here is, in this case, theĀ property name: property valueĀ e.g.Ā display: flex.
Letās say you want to check for support for theĀ backdrop-filterĀ property, particularly theĀ blur()Ā function. Typically, you can do this withĀ @supports:
/* Fallback in case the browser doesn't support backdrop-filter */ .card backdrop-filter: unset; background-color: oklch(20% 50% 40% / 0.8); @supports (backdrop-filter: blur(10px)) .card backdrop-filter: blur(10px); background-color: oklch(20% 50% 40% / 0.8);
But, with CSSĀ if(), we can also do this:
.card backdrop-filter: if( supports(backdrop-filter: blur(10px)): blur(10px); else: unset );
Note: Think of unset here as a possible fallback for graceful degradation.
That looks awesome, right? Multiple conditions can be checked as well forĀ supports()Ā and any of the supported functions. For example:
.card backdrop-filter: if( supports(backdrop-filter: blur(10px)): blur(10px); supports(backdrop-filter: invert(50%)): invert(50%); supports(backdrop-filter: hue-rotate(230deg)): hue-rotate(230deg);; else: unset );
Now, take a look at theĀ @media at-rule.Ā You can compare and check for a bunch of stuff, but Iād like to keep it simple and check for whether or not a screen size is a certain width and apply styles based on that:
h1 font-size: 2rem; @media (min-width: 768px) h1 font-size: 2.5rem; @media (min-width: 1200px) h1 font-size: 3rem;
TheĀ media()Ā wrapper works almost the same way as its at-rule counterpart. Note its syntax from the spec:
/* formal syntax for @media */ @media <media-query-list> <rule-list> /* formal syntax for media() */ media( <media-feature> | <media-condition> )
Notice how at theĀ end of the day, the formal syntax (<media-query>) is the same as the syntax for theĀ media()Ā function. And instead of returning a block of code inĀ @media, youād have something like this in the CSS inlineĀ if():
h1 font-size: if( media(width >= 1200px): 3rem; media(width >= 768px): 2.5rem; else: 2rem );
Again, these are early days
As of the time of this writing, only theĀ latest update of Chrome supportsĀ if()). Iām guessing other browsers will follow suit once usage and interest come in. I have no idea when that will happen. Until then, I think itās fun to experiment with this stuff, just as others have been doing:
Experimenting with early features is how we help CSS evolve. If youāre trying things out, consider adding your feedback to the CSSWG and Chromium. The more use cases, the better, and that will certain help make future implementations better as well.
Now that we have a high-level feel for the if()syntax, weāll poke a little harder at the function in another article where we put it up against a real-world use case.














