Skip to content

[css-values-5] Proposal for a new matches() CSS Function #10594

Open
@brandonmcconnell

Description

@brandonmcconnell

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); }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions