Skip to content

fix(styled-jsx): handle top-level & nesting selector correctly in scopeSelector - #95

Merged
sapphi-red merged 4 commits into
mainfrom
copilot/investigate-issue-94
Jun 29, 2026
Merged

fix(styled-jsx): handle top-level & nesting selector correctly in scopeSelector#95
sapphi-red merged 4 commits into
mainfrom
copilot/investigate-issue-94

Conversation

Copilot AI commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the handling of top-level & (CSS nesting) in scopeSelector. Previously the function had no awareness of :scope pseudo-classes (which lightningcss emits when it flattens a top-level &), so patterns like &:global(.foo) and & .bar produced incorrect or missing scoped selectors.

Changes

src/css.ts

  • Add isScopePseudo helper to detect :scope pseudo-classes emitted by lightningcss when expanding a top-level &
  • Fix &:global() handling: strip any leading :scope (from &) before unwrapping :global(), so &:global(.theme-dark) correctly emits .theme-dark instead of leaving .jsx-HASH:scope
  • Simplify non-global compound handling: instead of a two-step strip-then-insert, replace :scope in-place with the scope class. This means & is substituted directly rather than removed and re-added at a different position
    • &.active.jsx-HASH.active (was .active.jsx-HASH)
    • &.active:hover.jsx-HASH.active:hover (was .active.jsx-HASH:hover)
    • All other & patterns are unaffected: &:hover, &::before, &:not(...), bare &, & .child
  • Update JSDoc comments to describe the new behaviour

Test fixtures

Behaviour notes

The CSS semantics are equivalent in all cases — both .active.jsx-HASH and .jsx-HASH.active require both classes to be present on the element. The change is purely in which position the scope class occupies in the compound selector.

Copilot AI changed the title fix(styled-jsx): replace lone :scope with scope class to fix & :global(...) selectors fix(styled-jsx): & :global(...) selectors compile to dead .jsx-HASH:scope selector Jun 24, 2026
Copilot AI requested a review from sapphi-red June 24, 2026 10:44
Copilot AI changed the title fix(styled-jsx): & :global(...) selectors compile to dead .jsx-HASH:scope selector fix(styled-jsx): strip :scope from all & compound selectors, not just lone :scope Jun 24, 2026
Copilot AI changed the title fix(styled-jsx): strip :scope from all & compound selectors, not just lone :scope Replace two-step &/:scope removal+insertion with single in-place replacement Jun 24, 2026
@sapphi-red sapphi-red changed the title Replace two-step &/:scope removal+insertion with single in-place replacement fix(styled-jsx): handle top-level & nesting selector correctly in scopeSelector Jun 24, 2026
if (globalIdx >= 0) {
// Emit any components before :global() in this compound
// Emit any components before :global() in this compound, skipping any
// leading :scope (originated from `&` — e.g. `&:global(.foo)` → `:scope:global(.foo)`)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's arguable that &:global(.foo) should be .jsx-hash.foo, but I think either is fine as &:global(.foo) is contradictory.

@sapphi-red
sapphi-red marked this pull request as ready for review June 24, 2026 12:32
@pkg-pr-new

pkg-pr-new Bot commented Jun 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

@rolldown/plugin-babel

pnpm add https://pkg.pr.new/@rolldown/plugin-babel@95 -D
npm i https://pkg.pr.new/@rolldown/plugin-babel@95 -D
yarn add https://pkg.pr.new/@rolldown/plugin-babel@95.tgz -D

@rolldown/plugin-emotion

pnpm add https://pkg.pr.new/@rolldown/plugin-emotion@95 -D
npm i https://pkg.pr.new/@rolldown/plugin-emotion@95 -D
yarn add https://pkg.pr.new/@rolldown/plugin-emotion@95.tgz -D

@rolldown/plugin-jsx-remove-attributes

pnpm add https://pkg.pr.new/@rolldown/plugin-jsx-remove-attributes@95 -D
npm i https://pkg.pr.new/@rolldown/plugin-jsx-remove-attributes@95 -D
yarn add https://pkg.pr.new/@rolldown/plugin-jsx-remove-attributes@95.tgz -D

oxc-unshadowed-visitor

pnpm add https://pkg.pr.new/oxc-unshadowed-visitor@95 -D
npm i https://pkg.pr.new/oxc-unshadowed-visitor@95 -D
yarn add https://pkg.pr.new/oxc-unshadowed-visitor@95.tgz -D

@rolldown/pluginutils

pnpm add https://pkg.pr.new/@rolldown/pluginutils@95 -D
npm i https://pkg.pr.new/@rolldown/pluginutils@95 -D
yarn add https://pkg.pr.new/@rolldown/pluginutils@95.tgz -D

@rolldown/plugin-styled-jsx

pnpm add https://pkg.pr.new/@rolldown/plugin-styled-jsx@95 -D
npm i https://pkg.pr.new/@rolldown/plugin-styled-jsx@95 -D
yarn add https://pkg.pr.new/@rolldown/plugin-styled-jsx@95.tgz -D

@rolldown/plugin-transform-imports

pnpm add https://pkg.pr.new/@rolldown/plugin-transform-imports@95 -D
npm i https://pkg.pr.new/@rolldown/plugin-transform-imports@95 -D
yarn add https://pkg.pr.new/@rolldown/plugin-transform-imports@95.tgz -D

commit: b9150ba

@Kegulf Kegulf left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested the canary and it resolves issue #94 perfectly! 😊
The code also looks sane to me (without knowing the codebase extensively).

@sapphi-red
sapphi-red merged commit 2f9a3e6 into main Jun 29, 2026
20 of 21 checks passed
@sapphi-red
sapphi-red deleted the copilot/investigate-issue-94 branch June 29, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants