Skip to content

Add fontFeatureSettings and resolve font features in shared C++ - #58266

Open
Abbondanzo wants to merge 2 commits into
react:mainfrom
Abbondanzo:export-D115621392
Open

Add fontFeatureSettings and resolve font features in shared C++#58266
Abbondanzo wants to merge 2 commits into
react:mainfrom
Abbondanzo:export-D115621392

Conversation

@Abbondanzo

Copy link
Copy Markdown
Contributor

Summary:
Adds the fontFeatureSettings style prop, and moves feature-list composition out of the two
per-platform tables into one function in shared C++ so both renderers resolve precedence
identically. Contributions are emitted least- to most-specific, so a tag named more than once
resolves last-wins and no separate conflict-resolution step has to be kept in sync.

Also fixes a pre-existing bug: the Android span-creation guards did not test fontFeatureSettings,
so a fragment whose only font prop was fontVariant produced no span at all and dropped its
features.

Changelog:
[General][Added] - Add fontFeatureSettings style prop to <Text>
[General][Fixed] - A text run whose only font prop was fontVariant produced no styled span on Android and silently dropped its features

Differential Revision: D115621392

Summary:
`hash_combine` mixes each field into the previous seed, so it forms a dependency chain the CPU
cannot overlap and an unset optional still costs a full link. `hash_combine_optionals` folds a run
of optionals into one presence-mask link plus the engaged values, so a further optional costs a bit
in the mask rather than a link. The mask is what keeps it collision-free: skipping disengaged
fields alone would make the same value in two different slots hash identically.

Equality moves from `std::tie` to a short-circuit chain ordered cheapest first, with the string and
vector fields last, because the dominant caller is a successful cache lookup where the keys are
equal and every field has to be examined.

Changelog:
[Internal] - Hash optional `TextAttributes` fields via a presence mask and compare them with a short-circuit chain

Differential Revision: D115621401
Summary:
Adds the `fontFeatureSettings` style prop, and moves feature-list composition out of the two
per-platform tables into one function in shared C++ so both renderers resolve precedence
identically. Contributions are emitted least- to most-specific, so a tag named more than once
resolves last-wins and no separate conflict-resolution step has to be kept in sync.

Also fixes a pre-existing bug: the Android span-creation guards did not test `fontFeatureSettings`,
so a fragment whose only font prop was `fontVariant` produced no span at all and dropped its
features.

Changelog:
[General][Added] - Add `fontFeatureSettings` style prop to `<Text>`
[General][Fixed] - A text run whose only font prop was `fontVariant` produced no styled span on Android and silently dropped its features

Differential Revision: D115621392
@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 Aug 31, 2026
@meta-codesync

meta-codesync Bot commented Aug 31, 2026

Copy link
Copy Markdown

@Abbondanzo has exported this pull request. If you are a Meta employee, you can view the originating Diff in D115621392.

@github-actions

Copy link
Copy Markdown

Warning

JavaScript API change detected

This PR commits an update to ReactNativeApi.d.ts, indicating a change to React Native's public JavaScript API.

  • Please include a clear changelog message.
  • This change will be subject to additional review.

This change was flagged as: POTENTIALLY_BREAKING

@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 AI code review

Decision: Ready for human review (with comments)

Overall PR risk: Medium. The change modifies existing behavior for text attribute parsing, fontVariant resolution, equality and hash, and Android and iOS text spans, so blast radius is text rendering on both platforms with direct rollback by revert. The most plausible break is platform divergence where Android applies fontFeatureSettings while iOS drops it, plus loss of slant for angled oblique values. Coverage was reduced because the contracts-api-codegen review ran out of time, so findings are incomplete and absence of other findings does not mean the change is clean.

⏱️ Coverage note: coverage is partial — some review passes did not
finish (timed out or failed), so issues may exist in areas not fully reviewed:

  • The contracts-api-codegen review (part 2 of 2) ran out of time; its findings may be incomplete.

🔗 Review setup:

  • This PR changes code the reviewer prompts cite (packages/react-native/ReactAndroid/api/ReactAndroid.api). Check that the guidance quoting it is still correct.

🟡 Warning (2)

  • iOS ignores fontFeatureSettings while Android applies itpackages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm:143 (correctness) · id:2aa4a98f9853
    Confidence: High — traced serialization and both platform consumers.
    Impact if shipped: Medium — iOS text ignores the new style while Android renders it.
    Suggested remediation: Pass the resolved feature list into RCTFontProperties and apply it with UIFontDescriptorFeatureSettingsAttribute alongside fontVariant, with fontFeatureSettings last for last-wins.

    Evidence and reasoning

    JS adds fontFeatureSettings in StyleSheetTypes. C++ BaseTextProps parses it and conversions.h composes and serializes it under TA_KEY 32. Android TextAttributeProps and TextLayoutManager read and apply it. iOS RCTEffectiveFontFromTextAttributes copies fontVariant and fontVariationSettings into RCTFontProperties but never reads fontFeatureSettings. A grep for feature settings under platform/ios finds no other consumer. The shared example claims the same rows render on both platforms, so the same style diverges by platform.

  • Oblique angle type has no native parserpackages/react-native/Libraries/StyleSheet/StyleSheetTypes.js:1021 (correctness) · id:1f2a0d800516
    Confidence: High — traced the new type to both native fontStyle parsers.
    Impact if shipped: Medium — an angled oblique value loses its slant on both platforms.
    Suggested remediation: Parse the oblique prefix in C++ and Android and map it to Oblique, or narrow the JS type until the angle has native support.

    Evidence and reasoning

    The JS type admits oblique with an angle such as oblique 20deg. The C++ fromRawValue accepts only the exact strings normal, italic, and oblique and maps anything else to Normal with an error. The Android parseFontStyle accepts only italic and normal and maps anything else to UNSET. No code parses the angle. An author who uses the documented angle form gets Normal on C++ Fabric and inherited style on legacy Android.


This review is advisory — it never blocks a merge and never auto-approves.

@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

3 similar comments
@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

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. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant