[csswg-drafts] [css-selectors-5] @selector function for creating custom selectors (#14180)

paceaux has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-selectors-5] @selector function for creating custom selectors ==
I would like to suggest that there exist a feature within selectors that offers some sort of parity to what already exists within CSS values: the ability to create and interpolate a string value. 

## Problem: Specificity bloat leads to file bloat

Suppose a very real in-production selector:

```
#foo-navigation[breakpoint=desktop] .hidden-at-desktop[class][class][class],
foo-navigation[breakpoint=desktop] .hidden-at-desktop[class][class][class] {
  display:none
}
```

This is one of a series of rulesets that are unfortunately high due to specificity bloat that lives outside the domain of the stylesheet. 

But a side-effect of raising the specificity to accomplish the goal is that the selectors must requisitely become longer (lest we resort to !important or `id` selectors). 

### Solution 1: Nesting (already exists)

Nesting could be one solution to reducing redundancy, but it comes at the cost of incidental specificity sideeffects, since the most -specific selector in the list would elevate the specificity of all selectors (due to the fact that nesting uses the same mechanics as `:is()` under the hood)

So someone could write

```css
#foo-navigation[breakpoint=desktop], foo-navigation[breakpoint=desktop]) {

  .hidden-at-desktop[class][class][class] {

  }
}
```
which is functionally equivalent to `:is(#foo-navigation[breakpoint=desktop], foo-navigation[breakpoint=desktop]) .hidden-at-desktop[class][class][class] `. 


But now `foo-navigation` is given the specificity of an id, which could have un intended consequences. Not only that, the duplication is eliminated with the child elements; so the code doesn't immediately express what problem is solved with nesting. 

### Solution 2: `:where()`

It might be tempting to instead eliminate redundancy by using `:where()` since neither it nor its arguments contribute specificity:


```css
:where(#foo-navigation[breakpoint=desktop], foo-navigation[breakpoint=desktop]) .hidden-at-desktop[class][class][class]  {
display: none;
}

```
Unfortunately this creates the exact problem that now all specificity is _gone_, and this may also not be a desired outcome. 

### Solution 3: CSS Selector Function (proposed)
One solution could eliminate redundancy, improve cognitive load, and not create specificity side-effects:

```css
@selector --<name>() {
   <valid selector string>
}
```

A real-world use-case could be as follows:

```css

@selector --foo-nav-desk() {
  #foo-navigation[breakpoint=desktop]
}

:--foo-nav-desk() .hidden-at-desktop[class][class][class] {

}
```

In this case, the custom selector function has been set to not provide _any_ specificity. It will behave like a CSS custom property and handle the returned argument like a string and the value will be rendered at request time. 

This solution would also mimic the behavior of the CSS function syntax. 

### Problem: Interpolating CSS selectors

Currently, it is not possible to interpolate CSS selectors. 

In other words, this selector cannot be made less redundant or reusable with any selector-generating syntax

```CSS 
#foo-navigation[breakpoint=desktop] .hidden-at-desktop[class][class][class],
foo-navigation[breakpoint=desktop] .hidden-at-desktop[class][class][class] {
  display:none
}
```

*Solution: Allow string arguments passed into a selector function*

I would propose this syntax:

```css
@selector --<name>(<variable>) {
   <interpolated string>
}
```

essentially, the arguments, however many, would be inserted as-is whereever the `var` keyword is used (just like with properties). 


In this case, an application could be like so:
```CSS
@selector --foo-nav(--desktop) {
  #foo-navigation[breakpoint=var(--desktop)]
}



:--foo-nav(desktop) .hidden-at-desktop[class][class][class] {
  display: none;
}

:--foo-nav(mobile) .hidden-at-desktop[class][class][class] {
  display: none;
}
```

## TL;DR
Proposing a selector function would allow us to reduce duplicativity as developers without specificity side-effects. smaller stylesheets without side-effects makes for happier end-users. 


Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/14180 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Tuesday, 14 July 2026 17:32:31 UTC