Skip to content

Fix zero-unit stripping inside functions after the first (invalid CSS)#1750

Open
durvesh1992 wants to merge 1 commit into
facebook:mainfrom
durvesh1992:fix/zero-dimensions-nested-function
Open

Fix zero-unit stripping inside functions after the first (invalid CSS)#1750
durvesh1992 wants to merge 1 commit into
facebook:mainfrom
durvesh1992:fix/zero-dimensions-nested-function

Conversation

@durvesh1992

Copy link
Copy Markdown

Summary

normalizeZeroDimensions strips redundant units from zero values (0px0) but must not do so inside a function — e.g. in calc(0px + 1%), a unitless 0 is a <number> and cannot be added to a <percentage>. It tracks the active function's range via endFunction, but the guard only recorded the first function:

if (node.type === 'function' && !endFunction) {   // only the first function
  endFunction = node.sourceEndIndex ?? 0;
}
if (endFunction > 0 && node.sourceIndex > endFunction) {
  endFunction = 0;                                  // reset once past it
}

postcss-value-parser walks a function node before its children, so when a second function is reached the && !endFunction guard blocks the update, the reset line then zeroes endFunction, and the second function's zero values get their units stripped — producing invalid CSS:

input output
calc(0px + 1%) (alone) calc(0px + 1%)
calc(0px + 1%) max(1px,2px) (1st) calc(0px + 1%) …
max(1px,2px) calc(0px + 1%) (2nd) max(1px,2px) calc(0 + 1%)

Fix

Record every function's end with Math.max(endFunction, node.sourceEndIndex ?? 0). The existing sourceIndex > endFunction reset still clears the range once walking moves past the last function, so bare zeros outside functions (margin: 0px0, calc(5px) 0px… 0) still normalize correctly.

Test plan

yarn --cwd packages/@stylexjs/babel-plugin jest

Added a regression test (width: 'max(1px, 2px) calc(0px + 1%)' stays calc(0px + 1%)) that fails before and passes after. Full babel-plugin suite: 960 passed, no snapshot regressions.

normalizeZeroDimensions must not strip the unit from a zero inside a
function (e.g. calc(0px + 1%) — a unitless 0 can't be added to a %). It
tracks the active function via endFunction, but the guard
`node.type === 'function' && !endFunction` only ever recorded the FIRST
function. For a later function, endFunction had already been reset to 0,
so its children were treated as outside a function and their units were
stripped — emitting invalid CSS like `max(1px,2px) calc(0 + 1%)`.

Track every function with Math.max(endFunction, sourceEndIndex); the
existing reset still clears it once walking moves past the last function,
so bare zeros outside functions are still normalized.

Adds a regression test (fails before / passes after).
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant