Description
Abstract
The matches()
function is proposed to enhance the conditional application of styles in CSS by checking if the current element matches a specified selector. This function complements the recently accepted new if()
function, providing a more semantic and readable way to handle conditional logic within CSS properties.
Motivation
The current method of conditionally applying styles in CSS often involves the use of arguably complex techniques like emulating boolean values using integers (0
and 1
) or toggles using empty values (The --var: ; hack...
), both of which can become complex and less readable.
The introduction of if()
allows for clearer conditionality using boolean logic. However, the need to conditionally style elements based on their selector remains verbose and needs and is only really doable via addtl rules/selectors in the cascade, even if it would be simpler inline in some cases. The matches()
function aims to address this by allowing a direct approach to conditionally setting styles based on selector matches.
This would also bring greater parity with the related JS Element.prototype.matches
method.
Syntax
matches(selector)
- selector: A CSS selector to check against the current element.
Usage
The matches()
function can be used inside any CSS property value expression where conditional styles are necessary based on whether the current element matches a specified selector.
Example 1: Basic example
.item {
--symbol: if(matches(.favorite) ? "🧡" : "🩶");
&::before {
content: var(--symbol);
}
}
In this example, the --symbol
custom property is conditionally set to different emoji characters based on whether the .item
element has the .favorite
class.
Comparison to existing methods
Example 2.1: Without if()
and matches()
.item {
--symbol: "🩶";
&::before {
content: var(--symbol);
}
&.favorite {
--symbol: "🧡";
}
}
This traditional approach involves redundant code as it sets the same property (--symbol
) in multiple places.
Example 2.2: Using if()
and matches()
.item {
--symbol: if(matches(.favorite) ? "🧡" : "🩶");
&::before {
content: var(--symbol);
}
}
This approach reduces redundancy and centralizes the logic for setting the --symbol
property.
A case for boolean expressions outside if()
I've opened a separate proposal (#10593) focused on supporting a new "boolean" custom property type, which could hypothetically be valid and type-safe even outside of if()
. If that's accepted, an example using a math-based switch like the one below could be simplified to use boolean logic.
Example 3.1: Using a math-based switch
This example uses a math-based switch to zero out values when a condition is not met.
label {
--selected: 0;
&:has(:checked) { --selected: 1; }
grid-template-columns: auto calc(var(--selected) * 20px);
svg { opacity: var(--selected); }
}
Example 3.2: Using matches()
and if()
This moves the conditions inline, but they're a bit complex, so it's not ideal that we have to repeat the conditions in both values.
label {
grid-template-columns: auto if(matches(:has(:checked)) ? 20px : 0);
svg { opacity: if(matches(:has(:checked)) ? 1 : 0); }
}
Example 3.3: Using matches()
and if()
Using reusable boolean values can offload some of this complexity.
label {
--selected: matches(:has(:checked));
grid-template-columns: auto if(var(--selected) ? 20px : 0);
svg { opacity: if(var(--selected) ? 1 : 0); }
}