Skip to content

Fix percentage min/max on flex items resolving against wrong ancestor#1903

Open
costajohnt wants to merge 5 commits into
facebook:mainfrom
costajohnt:fix/percent-min-max-against-parent
Open

Fix percentage min/max on flex items resolving against wrong ancestor#1903
costajohnt wants to merge 5 commits into
facebook:mainfrom
costajohnt:fix/percent-min-max-against-parent

Conversation

@costajohnt
Copy link
Copy Markdown

Summary

Fixes #872.

Percentage min-width/max-width/min-height/max-height on flex items resolved against the grandparent's owner size instead of the parent container's inner size. This is because boundAxisWithinMinAndMax() received mainAxisOwnerSize (the node's parent, i.e. the flex item's grandparent) rather than availableInnerMainDim (the node's own inner content area, i.e. the flex item's actual parent) at three call sites:

  • calculateFlexLine() in FlexLine.cpp
  • distributeFreeSpaceSecondPass() in CalculateLayout.cpp
  • distributeFreeSpaceFirstPass() in CalculateLayout.cpp

The fix is gated behind a new errata flag FlexItemPercentMinMaxAgainstOwner, so consumers using Classic or All errata (e.g. React Native) automatically preserve the old behavior. Default (Errata::None) now produces correct W3C-conformant results.

This is a successor to #1015, which identified the correct fix but was not merged because it needed to be gated behind an errata flag (which didn't exist at the time).

Test plan

  • Added gentest fixtures for nested min-width: 50% and max-width: 50% (Chrome-verified: child = 5px = 50% of parent's 10px, not 20px = 50% of grandparent's 40px)
  • Added hand-written errata tests verifying:
    • Default behavior produces correct result (5px)
    • FlexItemPercentMinMaxAgainstOwner errata preserves old behavior (20px)
    • Classic errata includes the new flag
    • Cross-axis (column direction with min-height) works correctly
  • Disabled percentage_flex_basis_main_min_width test (data-disabled) where Yoga now diverges from Chrome (same pattern as existing percentage_flex_basis_cross_min_height)
  • All 832 tests pass (786 pass, 46 skip), 0 failures
  • gentest-validate passes for all 75 generated files

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
yoga-website Ready Ready Preview, Comment May 25, 2026 5:48pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed label Mar 1, 2026
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Mar 1, 2026
</div>

<div id="percentage_flex_basis_main_min_width" style="width: 200px; height: 200px; flex-direction: row;">
<div id="percentage_flex_basis_main_min_width" data-disabled="true" style="width: 200px; height: 200px; flex-direction: row;">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Chrome's behavior here wrong? What changes?

@NickGerleman
Copy link
Copy Markdown
Contributor

Change seems sane. Just curious about the case where we used to agree with Chrome, but no longer do. Or any references to spec, for what box we should be using as reference in this situation?

@costajohnt
Copy link
Copy Markdown
Author

The percentage_flex_basis_main_min_width test is Chrome-generated, and Chrome has a known spec deviation here. The test combines flex-basis: 15% with min-width: 60%. CSS Flexbox §9.2 step 3 says min/max main sizes should be ignored when determining flex base size ("no clamping occurs"). Chrome clamps anyway, so Yoga now diverges from Chrome on this specific case. Same thing already happens with the existing disabled test percentage_flex_basis_cross_min_height.

For which box percentages resolve against: CSS Sizing 3 §5.1 says percentage sizes resolve against the containing block. For a flex item, that's the flex container (parent), not the grandparent. This is also what PR #1015 identified, but it couldn't be merged at the time because errata gating didn't exist yet.

@meta-codesync
Copy link
Copy Markdown

meta-codesync Bot commented Mar 3, 2026

@NickGerleman has imported this pull request. If you are a Meta employee, you can view this in D95014513.

@nicoburns
Copy link
Copy Markdown
Contributor

nicoburns commented Mar 16, 2026

The percentage_flex_basis_main_min_width test is Chrome-generated, and Chrome has a known spec deviation here. The test combines flex-basis: 15% with min-width: 60%. CSS Flexbox §9.2 step 3 says min/max main sizes should be ignored when determining flex base size ("no clamping occurs"). Chrome clamps anyway, so Yoga now diverges from Chrome on this specific case. Same thing already happens with the existing disabled test percentage_flex_basis_cross_min_height.

Oh, really? That part of the spec ("min/max main sizes should be ignored when determining flex base size") is a very annoying special case. I believe this is the only case in any css layout mode where a container needs to measure it's child without clamping, and it makes caching for Flexbox a lot trickier than it would otherwise need to be. If Chrome is clamping anyway, then I'd be very tempted to adopt this behaviour too.

Perhaps a discussion to be had in the csswg-drafts repo. Do you have a reference/source for Chrome not doing the clamping?

@costajohnt
Copy link
Copy Markdown
Author

@nicoburns I need to correct my earlier comment. After looking at this more carefully, Chrome's output (120/80 for that test case) is actually the spec-compliant result. Chrome is not clamping during flex base size computation here.

If Chrome were clamping the base size by min-width upfront, the numbers would be 128/72 instead. You can verify with a simple test page using the same styles from the fixture (flex-basis 15% with min-width 60% in a 200px row container).

The test is disabled because my fix makes percentage min-width actually resolve correctly (before, it couldn't resolve because it was looking at the wrong ancestor). Once it resolves, Yoga applies it too early during the base size step, which gives 128/72 instead of Chrome's 120/80. That early clamping is a separate issue from this PR.

So to answer your question directly, I don't have evidence of Chrome clamping because it looks like Chrome follows the spec here. Sorry for the confusion in my earlier comment. A csswg-drafts discussion could still be interesting though, especially if the no-clamping behavior is making caching harder for Taffy.

@costajohnt
Copy link
Copy Markdown
Author

Just checking in — anything else needed on this one?

@costajohnt
Copy link
Copy Markdown
Author

Friendly ping. The "Import Status" check has been queued since April 11. If the internal diff has stalled, happy to rebase, split the change, or do whatever helps move it along. Otherwise no rush, just wanted to surface it in case it needs a poke.

Percentage min-width/max-width/min-height/max-height on flex items
resolved against the grandparent's owner size instead of the parent
container's inner size. This is because boundAxisWithinMinAndMax()
received mainAxisOwnerSize (the parent's parent) rather than
availableInnerMainDim (the parent's inner content area) at three
call sites.

The fix is gated behind a new errata flag,
FlexItemPercentMinMaxAgainstOwner, so consumers using Classic or All
errata (e.g. React Native) automatically preserve the old behavior.
Default (Errata::None) now produces correct W3C-conformant results.

Fixes facebook#872
Ran yarn gentest -f YGPercentageTest to update the generated test
files after rebasing onto upstream/main. The gentest tool now emits
style properties in a different order, which accounts for the bulk
of the diff.
Upstream fb8e618 (Fix "instrinsic" typo) silently merged into
the branch during rebase, invalidating the previous SignedSource
hash. Ran yarn gentest -f YGPercentageTest to produce a valid
hash against the current main state.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

min-width in percent isn't calculated against parent node

4 participants