Skip to content

fix: scope :extend inside @container like @media - #4542

Open
dchaudhari7177 wants to merge 1 commit into
less:masterfrom
dchaudhari7177:fix/4540-extend-in-container
Open

dchaudhari7177 wants to merge 1 commit into
less:masterfrom
dchaudhari7177:fix/4540-extend-in-container

Conversation

@dchaudhari7177

@dchaudhari7177 dchaudhari7177 commented Sep 16, 2026

Copy link
Copy Markdown

What:

Fixes #4540. An :extend inside @container now only matches selectors inside that same block, the rule @media already follows.

Container got its own node type when container queries were added (#3811). The visitor dispatches on visit${node.type}, and neither ExtendFinderVisitor nor ProcessExtendsVisitor had a visitContainer. Extends inside @container were therefore collected into the enclosing scope and matched selectors anywhere. This adds visitContainer / visitContainerOut to both visitors, delegating to the Media handlers.

With the snippet from the issue:

.a { color: red; }

@container container-name {
  .b { &:extend(.a); }
}
/* before */
.a,
.b {
  color: red;
}
@container container-name {
}

/* after, same as the equivalent @media block produces */
.a {
  color: red;
}
@container container-name {
}

To be clear about what this doesn't do: extend never copies declarations into a block. The output the issue asks for (.b { color: red; } inside the container) isn't something :extend does for @media either. The empty @container container-name {} left behind is a separate, pre-existing difference: an empty @media block is removed by ToCSSVisitor.visitMedia, which has no Container counterpart either. It happens without any extend too, so I left it out of this PR.

Why:

Before this change, extends leaked across container boundaries both ways. A selector extended inside one @container got added to rulesets at the top level and inside unrelated @container blocks.

Tests:

New packages/test-data/tests-unit/extend-container fixture, modelled on extend-media. It covers:

  • two sibling containers, each extending .ext1 all from inside;
  • a top-level .all:extend(.ext1 all), which should still reach into both containers;
  • the case from the issue.

I used sibling containers rather than nesting them, so the fixture doesn't pin how nested named container queries are merged.

Running this fixture without the fix shows the leak. .card-narrow and .card-wide get added to top-level .ext1 .ext2 and to each other's container, and .b joins .a at the root:

node test/index.js   # packages/less
with the fix:     All Passed, 282 run
without the fix:  1 Failed (tests-unit/extend-container), 281 passed

eslint is clean on extend-visitor.js. I ran the Node test suite only, not the headless-browser tests.

Checklist:

  • Documentation N/A
  • Added/updated unit tests
  • Code complete

Summary by CodeRabbit

  • Bug Fixes

    • Corrected :extend behavior inside CSS container queries.
    • Prevented selectors extended within a container query from matching rules outside that container’s scope.
  • Tests

    • Added coverage for named and unnamed container queries, including nested extensions and container-specific selector matching.

Container has its own node type, and neither extend visitor had a
visitContainer, so extends inside @container were collected into the
enclosing scope. They matched selectors outside the block, and anything
extended from inside one container leaked into the root and into other
containers. Give Container the same extend scoping as Media.

Fixes less#4540
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 091eb88b-6445-4f64-ac7b-5de0dd516137

📥 Commits

Reviewing files that changed from the base of the PR and between 7133316 and fb3613c.

📒 Files selected for processing (3)
  • packages/less/lib/less/visitors/extend-visitor.js
  • packages/test-data/tests-unit/extend-container/extend-container.css
  • packages/test-data/tests-unit/extend-container/extend-container.less

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The extend visitors now scope :extend directives inside @container blocks. New Less and CSS fixtures cover named, unnamed, nested, and top-level container extensions.

Changes

Container-scoped extends

Layer / File(s) Summary
Container visitor scoping
packages/less/lib/less/visitors/extend-visitor.js
ExtendFinderVisitor and ProcessExtendsVisitor delegate @container entry and exit handling to the existing media visitor methods.
Container extend coverage
packages/test-data/tests-unit/extend-container/extend-container.less, packages/test-data/tests-unit/extend-container/extend-container.css
Test fixtures cover extends in unnamed and named container queries, selectors defined outside a container, and a top-level extension.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to fb361

The change scopes container-local extends without introducing an identified current-head regression. It is ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #4540 requires :extend inside @container to remain local to that container. extend-visitor.js adds visitContainer and visitContainerOut to ExtendFinderVisitor and `ProcessExtendsVisi…
Out of Scope Changes check ✅ Passed The changed source file updates extend scoping for @container. The added test fixtures exercise container-local, sibling-container, and root-scope extend behavior. These changes directly support iss…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: scoping :extend inside @container blocks to match @media behavior.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no actionable correctness, security, or repository-rule issues identified.

Summary

This PR scopes :extend processing within @container blocks using the established @media behavior.

  • Adds container-specific entry and exit handlers to both extend-processing visitor phases.
  • Prevents extends declared inside a container from affecting root rules or sibling containers.
  • Adds an automatically discovered fixture covering local, sibling, named-container, and inherited top-level behavior.

Reviews (1) · Last reviewed commit: "fix: scope :extend inside @container lik..."

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend within container query

1 participant