The Hidden Trick of Style Queries and if()
With modern CSS, we have two new ways to express conditions: inline if() and style queries. Their syntax is as follows:
/* inline if() */
.box {
background: if(style(--n: 3): red; else: green);
}
/* style queries */
.box {
background: green;
@container style(--n: 3) {
border-color: red;
}
}
The common part between them is the style() part, which is defined to behave the same with both features. It contains the condition that evaluates to either true or false. Most of the online demos will show you the above version, but there is another valid syntax using =
/* inline if() */
.box {
background: if(style(--n = 3): red; else: green);
}
/* style queries */
.box {
background: green;
@container style(--n = 3) {
border-color: red;
}
}
You may intuitively think that both are equivalent, but they are not!
When using style(--n: 3) we are relying on the following syntax style(<style-feature-name>: <style-feature-value>) (called <style-feature-plain>). In this case, the condition evaluates to true if the computed value of the given property matches the given value (which is also computed).
Let's take the following example:
.box {
--n: calc(6/2);
background: if(style(--n: 3): red; else: green);
}
What is the computed value of --n? 3? No, it's calc(6/2). The browser doesn't perform the calculation in this context, hence the computed value is calc(6/2). That value doesn't match 3 and the condition is false (we get a green color). I know, it's a bit strange, but you can see it as a string matching.
The following will work fine:
.box {
--n: calc(6/2);
background: if(style(--n: calc(6/2)): red; else: green);
}
The following as well:
.box {
--n: calc(6/2);
background: if(style(--n: var(--n)): red; else: green);
}
Same as the following:
.box {
--s: new;
background: if(style(--s: new): red; else: green);
}
When using style(--n = 3) we are relying on the following syntax style(<style-range-value> <mf-comparison> <style-range-value>) (called a <style-range>). Then we do the following steps to evaluate the condition:
- If
<style-range-value>is a custom property, it needs to be substituted as if the custom property was wrapped inside avar(). - Parse
<style-range-value>to<number>,<percentage>,<length>,<angle>,<time>,<frequency>or<resolution>. If this cannot be done, evaluate to false. - If each
<style-range-value>from the range have the same type, compute each and evaluate the comparison.
Let's apply this to our example by replacing : with =
.box {
--n: calc(6/2);
background: if(style(--n = 3): red; else: green);
}
We do the substitution to get style(calc(6/2) = 3). We do the parsing, and we can see both are <integer> (here we perform the calculation). They have the same type and are equal so the condition is true!
The = notation give us more freedom on how to write the condition. All the below are valid:
.box {
background: if(style(3 = --n): red; else: green);
background: if(style(var(--n) = 3): red; else: green);
background: if(style(3 = var(--n)): red; else: green);
background: if(style(calc(6/2) = var(--n)): red; else: green);
}
You can reverse the order, use var(), include a calculation, etc. When using the : notation we must always start with the custom property and we cannot use it with var(). All the below are invalid:
.box {
background: if(style(var(--n): 3): red; else: green);
background: if(style(3: --n): red; else: green);
background: if(style(3: var(--n)): red; else: green);
}
Let's try the example using the new value with the = notation
.box {
--s: new;
background: if(style(--s = new): red; else: green);
}
The substitution gives style(new = new). The parsing will .. fail! new is none of the listed types, so the condition is false.
Here is a demo showing all the conditions so can see the difference between both notation:
See the Pen The hidden trick of if() by Temani Afif (@t_afif) on CodePen.
And the same using style queries
See the Pen The hidden trick of style queries() by Temani Afif (@t_afif) on CodePen.
As a conclusion, here is what you need to remember:
-
The use of
style(--variable: value)will perform an exact match of both computed values. This one is suitable for string-like matching (ex:style(--stock: low)). -
style(--variable = value)will perform a numerical comparison between two values that should have the same type (from the types I listed previously). This one is suitable for math stuff (ex:style(--n = 5))
The first method has another interesting behavior when combined with @property. Learn more about this case here: How to correctly use if() in CSS.
More CSS Tips
- Graph Theory using Modern CSS A CSS-only implementation of a shortest path algorithm.
- A Better Way to Express Percentage Height A modern alternative to the classic percentage height that always work.