fix(v4): output static components inside @layer components (issue #15045)#20131
fix(v4): output static components inside @layer components (issue #15045)#20131saitejabandaru-in wants to merge 4 commits into
Conversation
…abs#15045) Aligns v4 compatibility layer behavior with v3 by placing styles registered via `addComponents` inside the static `@layer components` layer rather than `@layer utilities`. This correctly preserves layer specificity so that component libraries like daisyUI can be overridden by standard utility classes.
…nents Updates the snapshot assertion to verify that component styles are properly wrapped inside `@layer components`.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Caution Review failedPull request was closed or merged during review WalkthroughThis PR changes the addComponents() method in the Tailwind CSS plugin API to emit component styles wrapped in an 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
Confidence Score: 2/5Not safe to merge — the matchComponents rewrite produces invalid CSS and breaks an existing test. The addComponents fix is correct and achieves the stated goal. However, the matchComponents rewrite returns a @layer at-rule as children of the outer style rule, which is not valid CSS and silently does nothing in browsers. Any plugin using matchComponents would see its components output in the wrong layer. The corresponding snapshot test was also not updated, so CI will fail. packages/tailwindcss/src/compat/plugin-api.ts — the matchComponents compileFn wrapping logic; packages/tailwindcss/src/compat/plugin-api.test.ts — the matchComponents snapshot needs to be updated alongside a correct implementation. Reviews (2): Last reviewed commit: "Update addComponents test to expect styl..." | Re-trigger Greptile |
…abs#15045) Aligns v4 compatibility layer behavior with v3 by placing styles registered via `addComponents` inside the static `@layer components` layer rather than `@layer utilities`. This correctly preserves layer specificity so that component libraries like daisyUI can be overridden by standard utility classes.
…nents Updates the snapshot assertion to verify that component styles are properly wrapped inside `@layer components`.
|
Hey! Appreciate the PR, but we're not sure yet if we even want this. While this is a breaking change, it was an intentional breaking change to simplify the system. The PR currently is incomplete, but since we're not sure if we even want this, I'm going to close this for now. Thanks! |
| if (!candidate.modifier) { | ||
| modifier = null | ||
| } else if (modifiers === 'any' || candidate.modifier.kind === 'arbitrary') { | ||
| modifier = candidate.modifier.value | ||
| } else if (modifiers && Object.hasOwn(modifiers, candidate.modifier.value)) { | ||
| modifier = modifiers[candidate.modifier.value] | ||
| } else if (isColor && !Number.isNaN(Number(candidate.modifier.value))) { | ||
| modifier = `${candidate.modifier.value}%` |
There was a problem hiding this comment.
@layer block nested inside selector rule — invalid CSS output
The compileFn here returns [rule] where rule = atRule('@layer', 'components', ast). In the compilation pipeline (compileAstNodes in compile.ts), the array returned by compileFn becomes the nodes of an outer StyleRule: { kind: 'rule', selector: '.prose', nodes: [atRule('@layer', 'components', …)] }. The serialised CSS is therefore .prose { @layer components { --container-size: normal; } } — an @layer at-rule nested inside a style rule, which is invalid CSS. Browsers silently ignore it, so declarations end up in the wrong cascade layer. The fix for addComponents (pushing a root-level @layer components block directly to ast) cannot be reused here because functional components are generated on demand per candidate, but wrapping the inner compileFn return value is the wrong place to apply it.
| @@ -4242,13 +4244,15 @@ describe('matchComponents()', () => { | |||
| ), | |||
| ).toMatchInlineSnapshot(` | |||
| " | |||
| .prose { | |||
| --container-size: normal; | |||
| } | |||
| @layer components { | |||
| .prose { | |||
| --container-size: normal; | |||
| } | |||
|
|
|||
| @media (hover: hover) { | |||
| .hover\\:prose-lg:hover { | |||
| --container-size: lg; | |||
| @media (hover: hover) { | |||
| .hover\\:prose-lg:hover { | |||
| --container-size: lg; | |||
| } | |||
| } | |||
| } | |||
| " | |||
There was a problem hiding this comment.
matchComponents test snapshot not updated — will fail
The matchComponents implementation was rewritten to return [atRule('@layer', 'components', ast)] from each compileFn, so the generated CSS for every candidate is now wrapped in @layer components. The existing snapshot still reflects the original output (no @layer wrapping), so the test suite will fail as-is. The snapshot and test description ('is an alias for matchUtilities') both need to be updated to match the intended new output once the underlying implementation bug is resolved.
Description
This pull request resolves issue #15045 by correctly placing styles registered via the JavaScript plugin compatibility API
addComponentsinside the static@layer componentscascade layer rather than@layer utilitiesin Tailwind CSS v4.Background
In Tailwind CSS v3, plugins using
addComponents(such asdaisyuior@tailwindcss/forms) registered custom components whose styles were output inside@tailwind components(predefined@layer components). This allowed developers to cleanly override component-level styles with utility classes (which are ordered later in@layer utilities) in their HTML.In v4, the compatibility layer defined
addComponentsas a simple alias foraddUtilities:This causes component styles to be generated inside the dynamic
@layer utilitieslayer. Because they share the same cascade layer as standard utility classes, they are ordered based on property counts and property-overlap heuristics rather than cascade layering. This breaks specificity, making it extremely difficult or impossible to override component classes (like.btn,.card) with utility classes (likebg-red-100) in markup without resorting to!importanteverywhere.Changes
addComponentsimplementation inplugin-api.tsto parse component styles usingobjectToAstand wrap them inside an explicit@layer componentsat-rule, pushing it directly to the rootastarray (similar to howaddBaseworks for global baseline styles):plugin-api.test.tsto verify thataddComponentsoutputs compiled rules cleanly wrapped inside@layer componentsin the generated CSS, rather than placing them straight into the flat utilities listing.This is a critical backward compatibility fix that enables a seamless transition to Tailwind CSS v4 for standard component libraries (such as daisyUI, flowbite, tailwindcss-forms, etc.) that rely on cascade layering specificity.