Skip to content

Comments

fix: preserve parentheses around "or" in @media with type#4406

Open
veeceey wants to merge 1 commit intoevanw:mainfrom
veeceey:fix/issue-4395-css-media-parens
Open

fix: preserve parentheses around "or" in @media with type#4406
veeceey wants to merge 1 commit intoevanw:mainfrom
veeceey:fix/issue-4395-css-media-parens

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 23, 2026

The CSS minifier was dropping necessary grouping parentheses around "or" expressions when they appeared after a media type with "and". For example:

/* Input */
@media screen and ((min-width: 10px) or (min-height: 10px)) { color: blue; }

/* Before (invalid CSS - "and" and "or" mixed at same level) */
@media screen and (min-width:10px)or (min-height:10px){color:blue}

/* After (valid) */
@media screen and ((min-width:10px)or (min-height:10px)){color:blue}

Per the CSS spec, <media-condition-without-or> only allows <media-in-parens> [and <media-in-parens>]*, so an "or" group must be wrapped in parentheses. The printer was passing flags=0 when printing the condition from MQType, so MQBinary{Op: Or} didn't get its outer parens. The fix passes mqNeedsParens when the condition is an "or" binary.

Verified that regular "and" chains after media types still produce minimal output without extra wrapping.

Tests pass:

go test ./... 
ok  github.com/evanw/esbuild/internal/css_parser    0.458s
ok  github.com/evanw/esbuild/pkg/api                2.718s
(all packages pass)

Fixes #4395

The CSS minifier was dropping grouping parentheses around "or"
expressions when they appeared after a media type + "and", producing
invalid CSS. For example:

  @media screen and ((min-width: 10px) or (min-height: 10px))

was being minified to:

  @media screen and (min-width:10px)or (min-height:10px)

This is invalid because CSS does not allow mixing "and" and "or" at the
same nesting level. The spec requires the "or" group to be wrapped in
parentheses as a <media-in-parens> production.

The fix passes mqNeedsParens when printing the condition from an MQType
node if the condition is an "or" binary, ensuring the outer parentheses
are preserved.

Fixes evanw#4395
@veeceey
Copy link
Author

veeceey commented Feb 23, 2026

Test Results

All tests pass:

$ go test ./...
ok  github.com/evanw/esbuild/internal/css_lexer     0.487s
ok  github.com/evanw/esbuild/internal/css_parser     0.458s
ok  github.com/evanw/esbuild/internal/css_printer    0.933s
ok  github.com/evanw/esbuild/pkg/api                 2.718s

Manual verification:

/* Input */
@media screen and ((min-width: 10px) or (min-height: 10px)) { .test { color: blue } }

/* --minify output (before fix) */
@media screen and (min-width:10px)or (min-height:10px){.test{color:#00f}}

/* --minify output (after fix) */
@media screen and ((min-width:10px)or (min-height:10px)){.test{color:#00f}}

Also verified no regression for regular "and" chains:

/* Input */
@media screen and (min-width: 10px) and (min-height: 10px) { .test { color: blue } }
/* --minify (no extra parens) */
@media screen and (min-width:10px)and (min-height:10px){.test{color:#00f}}

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.

CSS minifier produces invalid output for @media rules

1 participant