--minify produces invalid selectors for space-y-* (CSS Nesting + :where() mishandled)
Summary
When @tailwindcss/cli@4.3.0 builds with --minify, every space-y-N utility produces an invalid selector with a stray closing paren that browsers silently reject. The unminified output is correct; the minifier (lightningcss path) is stripping the :where(& opening but keeping the matching ).
Versions
@tailwindcss/cli@4.3.0
tailwindcss@4.3.0
- Node 18.x / macOS 14
- Browsers verified: Chrome 130, Safari 18 — both silently ignore the rule
Repro
Minimal input.css:
A page that uses any space-y-N class (e.g. <div class="space-y-12"><p>a</p><p>b</p></div>).
Unminified — correct
npx tailwindcss -i input.css -o out.css
grep -A 6 '\.space-y-12 {' out.css
.space-y-12 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 12) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 12) * calc(1 - var(--tw-space-y-reverse)));
}
}
✅ Valid CSS Nesting. Renders correctly in Chrome 112+ / Safari 16.5+ / Firefox 117+.
Minified — invalid
npx tailwindcss -i input.css -o out.min.css --minify
grep -oE '\.space-y-12[^,{}]*\{[^}]*\}' out.min.css
.space-y-12>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 12) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 12) * calc(1 - var(--tw-space-y-reverse)))}
❌ Note the trailing :not(:last-child)) — the :where(& was stripped but the closing paren survived. Browsers parse this as an invalid selector and discard the entire rule. No console warning is emitted.
Expected
Minified output should produce a selector equivalent to the unminified rule, e.g.:
:where(.space-y-12>:not(:last-child)){--tw-space-y-reverse:0; ...}
or any other valid flattening that preserves the :where(...) specificity (0,0,0) intent.
Impact
- Affects the entire
space-y-* family (every numeric value). Presumably space-x-* too — they share the same generation path.
- Silent failure mode: no console error, no build warning. Bug surfaces only when developers notice that vertical spacing isn't applying as expected. Trial-and-error debugging led us through stale-bundle / browser-cache theories before fetching the deployed CSS via curl and grepping the rule.
- Workaround: drop
--minify (we ship +1 KB gzipped to restore correct behavior) or replace space-y-N calls with flex flex-col gap-N (works identically for block-flow layouts).
Probable root cause
The bug is in how the minifier flattens nested CSS containing :where(& > ...). The opening :where( and the & parent reference are dropped (presumably collapsed into the parent selector) but the matching ) is not removed. Similar nested-rule patterns without :where() may need verifying — we only tested space-y-*, but any utility emitting selector { :where(& ...) { ... } } is a candidate.
Other Tailwind utilities verified clean under --minify
mt-*, mb-*, mx-*, my-*, gap-*, gap-x-*, gap-y-*, flex, flex-col, plus all the standard utility-classes we render in production. The bug appears isolated to utilities that emit :where(& > :not(...)) patterns.
Related
--minifyproduces invalid selectors forspace-y-*(CSS Nesting +:where()mishandled)Summary
When
@tailwindcss/cli@4.3.0builds with--minify, everyspace-y-Nutility produces an invalid selector with a stray closing paren that browsers silently reject. The unminified output is correct; the minifier (lightningcss path) is stripping the:where(&opening but keeping the matching).Versions
@tailwindcss/cli@4.3.0tailwindcss@4.3.0Repro
Minimal
input.css:A page that uses any
space-y-Nclass (e.g.<div class="space-y-12"><p>a</p><p>b</p></div>).Unminified — correct
npx tailwindcss -i input.css -o out.css grep -A 6 '\.space-y-12 {' out.css✅ Valid CSS Nesting. Renders correctly in Chrome 112+ / Safari 16.5+ / Firefox 117+.
Minified — invalid
npx tailwindcss -i input.css -o out.min.css --minify grep -oE '\.space-y-12[^,{}]*\{[^}]*\}' out.min.css❌ Note the trailing
:not(:last-child))— the:where(&was stripped but the closing paren survived. Browsers parse this as an invalid selector and discard the entire rule. No console warning is emitted.Expected
Minified output should produce a selector equivalent to the unminified rule, e.g.:
or any other valid flattening that preserves the
:where(...)specificity (0,0,0) intent.Impact
space-y-*family (every numeric value). Presumablyspace-x-*too — they share the same generation path.--minify(we ship +1 KB gzipped to restore correct behavior) or replacespace-y-Ncalls withflex flex-col gap-N(works identically for block-flow layouts).Probable root cause
The bug is in how the minifier flattens nested CSS containing
:where(& > ...). The opening:where(and the&parent reference are dropped (presumably collapsed into the parent selector) but the matching)is not removed. Similar nested-rule patterns without:where()may need verifying — we only testedspace-y-*, but any utility emittingselector { :where(& ...) { ... } }is a candidate.Other Tailwind utilities verified clean under
--minifymt-*,mb-*,mx-*,my-*,gap-*,gap-x-*,gap-y-*,flex,flex-col, plus all the standard utility-classes we render in production. The bug appears isolated to utilities that emit:where(& > :not(...))patterns.Related