Re: [csswg-drafts] [selectors] Declarative custom `:state()` states (#12502)

Overall, it looks like many people are thrown by the word "state" in `:state()`, since it's currently mainly used for transient states. The way I saw it, `:state()` is just the API we ended up on for doing custom pseudo-classes (instead of `:--foo`). It was called "state" because often pseudo-classes represent transient state, but they don't have to, `:state(button)` is a perfectly valid selector.

I was thinking that `:state()` would be paving the cowpaths, but if so many people are thrown by it, then it's clearly not a good option. Perhaps we need to revisit actual custom pseudo-classes with a similar syntax as what is proposed here. However, we do need WCs to have a composable way for hooking into them. We want authors to be able to create e.g.:

```css
@pseudo-class button {
 matches: button, .button;
}
```

And then we want a way for a WC to say "add me to whatever `:--button` matches. Perhaps something like [`@light { ... }`](https://github.com/w3c/csswg-drafts/issues/10941) could address that too, allowing the WC to just do:

```css
@pseudo-class button {
 matches: foo-button;
}
```

Alternatively, something like `:alias()` could work, with `:alias(foo)` automatically targeting `:state(foo)` (being an alias itself, which is delightfully meta 😅)

@seanmay:
> Would there be any means of nesting queries (media / container / style) inside of these declarations, or would it make resolving the tree too unwieldy?

Oooh that's a very interesting idea. I was thinking of it as a simpler at-rule that only takes descriptors and was thinking we could use a similar design for custom media queries (which is also a goal), but I quite like this as a way to feed two birds with one scone! This is brilliant actually, it also mostly solves #6247 and a bunch of other use cases. I’m not sure we have precedent of at-rules that can nest conditional rules but not regular rules, @tabatkins would know.

> [@seanmay](https://github.com/seanmay) I use the _existing_ [CSS Custom State](https://developer.mozilla.org/en-US/docs/Web/CSS/:state) to create CSS APIs for custom elements that are akin to `:checked`, `:open`, `:popover-open`, `:focus`, `:hover`, `:active`, `:playing`, `:invalid`, `:required`, `:disabled` for built-in elements. In any of those cases, having something within CSS override them would be inappropriate and it would be for custom elements as well, which is why I’m suggesting that _setting the state_ might be outside of the purview of CSS. That’s not to say one can’t use CSS Custom State to create selectors for custom elements that’d be like the built-in `:any-link` or `:heading`, but to me it seems a little misplaced to do so using something for _state._
> Imagine I have an existing custom element that has shadow styles for `:state(open)` which it sets when `ariaExpanded` has been set for its `ElementInternals`. As the custom element author, I’m deciding when that state gets added. If document authors can set states, then they could trigger those styles when they shouldn’t apply.

I don't understand what issue you're describing. Is the worry that authors may create a 

```css
@state focus {
 /* ... */
}
```

that would override your component's? 
If so, note that (as @seanmay explained) in the proposal these states **cascade**, so no CSS can ever *remove* a state set via either another CSS rule, or JS, it can only add states. So yes, in case of naming conflicts, _other_ elements may also have that state (or your WC, if it matches any author-defined selector *as well*), but I can't think of a case where that would be an issue?


@Loirooriol 
> Presumably this would need some restrictions to avoid cycles:
> 
> @state heading {
>  matches: :state(heading);
> }

Correct. Perhaps a L1 could entirely forbid `:state()` from these selectors, then we could introduce proper cycle detection at a later stage.

-- 
GitHub Notification of comment by LeaVerou
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12502#issuecomment-3094834080 using your GitHub account


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

Received on Sunday, 20 July 2025 22:33:31 UTC