Skip to content

fix(css): scope pseudo-only selectors, nesting, and deep functions - #82

Merged
cmseguin merged 1 commit into
mainfrom
fix/css-scoping-gaps
Oct 25, 2026
Merged

cmseguin merged 1 commit into
mainfrom
fix/css-scoping-gaps

Conversation

@cmseguin

Copy link
Copy Markdown
Owner

Summary

Follow-up to #81 (the CSS feature-support matrix). This PR fixes the gaps documented there and moves every it.failing case into supported assertions.

Fixes

  • Pseudo-only selectors now receive the scope attribute:
    • :hover → :hover[hash]
    • :is() / :where() / :not() / :has() → …[hash]
    • ::before / ::slotted() → [hash]::before (prepended, since an attribute cannot follow a pseudo-element)
  • Leading pseudo-only compounds in complex selectors are scoped (:hover > .b, :is(.a) .b, :not(.a) ~ .b, :where(.a) .b).
  • CSS nesting is scoped, both explicit and implicit, including inside at-rules:
    • explicit & rules are found by recursing into rule blocks
    • implicit nesting is parsed as Raw by css-tree, so each raw nested rule is re-parsed, scoped, and written back
  • Column combinator ||: css-tree serializes the prelude as Raw; it is now split on top-level ||, each part is scoped, and the parts are re-joined.
  • :deep() / :v-deep() function form is supported. A single argument is spliced in place; selector-list arguments become :is(...) with the same matching semantics.

Behaviour notes

  • :root and :scope remain global. The root element never receives the scope attribute, so :root[hash] would only turn working rules into dead ones. Descendant subjects are still scoped (:root .b → :root .b[hash]).
  • & itself does not receive a second attribute — the parent rule is already scoped.

Tests

src/css/css-features.spec.ts now contains 126 supported cases, up from 92 supported + 19 it.failing gaps.

Verification:

npm test                      # 219 passed
npm run build                 # clean
npm run lint                  # clean
npm run test:e2e              # vite 5/6/7/8, dev + build all pass

🤖 Generated with Claude Code

- Scope pseudo-only compounds (`:hover`, `:is()`, `:where()`, `:not()`,
  `:has()`, `::before`, `::slotted()`), including leading compounds.
- Recurse into rule blocks and re-parse `Raw` nested rules so explicit
  (`&`) and implicit CSS nesting is scoped, including inside at-rules.
- Scope the `||` column combinator by splitting and scoping the Raw
  prelude parts.
- Support the `:deep()` / `:v-deep()` function form.
- Keep `:root` / `:scope` global, since the root element never receives
  the scope attribute.
- Move all previously documented gaps into the supported matrix.

Co-Authored-By: Claude <noreply@anthropic.com>
@cmseguin
cmseguin merged commit 2fccfdd into main Oct 25, 2026
2 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 25, 2026
## [1.2.7](v1.2.6...v1.2.7) (2026-10-25)

### Bug Fixes

* **css:** scope pseudo-only selectors, nesting, and deep functions ([#82](#82)) ([2fccfdd](2fccfdd))
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant