[csswg-drafts] [css-selectors-5] Proposal: `:capture()` and `:group()` functional pseudo-classes (#13368)

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

== [css-selectors-5] Proposal: `:capture()` and `:group()` functional pseudo-classes ==
The purpose of this pair of functions is to allow capture and re-use of specific identifiers within a selector-list. After writing this, I also found previous, closely related discussion in [#10567](https://github.com/w3c/csswg-drafts/issues/10567)


`:capture()` is a functional pseudo-class taking a modified complex-selector as an argument. In place of any identifier, an asterisk may be used to capture values. `:capture()` restricts the matches of a selector in the same way the provided complex-selector argument would, ignoring any identifiers replaced with an asterisk. For example, `h1:capture(#*.cls)` would match the same elements as `h1.cls`.

`:group()` is a corresponding functional pseudo-class taking a list of integer number-tokens, and appears in the same selector-list as at least one `:capture()`. The numbers refer to `:capture()` classes in the order that they appear. `:group()` restricts the selector by the elements which the referent capture would match. For example, `table:capture(.*:hover) :group(1)` would match any descendants of a table with `:hover`, where the descendants also share a class with an element with `:hover`.

To expand on this example, say a document has exactly 5 classes: `cls1`, `cls2`, `cls3`, `cls4`, `cls5`.  
`table:capture(.*:hover) div:group(1)` could then be equivalently written:
```css
:root:has(.cls1:hover) table:hover div.cls1,
:root:has(.cls2:hover) table:hover div.cls2,
:root:has(.cls3:hover) table:hover div.cls3,
:root:has(.cls4:hover) table:hover div.cls4,
:root:has(.cls5:hover) table:hover div.cls5
```
note, even if all elements have a class, how this is different from:
```css
:root:has(.cls1:hover, .cls2:hover, .cls3:hover, .cls4:hover, .cls5:hover) table:hover div
```

---

The argument to `:capture()` may include multiple asterisks. In this case, the corresponding `:group()` must match at least one identifier from each asterisk. For example, say `:capture(*.*#id1)` captures `div`, `h1`, and `cls1`, `cls2`, `cls3`. The corresponding `:group(1)` will now restrict matches to one of `div.cls1`, `div.cls2`, `div.cls3`, `h1.cls1`, `h1.cls2`, and `h1.cls3`. Each application must also be unique, for example `:capture(*.*.*#id1)` with the same captures would give a `:group(1)` with restricts things to `div.cls1.cls2`, `div.cls1.cls3`, `div.cls2.cls3`, `h1.cls1.cls2`, `h1.cls1.cls3`, and `h1.cls2.cls3`.

There may be multiple numeric arguments to `:group()`. In this case, only one group must match. For example, say `:capture(*#id1):capture(.*#id2)` captures `div`, `h1`, `h2`, and `cls1`, `cls2`. The corresponding `:group(1, 2)` will now restrict matches to any of `div`, `h1`, `h2`, `.cls1`, or `.cls2`; this means `a.cls2` and `div.cls5` both match. `:group()` may come before the `:capture()` it references in a selector list, but the complex selector in `:capture()` may not contain any `:group()` calls.

If a `:capture()` appears inside a `:capture()`, the outer `:capture()` retains asterisks from the inner one. For example, `:capture(*:capture(.*#id1))` would result in a `:group(1)` which matches `:capture(*.*#id1)` and a `:group(2)` which matches `:capture(.*#id1)`.

`:capture()` may capture any identifier in the complex-selector.
 - namespace: `*|`
 - type: `*`
 - id: `#*`
 - class: `.*`
 - attribute: `[*]`
 - value: `[foo=*]`
 - attribute with a value: `[*="foo"]`, `[*|="foo" s]`, `[*$="foo" i]`, `[**="foo"]`, etc.
 - pseudo-classes (non-function ones): `:*`
 - pseudo-elements: `::*`

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


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

Received on Monday, 19 January 2026 09:13:32 UTC