Skip to content

Keep cross axis margins and relative insets of items in wrapping containers - #2045

Open
Om-singhaI wants to merge 1 commit into
react:mainfrom
Om-singhaI:fix-wrap-cross-margin-inset
Open

Om-singhaI wants to merge 1 commit into
react:mainfrom
Om-singhaI:fix-wrap-cross-margin-inset

Conversation

@Om-singhaI

Copy link
Copy Markdown

Fixes #1856
Fixes #1067

In wrapping containers, STEP 8 (multi line alignment) of CalculateLayout.cpp repositions items on the cross axis and drops part of each box. In #1856 the container is a column (Yoga's default), so left is on the cross axis.

Every case now uses the leading margin, trailing margin and relativePosition. wrap-reverse mirrors positions afterwards, so it leads with the flex end margin and applies the inset after mirroring, keeping each margin on its edge and top: 10 moving the item down.

Also, a percentage inset on a flex-start item now resolves against the container's cross size, as without wrap (top: 10% in a 100x200 row: 10 to 20), and baseline rows, which run this pass even without wrap, honor bottom and ignore insets on static items.

Column container, padding 10, flex-wrap: wrap, default align-content: flex-start, item with margin 5 and left: 10 (item left; expected worked out from the spec):

align-items main this PR expected
flex-start 20 25 25
stretch 15 25 25
center 15 25 25
flex-end 15 25 25
flex-start, no inset 10 15 15

The #1067 repro goes from top 50 to 100.

I added tests/YGFlexWrapCrossAlignmentTest.cpp: 9 hand written gtest cases like the other non generated tests, covering row and column, LTR and RTL, wrap and wrap-reverse (including one sided margins), baseline, static items and both issue repros. All 9 fail without the CalculateLayout.cpp change; with it, all 877 tests pass (868 plus 9). I can move them into gentest fixtures or add an Errata flag if you prefer.

…ainers

The multi line alignment pass (STEP 8) dropped the relative inset for
`stretch`, `center` and `flex-end`, and for `flex-start` it used the
inset in place of the leading margin. `center` also ignored margins, so
an item with a one sided cross margin was centered on its border box.
Baseline layouts run the same pass without wrapping, so they were
affected too.

Compute the leading margin, trailing margin and relative inset once per
item and use them in every alignment, including baseline, which now
honors `bottom` and skips insets on static items like the single line
path does. Percentage insets on `flex-start` items now resolve against
the container's cross size, as they do without wrapping.

`flex-wrap: wrap-reverse` mirrors these positions afterwards, so there
the flex end margin is the leading one, which keeps each margin on the
edge it was set on. The inset is taken out before mirroring and added
back afterwards so it keeps its physical direction.

Fixes react#1856
Fixes react#1067
@meta-cla meta-cla Bot added the CLA Signed label Oct 19, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 19, 2026
@meta-codesync

meta-codesync Bot commented Oct 23, 2026

Copy link
Copy Markdown

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

@cipolleschi cipolleschi left a comment

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.

Thanks for digging into this, and for the clear before/after table. The fix looks right to me. I built it locally, the full suite passes, and I compared a few wrap layouts against main.
My concern isn't the change itself but what it does to existing layouts. I left a couple of questions inline.

case Align::End:
// Not yet implemented
break;
case Align::FlexStart: {

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.

This is the right fix, but it moves items that apps have been laying out with since 3.0. I compared main and this PR with a small Yoga test: a row with flexWrap: 'wrap', alignItems: 'flex-start' and children with margin: 4, which is a common setup in RN. On main the children on the first line sit at top 0; with this PR they move to 4, and the second line moves from 38 to 42. center with a one-sided margin and wrap-reverse with stretch move too.

React Native runs Yoga with Errata::All by default. Would it make sense to put the old behavior behind a new Errata flag, as you offered in the summary? That way RN keeps its current layout under the classic conformance, and strict gets the spec behavior.


// The second line spans top 40 to 100. The item keeps its margin and moves
// 10 down for its top inset under every alignment.
TEST(YogaTest, wrap_row_keeps_cross_axis_margin_and_inset) {

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.

The expected values here are worked out by hand from the spec. Could we add these cases as gentest fixtures instead, for example in YGFlexWrapTest.html? Then the expected values come from Chrome, and we get the Java and JS tests for free.

isWrapReverse ? flexEndMargin : flexStartMargin;
const float trailingCrossMargin =
isWrapReverse ? flexStartMargin : flexEndMargin;
const float relativePositionCross = child->relativePosition(

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.

The summary says every case now uses the leading and trailing margins. What about margin: auto on the cross axis? In a row with flexWrap: 'wrap', an item with marginTop: 'auto' in a 50pt line lands at top 0, but at 40 without wrap, because this pass overwrites what STEP 7 computed. It's the same on main, so it's not a regression from this PR. Could you open an issue for it, or handle it here if it's small?

This branch has not been deployed

No deployments
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

2 participants