HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-09
1 <p>Sep 25, 2025</p>
1 <p>Sep 25, 2025</p>
2 <p>By the summer of 2025, Chrome shipped an experimental CSS feature - if(). At first glance, it sounds revolutionary: finally,<em>conditions in CSS</em>! But is it as powerful as it sounds? Let’s go through it step by step.</p>
2 <p>By the summer of 2025, Chrome shipped an experimental CSS feature - if(). At first glance, it sounds revolutionary: finally,<em>conditions in CSS</em>! But is it as powerful as it sounds? Let’s go through it step by step.</p>
3 <h2>What does if() actually do?</h2>
3 <h2>What does if() actually do?</h2>
4 <p>The if() function lets you assign property values based on conditions. It lives<em>right inside the value</em>- conceptually closer to a ternary operator than to a full-blown if statement in programming languages.</p>
4 <p>The if() function lets you assign property values based on conditions. It lives<em>right inside the value</em>- conceptually closer to a ternary operator than to a full-blown if statement in programming languages.</p>
5 <h2>First kind of condition - style queries</h2>
5 <h2>First kind of condition - style queries</h2>
6 <p>If the custom property --scheme equals dark, the background becomes gray:</p>
6 <p>If the custom property --scheme equals dark, the background becomes gray:</p>
7 body { --scheme: dark; background: if( style(--scheme: dark): gray; ); }<p>If the value is different, the function resolves to<em>Guaranteed-invalid value</em>(effectively the same as initial).</p>
7 body { --scheme: dark; background: if( style(--scheme: dark): gray; ); }<p>If the value is different, the function resolves to<em>Guaranteed-invalid value</em>(effectively the same as initial).</p>
8 body { --scheme: color; background: if( style(--scheme: dark): gray; ); }<p>The regular cascade just skips a broken declaration and keeps the previous value. if(), on the other hand, resets the property entirely - as if you’d explicitly written initial.</p>
8 body { --scheme: color; background: if( style(--scheme: dark): gray; ); }<p>The regular cascade just skips a broken declaration and keeps the previous value. if(), on the other hand, resets the property entirely - as if you’d explicitly written initial.</p>
9 <p>You can add another condition to handle a new value:</p>
9 <p>You can add another condition to handle a new value:</p>
10 body { --scheme: color; background: if( style(--scheme: dark): gray; style(--scheme: color): lightblue; ); }<p>And if nothing matches - there’s always an else branch, just like in “real” programming languages:</p>
10 body { --scheme: color; background: if( style(--scheme: dark): gray; style(--scheme: color): lightblue; ); }<p>And if nothing matches - there’s always an else branch, just like in “real” programming languages:</p>
11 body { --scheme: other; background: if( style(--scheme: dark): gray; style(--scheme: color): lightblue; else: tomato; ); }<h2>Second kind - media queries</h2>
11 body { --scheme: other; background: if( style(--scheme: dark): gray; style(--scheme: color): lightblue; else: tomato; ); }<h2>Second kind - media queries</h2>
12 <p>The function also works with media expressions:</p>
12 <p>The function also works with media expressions:</p>
13 h1 { font-size: if( media(width &gt; 700px): 72px; else: 42px; ); }<p>If the viewport is wider than 700 px - you get a big, bold heading. Otherwise, it stays smaller.</p>
13 h1 { font-size: if( media(width &gt; 700px): 72px; else: 42px; ); }<p>If the viewport is wider than 700 px - you get a big, bold heading. Otherwise, it stays smaller.</p>
14 <h2>Third kind - feature queries</h2>
14 <h2>Third kind - feature queries</h2>
15 <p>if() also supports<em>feature queries</em>. For example, you can set a red text color if the element() function isn’t supported:</p>
15 <p>if() also supports<em>feature queries</em>. For example, you can set a red text color if the element() function isn’t supported:</p>
16 h1 { color: if( not supports(element("#myid")): red; else: white; ); }<blockquote>If you only need to tweak a<strong>single</strong>value - if() looks neat and concise.</blockquote><p>But let’s be honest - everything we’ve seen so far could be done before, just differently.</p>
16 h1 { color: if( not supports(element("#myid")): red; else: white; ); }<blockquote>If you only need to tweak a<strong>single</strong>value - if() looks neat and concise.</blockquote><p>But let’s be honest - everything we’ve seen so far could be done before, just differently.</p>
17 <h2>Do we really need it?</h2>
17 <h2>Do we really need it?</h2>
18 <p>The same checks can be done using plain directives: @container, @media, and @supports. The behavior is identical:</p>
18 <p>The same checks can be done using plain directives: @container, @media, and @supports. The behavior is identical:</p>
19 :root { --scheme: other; } body { background: tomato; } @container style(--scheme: dark) { body { background: gray; } } @container style(--scheme: color) { body { background: lightblue; } } @media (width &gt; 700px) { h1 { font-size: 72px; } } @supports not (element("#myid")) { h1 { color: red; } } @supports (width: calc(1px * sibling-count())) { h1 { background-color: white; } }<p>In short: if() doesn’t introduce new logic. It’s pure syntactic sugar that brings existing query mechanisms down to the value level.</p>
19 :root { --scheme: other; } body { background: tomato; } @container style(--scheme: dark) { body { background: gray; } } @container style(--scheme: color) { body { background: lightblue; } } @media (width &gt; 700px) { h1 { font-size: 72px; } } @supports not (element("#myid")) { h1 { color: red; } } @supports (width: calc(1px * sibling-count())) { h1 { background-color: white; } }<p>In short: if() doesn’t introduce new logic. It’s pure syntactic sugar that brings existing query mechanisms down to the value level.</p>
20 <p>At first, if()-based code looks more compact. But that’s true only when you’re changing a single property - which, frankly, is pretty rare.</p>
20 <p>At first, if()-based code looks more compact. But that’s true only when you’re changing a single property - which, frankly, is pretty rare.</p>
21 <h2>Not quite what we imagined</h2>
21 <h2>Not quite what we imagined</h2>
22 <p>When you hear about a “real if in CSS”, you imagine comparing variables and triggering different sets of properties right inside a CSS rule:</p>
22 <p>When you hear about a “real if in CSS”, you imagine comparing variables and triggering different sets of properties right inside a CSS rule:</p>
23 .compare-numbers { --a: 5; --b: 10; if (var(--a) &gt; var(--b)) { width: 100px; height: 200px; } else { width: 250px; height: 150px; } }<p>The reality turned out much simpler: if() works<strong>only inside a single property value</strong>- no comparison operators, no multiple variables, no logic blocks.</p>
23 .compare-numbers { --a: 5; --b: 10; if (var(--a) &gt; var(--b)) { width: 100px; height: 200px; } else { width: 250px; height: 150px; } }<p>The reality turned out much simpler: if() works<strong>only inside a single property value</strong>- no comparison operators, no multiple variables, no logic blocks.</p>
24 <p>Basically, you can only check whether a variable equals a specific value. That’s it:</p>
24 <p>Basically, you can only check whether a variable equals a specific value. That’s it:</p>
25 .selector { --a: 5; width: if(style(--a: 5): 100px;); }<p>In JavaScript, that would look roughly like this:</p>
25 .selector { --a: 5; width: if(style(--a: 5): 100px;); }<p>In JavaScript, that would look roughly like this:</p>
26 let a = 5; let width = (a === 5) ? "100px" : "";<blockquote>“Imagine if JavaScript had<em>only</em>this kind of powerful conditional logic!”</blockquote><h2>A glimmer of hope: self queries</h2>
26 let a = 5; let width = (a === 5) ? "100px" : "";<blockquote>“Imagine if JavaScript had<em>only</em>this kind of powerful conditional logic!”</blockquote><h2>A glimmer of hope: self queries</h2>
27 <p>But not everything is disappointing.</p>
27 <p>But not everything is disappointing.</p>
28 <p>Here’s the bright side: if() can check not only parent variables (as container style queries do) but also variables defined on the<em>element itself</em>.</p>
28 <p>Here’s the bright side: if() can check not only parent variables (as container style queries do) but also variables defined on the<em>element itself</em>.</p>
29 <p>For example:</p>
29 <p>For example:</p>
30 h1 { font-size: if( style(--size: small): 32px; else: 72px; ); }<p>When --size isn’t defined, the else branch triggers (72 px). Add the variable directly to the element - and the condition fires:</p>
30 h1 { font-size: if( style(--size: small): 32px; else: 72px; ); }<p>When --size isn’t defined, the else branch triggers (72 px). Add the variable directly to the element - and the condition fires:</p>
31 h1 { --size: small; font-size: if( style(--size: small): 32px; else: 72px; ); }<p>This may look minor, but it’s important: traditional directives like @container only observe parents, while if() can look at<strong>itself</strong>.</p>
31 h1 { --size: small; font-size: if( style(--size: small): 32px; else: 72px; ); }<p>This may look minor, but it’s important: traditional directives like @container only observe parents, while if() can look at<strong>itself</strong>.</p>
32 <h2>Looking ahead</h2>
32 <h2>Looking ahead</h2>
33 <p>How could if() evolve?</p>
33 <p>How could if() evolve?</p>
34 <ul><li>Add comparison operators: &lt;, &gt;, &lt;=, &gt;=.</li>
34 <ul><li>Add comparison operators: &lt;, &gt;, &lt;=, &gt;=.</li>
35 <li>Support multiple variable comparisons.</li>
35 <li>Support multiple variable comparisons.</li>
36 <li>Work on the rule level, not just property values.</li>
36 <li>Work on the rule level, not just property values.</li>
37 </ul><p>Extra operators and variable comparisons are probably next on the roadmap. But lifting if() to the rule level? That’s going to be hard.</p>
37 </ul><p>Extra operators and variable comparisons are probably next on the roadmap. But lifting if() to the rule level? That’s going to be hard.</p>
38 <h2>Verdict: breakthrough or breakdown?</h2>
38 <h2>Verdict: breakthrough or breakdown?</h2>
39 <p>If if() stays exactly as it is now, it’s a bit of a failure. Put it on your linter’s blacklist and move on - because in its current form, it lets you write the same messy logic as in preprocessors, only natively.</p>
39 <p>If if() stays exactly as it is now, it’s a bit of a failure. Put it on your linter’s blacklist and move on - because in its current form, it lets you write the same messy logic as in preprocessors, only natively.</p>
40 <p>Still, there’s hope. If this experiment keeps evolving, it might become something bigger. The current release isn’t a failure or a triumph - more like a “pre-patch before the major update.”</p>
40 <p>Still, there’s hope. If this experiment keeps evolving, it might become something bigger. The current release isn’t a failure or a triumph - more like a “pre-patch before the major update.”</p>