Simplify @variant usage, allow compound and stacked variants#19996
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThe change updates 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Review rate limit: 3/5 reviews remaining, refill in 18 minutes and 10 seconds. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/tailwindcss/src/index.test.ts (1)
5519-5549: ⚡ Quick winMake whitespace coverage deterministic instead of random sampling.
Line 5520 and Line 5521 use random generation, which makes this regression test less reproducible and can miss edge combinations. Prefer an explicit matrix (
test.each) for stable coverage.♻️ Suggested deterministic rewrite
- it('should handle optional whitespace between `@variant` variants', async () => { - let before = ['', ' ', '\t'][(Math.random() * 3) | 0].repeat((Math.random() * 3) | 0) - let after = ['', ' ', '\t'][(Math.random() * 3) | 0].repeat((Math.random() * 3) | 0) - - await expect( - compileCss(css` - .btn { - background: black; - - `@variant` hover${before},${after}focus { - background: red; - } - } - `@tailwind` utilities; - `), - ).resolves.toMatchInlineSnapshot(` + it.each([ + ['', ''], + [' ', ''], + ['', ' '], + ['\t', ''], + ['', '\t'], + [' ', '\t'], + ['\t\t', ' '], + ])( + 'should handle optional whitespace between `@variant` variants (%j,%j)', + async (before, after) => { + await expect( + compileCss(css` + .btn { + background: black; + + `@variant` hover${before},${after}focus { + background: red; + } + } + `@tailwind` utilities; + `), + ).resolves.toMatchInlineSnapshot(` ".btn { background: `#000`; } `@media` (hover: hover) { .btn:hover { background: red; } } .btn:focus { background: red; }" - `) - }) + `) + }, + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/tailwindcss/src/index.test.ts` around lines 5519 - 5549, The test "should handle optional whitespace between `@variant` variants" currently uses Math.random to build `before` and `after` whitespace which makes the test nondeterministic; replace the randomized whitespace generation (the Math.random branches used to build `before` and `after`) with an explicit deterministic matrix using test.each (or describe.each) that iterates over all needed whitespace combos (e.g., '', ' ', '\t', multiple repeats) and runs the same assertion body calling compileCss, keeping the test title and using the same compileCss/expect/resolves.toMatchInlineSnapshot assertions so behavior is unchanged but coverage is deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/tailwindcss/src/index.test.ts`:
- Around line 5519-5549: The test "should handle optional whitespace between
`@variant` variants" currently uses Math.random to build `before` and `after`
whitespace which makes the test nondeterministic; replace the randomized
whitespace generation (the Math.random branches used to build `before` and
`after`) with an explicit deterministic matrix using test.each (or
describe.each) that iterates over all needed whitespace combos (e.g., '', ' ',
'\t', multiple repeats) and runs the same assertion body calling compileCss,
keeping the test title and using the same
compileCss/expect/resolves.toMatchInlineSnapshot assertions so behavior is
unchanged but coverage is deterministic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8eb24179-c115-4122-a595-9b50d2c2d417
📒 Files selected for processing (3)
packages/tailwindcss/src/index.test.tspackages/tailwindcss/src/source-maps/source-map.test.tspackages/tailwindcss/src/variants.ts
Instead of creating a new array for every map and reverse action, we only create the necessary ones as we go. Then we loop through the array in reverse instead of reversing and looping normally.
65be6bf to
c03ac88
Compare
This PR is an internal change only related to how we visualize source maps. As part of this PR (#19996) I added a test for source maps related to how `@variant` is processed. And while the result was correct, I had a hard time verifying if this was _actually_ correct. I did the mental mapping of comparing locations from the output to the input. <img width="495" height="495" alt="image" src="https://github.com/user-attachments/assets/2b1c12cd-43ee-461a-b93b-bafe6ce1cce5" /> With this PR, I want to make that more visual by actually printing the input source(s) and output file and highlight the necessary parts: <img width="1101" height="1085" alt="image" src="https://github.com/user-attachments/assets/14390026-f211-4cfc-8c6c-3293105f6403" /> I didn't want to get too clever here. But printing line numbers also helps in case we point to different spots on the same line: <img width="1200" height="981" alt="image" src="https://github.com/user-attachments/assets/e75f7622-eb51-49ee-928e-fdc8672d2c3f" /> And if we point to different files, then we visualize these as well: <img width="851" height="957" alt="image" src="https://github.com/user-attachments/assets/77e65801-b46b-4579-8659-d919a8750f60" /> If you combine this with snapshot tests, then it's very easy to verify that locations match up correctly. Each source map location is highlighted and references a symbol starting at `A`, `B`, etc. A change to the source maps _can_ result in a big diff, and even this PR introduces a big diff because of the preflight diffs. But at least you can see how things line up. ## Test plan 1. All tests still pass 2. Added tests for the source map visualizer that is only used in tests 3. No actual source code was touched
|
👏 |
…ants (#2481) Direct link: https://tailwindcss-com-git-feat-document-compound-9d8eca-tailwindlabs.vercel.app/docs/adding-custom-styles#using-variants This PR documents the new `@variant` behavior that was introduced in tailwindlabs/tailwindcss#19996 where you can stack variants (like you would in normal HTML): ```css .my-element { background: white; @variant hover:focus { background: black; } } ``` Which compiles to: ```css .my-element { background: white; &:hover { @media (hover: hover) { &:focus { background: black; } } } } ``` Similarly, this adds docs for compound variants, where you can define the same CSS in different situations: ```css .my-element { background: white; @variant hover, focus { background: black; } } ``` Which compiles to: ```css .my-element { background: white; &:hover { @media (hover: hover) { background: black; } } &:focus { background: black; } } ``` <img width="709" height="1251" alt="image" src="https://github.com/user-attachments/assets/46089844-06c9-4640-ab3b-a05d69bea8f9" />
This PR improves and simplifies the
@variantusage.When we originally added support for
@variant, we wanted to keep things simple, where we could only use a single variant at a time. The original PR did have a more complex system with all these features enabled, but we wanted to make sure that we only introduced the additional complexity when the community felt like it was needed.But of course we still wanted to make sure that you could do compound and stacked variants, it just required some additional code.
For compound variants, where you want to use variant
aand variantb, you could duplicate the rules as siblings:But with this PR, you can comma separate each variant to get the same effect:
You can think of this as-if we are expanding this syntax into the aforementioned syntax. In other words, we would do the duplication for you.
Additionally, you also want to be able to stack variants. For that you had to nest your
@variantrules:Not the end of the world, but it can get pretty nested if you want to use multiple variants. Luckily we already have a syntax for this in normal Tailwind CSS classes:
a:b:flex. Which is exactly what we can use here as well:Again, conceptually you can think of this syntax being expanded into the syntax from above.
Last but not least, we can also combine these:
This conceptually translates into the much more verbose version today:
The biggest downside is that this could potentially easily balloon your CSS file size if you're not careful. Because with this, it's pretty easy to add one more variant that introduces a lot of duplicated CSS.
This feature is completely backwards compatible, you can still nest your
@variantcalls yourself if you want, and combine them with these features if you want.This is also a continuation of #19526 and #19884, but for some reason I don't have push rights, so I'm creating this new PR instead. I did keep the original commits of those PRs so these contributors are still properly marked as contributors.


Closes: #19526
Closes: #19884
Test plan