Skip to content

feat: calc support#56162

Open
paradowstack wants to merge 5 commits intofacebook:mainfrom
paradowstack:feat/calc
Open

feat: calc support#56162
paradowstack wants to merge 5 commits intofacebook:mainfrom
paradowstack:feat/calc

Conversation

@paradowstack
Copy link

Why?

calc() is a core CSS primitive and a common expectation for developers. Adding it to React Native enables more expressive and maintainable styles without JS-side manual calculations. It improves parity across web and native worlds, reducing friction when switching from another platform.

My tweet about this potential feature in React Native was really well received and many people expressed excitement about it.

Yoga PR link

Summary:

Bring Yoga dynamic value resolution into React Native and wire calc()-driven values.

Yoga resolves dynamic values during layout by calling back into React Native with layout context and dynamic id, allowing mixed-unit expressions to be evaluated just-in-time.

Changelog:

[GENERAL] [ADDED] - CSS calc() support

This PR includes RN-side integration needed to make Yoga dynamic values usable in renderer layout paths.

  • Wire Yoga dynamic callback resolution to RN calc expression storage and lookup.
  • Apply dynamic calc resolution for layout properties: dimensions, min/max, flex-basis, gap, position, margin, and padding.
  • Add viewport-aware resolution flow for vw/vh-based expressions.

Missing:

To keep this PR focused, some areas are intentionally out of scope and will follow in separate work.

  • Missing calc support for other CSS properties areas:
    • borders & outlines
    • transforms
    • typography
  • Missing calc support for transform-related layout properties.
  • Missing full JNI/Java/JS API surface completion.

Test Plan:

Added focused C++ tests for calc parsing/evaluation.

Broader platform and API-level testing will be expanded in follow-up PRs.

cc @NickGerleman

@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 Mar 19, 2026
@paradowstack paradowstack marked this pull request as ready for review March 19, 2026 19:15
@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 Mar 19, 2026
REBUILD_YG_FIELD_SWITCH_CASE_INDEXED( \
position, setPosition, yoga::Edge::All, "inset");

#define APPLY_CALC_COMMON(fieldName, setPoints, setPercent, setDynamic) \
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: why does this need to be a macro?

Copy link
Author

Choose a reason for hiding this comment

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

It does not - I wrote it as macros because this file already achieve similar functionality using macros (REBUILD_FIELD_YG_* stuff) and I got the feeling it would be more consistent.
Would you like me to rewrite it using template functions?

/*
* Viewport size is size of the React Native's root view.
*/
Size viewportSize{};
Copy link
Contributor

Choose a reason for hiding this comment

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

Ooh, this may be helpful in more places in the future 👍. If you had a PR for just the plumbing for e.g. this part of the change, would be quick to import and accept.

Copy link
Author

Choose a reason for hiding this comment

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

I extracted it to the separated PR: #56209

}

TEST(CSSCalc, simple_pixel_value) {
auto result = parseCalc("calc(10px)");
Copy link
Contributor

@NickGerleman NickGerleman Mar 24, 2026

Choose a reason for hiding this comment

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

I'm trying to figure out how CSSCalc works, more generically.

Ideally, any place where we use a specific CSS type, valid calc expression should work. So e.g. if we are parsing the result of a box-shadow, which expects a <length> in the middle, that length should be able to be some arbitrary calc() expression.

Like 0 calc(16px - 2px) should be valid, for reading something that requires two lengths.

Can we make that sort of scenario work? If we can do this sort of more generic substitution, it also gives us a way later to introduce variables.

Comment on lines +287 to +297
auto opResult =
parser.syntaxParser().consumeComponentValue<std::optional<char>>(
CSSDelimiter::None, [](const CSSPreservedToken &token) {
if (token.type() == CSSTokenType::Delim) {
auto sv = token.stringValue();
if (!sv.empty() && (sv[0] == '+' || sv[0] == '-')) {
return std::optional<char>{sv[0]};
}
}
return std::optional<char>{};
});
Copy link
Contributor

@NickGerleman NickGerleman Mar 24, 2026

Choose a reason for hiding this comment

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

IIRC these aren't actually Delim tokens in the spec, but CSSTokenizer, didn't add any of the tokens for calc(), calc-constants, etc. This change will need to update tokenizer layer a bit. Adding tokenizer awareness of calc tokens, could be its own change, that would be easy to merge independently.

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. p: Callstack Partner: Callstack Partner 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.

2 participants