From 7ae2cf2b49376a534450183786b7fae0e5c56d4b Mon Sep 17 00:00:00 2001 From: Om Singhal Date: Tue, 27 Oct 2026 09:38:01 -0700 Subject: [PATCH] Keep cross axis margins and relative insets of items in wrapping containers (#59532) Summary: Fixes https://github.com/react/yoga/issues/1856 Fixes https://github.com/react/yoga/issues/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 https://github.com/react/yoga/issues/1856 the container is a column (Yoga's default), so `left` is on the cross axis. - `flex-start` uses `computeFlexStartPosition` (the inset) where `computeFlexStartMargin` belongs. v2.0.1 used the margin, so this is a 3.0 regression from https://github.com/react/yoga/issues/1561. - `stretch`, `center` and `flex-end` never add the relative inset. - `center` centers the border box instead of the margin box, which is https://github.com/react/yoga/issues/1067. 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 https://github.com/react/yoga/issues/1067 repro goes from top 50 to 100. ### Errata These positions have been in place for a long time, and React Native runs Yoga with `YGErrataAll`. So the previous behavior is kept behind a new erratum, `YGErrataWrapCrossAlignmentIgnoresMarginAndInset` (`1 << 5`). When it is set, STEP 8 and the `wrap-reverse` mirroring use their previous offsets. New configs leave it unset and get the fix; `YGErrataAll` and `YGErrataClassic` include it, so React Native keeps its current layout under the classic conformance, and strict gets the spec behavior. With the erratum, the `main` column of the table above is what you get. The enum bindings (C++, Kotlin and TypeScript) are regenerated with `enums.py`. ### Tests The spec cases are gentest fixtures, eleven in `YGFlexWrapTest.html` and one in `YGAlignItemsTest.html`, with expected values from Chrome and generated C++, Java and JS tests. They cover row and column, LTR and RTL, `wrap` and `wrap-reverse` (including one sided margins), baseline, static items, a percentage inset and both issue repros. All 12 generated C++ tests fail without the fix. `tests/YGFlexWrapCrossAlignmentTest.cpp` keeps 4 hand written errata cases that pin the previous positions under the erratum, including a wrapping row with `align-items: flex-start` and items with `margin: 4`, whose lines stay at top 0 and 38 under `YGErrataAll` and move to 4 and 42 under `YGErrataNone`. The positions they expect under the erratum are the ones `main` gives, and all 4 fail without the erratum checks in `CalculateLayout.cpp`. With the change, all 884 C++ tests pass (868 plus 12 generated plus 4). Laying out 20000 random trees with `YGErrataAll`, every position on this branch matches the base commit exactly, and without the erratum every position matches the first commit of this PR. Not covered here: `auto` cross margins in wrapping containers and baseline rows, which STEP 8 also ignores on `main` (https://github.com/react/yoga/issues/2048). Changelog: [General][Fixed] - Keep cross axis margins and relative insets of items in wrapping containers, with a new erratum that keeps the previous positions under YGErrataAll and YGErrataClassic X-link: https://github.com/react/yoga/pull/2045 Test Plan: Universal macOS Cosmo Studio app and dSYM build passed with the configuration from the failing CI job: ```bash buck2 build fbsource//xplat/mode/cosmo/auto.py#production,macosx-universal,e2e,bundle-rn --show-json-output --config apple.fb_bundle_version=1101344647 //xplat/arfx/cosmo/studio/app/mac:Cosmo "//xplat/arfx/cosmo/studio/app/mac:Cosmo[dsym]" ``` `BUILD SUCCEEDED`, exit code 0. https://www.internalfb.com/buck2/fbae0652-2658-4575-a895-b8a3d54d386f Yoga regression tests: ```bash buck2 test //xplat/yoga:tests ``` Pass 893. Fail 0. Skip 0. Infra Failure 0. Build failure 0. https://www.internalfb.com/intern/testinfra/testrun/12666374165098828 C++ stable API snapshot validation: ```bash buck2 run //xplat/js/react-native-github/scripts/cxx-api:api-snapshot -- --validate ``` Passed, exit code 0. The base includes the landed Boost Filesystem fix in D127790095. The Yoga changes are unchanged apart from merging onto the current base. App launch and Cosmo E2E execution were not tested by the local build. Reviewed By: javache Differential Revision: D127132637 Pulled By: cipolleschi --- .../main/java/com/facebook/yoga/YogaErrata.kt | 2 + .../ReactCommon/yoga/yoga/YGEnums.cpp | 2 + .../ReactCommon/yoga/yoga/YGEnums.h | 1 + .../yoga/yoga/algorithm/CalculateLayout.cpp | 91 ++++++++++++++----- .../ReactCommon/yoga/yoga/enums/Errata.h | 1 + .../api-snapshots/ReactAndroidDebugCxx.api | 1 + .../ReactAndroidFrameworksCxx.api | 1 + .../api-snapshots/ReactAndroidNewarchCxx.api | 1 + .../api-snapshots/ReactAndroidReleaseCxx.api | 1 + .../api-snapshots/ReactAppleDebugCxx.api | 1 + .../api-snapshots/ReactAppleFrameworksCxx.api | 1 + .../api-snapshots/ReactAppleNewarchCxx.api | 1 + .../api-snapshots/ReactAppleReleaseCxx.api | 1 + .../api-snapshots/ReactCommonDebugCxx.api | 1 + .../ReactCommonFrameworksCxx.api | 1 + .../api-snapshots/ReactCommonNewarchCxx.api | 1 + .../api-snapshots/ReactCommonReleaseCxx.api | 1 + 17 files changed, 88 insertions(+), 21 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/yoga/YogaErrata.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/yoga/YogaErrata.kt index ff2bcaf1b637..c9196745e8f2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/yoga/YogaErrata.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/yoga/YogaErrata.kt @@ -16,6 +16,7 @@ public enum class YogaErrata(public val intValue: Int) { ABSOLUTE_PERCENT_AGAINST_INNER_SIZE(4), MIN_SIZE_UNDEFINED_INSTEAD_OF_AUTO(8), FLEX_FIRST_PASS_USES_RUNNING_TOTALS(16), + WRAP_CROSS_ALIGNMENT_IGNORES_MARGIN_AND_INSET(32), ALL(2147483647), CLASSIC(2147483646); @@ -31,6 +32,7 @@ public enum class YogaErrata(public val intValue: Int) { 4 -> ABSOLUTE_PERCENT_AGAINST_INNER_SIZE 8 -> MIN_SIZE_UNDEFINED_INSTEAD_OF_AUTO 16 -> FLEX_FIRST_PASS_USES_RUNNING_TOTALS + 32 -> WRAP_CROSS_ALIGNMENT_IGNORES_MARGIN_AND_INSET 2147483647 -> ALL 2147483646 -> CLASSIC else -> throw IllegalArgumentException("Unknown enum value: $value") diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGEnums.cpp b/packages/react-native/ReactCommon/yoga/yoga/YGEnums.cpp index 017d6ee45ad0..6b82c47ba8a5 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGEnums.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/YGEnums.cpp @@ -121,6 +121,8 @@ const char* YGErrataToString(const YGErrata value) { return "min-size-undefined-instead-of-auto"; case YGErrataFlexFirstPassUsesRunningTotals: return "flex-first-pass-uses-running-totals"; + case YGErrataWrapCrossAlignmentIgnoresMarginAndInset: + return "wrap-cross-alignment-ignores-margin-and-inset"; case YGErrataAll: return "all"; case YGErrataClassic: diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGEnums.h b/packages/react-native/ReactCommon/yoga/yoga/YGEnums.h index c672870023aa..d5c5937b8f25 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGEnums.h +++ b/packages/react-native/ReactCommon/yoga/yoga/YGEnums.h @@ -69,6 +69,7 @@ YG_ENUM_DECL( YGErrataAbsolutePercentAgainstInnerSize = 4, YGErrataMinSizeUndefinedInsteadOfAuto = 8, YGErrataFlexFirstPassUsesRunningTotals = 16, + YGErrataWrapCrossAlignmentIgnoresMarginAndInset = 32, YGErrataAll = 2147483647, YGErrataClassic = 2147483646) YG_DEFINE_ENUM_FLAG_OPERATORS(YGErrata) diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp index 192a6af77da9..263d2e6cd8df 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp @@ -2325,6 +2325,13 @@ static void calculateLayoutImpl( float leadPerLine = 0; float currentLead = leadingPaddingAndBorderCross; float extraSpacePerLine = 0; + // With wrap-reverse the cross positions set here are mirrored further + // below, so an item's flex end margin is the one that leads here. + const bool isWrapReverse = node->style().flexWrap() == Wrap::WrapReverse; + // The erratum keeps the cross positions this pass produced before it + // honored margins and relative insets. + const bool ignoresCrossMarginAndInset = + node->hasErrata(Errata::WrapCrossAlignmentIgnoresMarginAndInset); const float unclampedCrossDim = sizingModeCrossDim == SizingMode::StretchFit ? availableInnerCrossDim + paddingAndBorderAxisCross @@ -2443,43 +2450,69 @@ static void calculateLayoutImpl( continue; } if (child->style().positionType() != PositionType::Absolute) { + const float flexStartMargin = child->style().computeFlexStartMargin( + crossAxis, direction, availableInnerWidth); + const float flexEndMargin = child->style().computeFlexEndMargin( + crossAxis, direction, availableInnerWidth); + // The erratum leaves the margins unswapped for wrap-reverse and + // drops the inset. Flex start, center and baseline used other + // offsets there, so they check the erratum themselves. + const bool reverseMargins = + isWrapReverse && !ignoresCrossMarginAndInset; + const float leadingCrossMargin = + reverseMargins ? flexEndMargin : flexStartMargin; + const float trailingCrossMargin = + reverseMargins ? flexStartMargin : flexEndMargin; + const float relativePositionCross = ignoresCrossMarginAndInset + ? 0.0f + : child->relativePosition( + crossAxis, direction, availableInnerCrossDim); + switch (resolveChildAlignment(node, child)) { case Align::Start: case Align::End: // Not yet implemented break; case Align::FlexStart: { - child->setLayoutPosition( - currentLead + - child->style().computeFlexStartPosition( - crossAxis, direction, availableInnerWidth), - flexStartEdge(crossAxis)); + if (ignoresCrossMarginAndInset) { + child->setLayoutPosition( + currentLead + + child->style().computeFlexStartPosition( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + } else { + child->setLayoutPosition( + currentLead + leadingCrossMargin + relativePositionCross, + flexStartEdge(crossAxis)); + } break; } case Align::FlexEnd: { child->setLayoutPosition( - currentLead + lineHeight - - child->style().computeFlexEndMargin( - crossAxis, direction, availableInnerWidth) - + currentLead + lineHeight - trailingCrossMargin - child->getLayout().measuredDimension( - dimension(crossAxis)), + dimension(crossAxis)) + + relativePositionCross, flexStartEdge(crossAxis)); break; } case Align::Center: { - float childHeight = + const float childCrossDim = child->getLayout().measuredDimension(dimension(crossAxis)); + const float childOuterCrossDim = + childCrossDim + leadingCrossMargin + trailingCrossMargin; child->setLayoutPosition( - currentLead + (lineHeight - childHeight) / 2, + ignoresCrossMarginAndInset + ? currentLead + (lineHeight - childCrossDim) / 2 + : currentLead + (lineHeight - childOuterCrossDim) / 2 + + leadingCrossMargin + relativePositionCross, flexStartEdge(crossAxis)); break; } case Align::Stretch: { child->setLayoutPosition( - currentLead + - child->style().computeFlexStartMargin( - crossAxis, direction, availableInnerWidth), + currentLead + leadingCrossMargin + relativePositionCross, flexStartEdge(crossAxis)); // Remeasure child with the line height as it as been only @@ -2528,10 +2561,12 @@ static void calculateLayoutImpl( child->setLayoutPosition( currentLead + maxAscentForCurrentLine - calculateBaseline(child) + - child->style().computeFlexStartPosition( - FlexDirection::Column, - direction, - availableInnerCrossDim), + (ignoresCrossMarginAndInset + ? child->style().computeFlexStartPosition( + FlexDirection::Column, + direction, + availableInnerCrossDim) + : relativePositionCross), PhysicalEdge::Top); break; @@ -2643,14 +2678,28 @@ static void calculateLayoutImpl( } // As we only wrapped in normal direction yet, we need to reverse the - // positions on wrap-reverse. + // positions on wrap-reverse. A relative inset is a physical offset, so it is + // taken out before reversing and added back afterwards (the erratum mirrors + // it with the rest of the position). if (performLayout && node->style().flexWrap() == Wrap::WrapReverse) { + const bool ignoresCrossMarginAndInset = + node->hasErrata(Errata::WrapCrossAlignmentIgnoresMarginAndInset); for (auto child : node->getLayoutChildren()) { if (child->style().positionType() != PositionType::Absolute) { + const float relativePositionCross = + child->style().display() == Display::None || + ignoresCrossMarginAndInset + ? 0.0f + : child->relativePosition( + crossAxis, direction, availableInnerCrossDim); + const float positionInFlow = + child->getLayout().position(flexStartEdge(crossAxis)) - + relativePositionCross; child->setLayoutPosition( node->getLayout().measuredDimension(dimension(crossAxis)) - - child->getLayout().position(flexStartEdge(crossAxis)) - - child->getLayout().measuredDimension(dimension(crossAxis)), + positionInFlow - + child->getLayout().measuredDimension(dimension(crossAxis)) + + relativePositionCross, flexStartEdge(crossAxis)); } } diff --git a/packages/react-native/ReactCommon/yoga/yoga/enums/Errata.h b/packages/react-native/ReactCommon/yoga/yoga/enums/Errata.h index 09e6f83a3eba..a9fdb344f94b 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/enums/Errata.h +++ b/packages/react-native/ReactCommon/yoga/yoga/enums/Errata.h @@ -22,6 +22,7 @@ enum class Errata : uint32_t { AbsolutePercentAgainstInnerSize = YGErrataAbsolutePercentAgainstInnerSize, MinSizeUndefinedInsteadOfAuto = YGErrataMinSizeUndefinedInsteadOfAuto, FlexFirstPassUsesRunningTotals = YGErrataFlexFirstPassUsesRunningTotals, + WrapCrossAlignmentIgnoresMarginAndInset = YGErrataWrapCrossAlignmentIgnoresMarginAndInset, All = YGErrataAll, Classic = YGErrataClassic, }; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 0b8d391a4639..d0f5ce8daf55 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -6930,6 +6930,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api index 063d23740fac..ab27355f4213 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api @@ -11380,6 +11380,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 636dea874c76..e3effbfe8c50 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -6779,6 +6779,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 636dea874c76..e3effbfe8c50 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -6779,6 +6779,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 369407ed545c..86911ca94015 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -8682,6 +8682,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api index 1fd4521efc72..2a2809b8d8f4 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api @@ -13165,6 +13165,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index df4b2417b5af..946d58d82ae7 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -8534,6 +8534,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 3db0e926dc1e..e8fde4f81ecf 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -8541,6 +8541,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index dfbc4d44eccf..fdeca96fe01c 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -4165,6 +4165,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api index 40d47068ebce..76753b327e6f 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api @@ -8513,6 +8513,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index 845fb97ab917..35e5ef9137d8 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -4158,6 +4158,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t { diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 845fb97ab917..35e5ef9137d8 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -4158,6 +4158,7 @@ enum facebook::yoga::Errata : uint32_t { MinSizeUndefinedInsteadOfAuto, None, StretchFlexBasis, + WrapCrossAlignmentIgnoresMarginAndInset, } enum facebook::yoga::FlexDirection : uint8_t {