Skip to content

Conversation

@RobinMalfait
Copy link
Member

This PR fixes an infinite loop when you use a @variant inside of a @custom-variant, where the @variant used is another @custom-variant.

The issue stems from the fact that a @custom-variant can use a @slot that we have to replace with the proper AST nodes. However in this setup, the AST nodes will include a @slot node as well, which causes us to replace the @slot again, and so on, causing an infinite loop.

@custom-variant a {
  @slot;
}

@custom-variant b {
  @variant a {
    @slot;
  }
}

The solution here is to replace the @slot nodes and then skip walking the nodes that were just inserted. This does mean that we end up with a @slot node in the final AST but that's not a real issue because that will get replaced later when handling the next @custom-variant.

Test plan

  1. Existing tests still pass
  2. Added a regression test to ensure that the infinite loop does not happen anymore
  3. Added additional tests to ensure that the behavior is correct

Thanks @wongjn for your initial debugging help and providing a test case as well!

Fixes: #19618

With a bit more info by introducing an additional selector
When handling `@custom-variants` we substitute the `@slot` with the
incoming nodes. The issue is that the `nodes` might contain a `@slot` as
well, but we don't want to keep replacing it with itself. The `@slot`
that gets introduced is coming from _another_ `@custom-variant` so once
we replace a `@slot`, we can stop traversing the new nodes.
@RobinMalfait RobinMalfait requested a review from a team as a code owner February 2, 2026 12:15
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Walkthrough

This pull request addresses an infinite loop issue occurring when using @variant inside @custom-variant. The changes include documenting the bug in the changelog, adding three new test cases to validate complex interactions between @custom-variant and @variant directives including nested variants and slot handling, and modifying the variant walking logic in variants.ts to use WalkAction.ReplaceSkip(nodes) instead of WalkAction.Replace(nodes) when handling @slot during AST traversal.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main fix: preventing an infinite loop when using @variant inside @custom-variant that points to another @custom-variant, which matches the core change in variants.ts.
Description check ✅ Passed The description is directly related to the changeset, explaining the infinite loop issue, the root cause involving @slot replacement, the solution implemented, and the test plan.
Linked Issues check ✅ Passed The pull request successfully addresses the primary objective from issue #19618: fixing the infinite loop/crash when using @variant inside @custom-variant by changing @slot handling from Replace to ReplaceSkip in variants.ts, with comprehensive test coverage added.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the infinite loop issue: CHANGELOG.md documents the fix, variants.ts implements the core fix, and index.test.ts adds comprehensive test coverage for the specific issue and related scenarios.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@RobinMalfait RobinMalfait merged commit df96ea5 into main Feb 2, 2026
7 checks passed
@RobinMalfait RobinMalfait deleted the fix/issue-19618 branch February 2, 2026 13:42
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.

Variant inside custom variant makes the app crash

2 participants