Add core Yoga support for calc()#1874
Conversation
|
@paradowstack is attempting to deploy a commit to the Meta Open Source Team on Vercel. A member of the Team first needs to authorize it. |
|
Still catching up on a lot that happened while I was away, but it’s exciting to see new PRs like this! When I had looked at to same problem historically, I had a couple intuitions: Previous CompactFloat for styles didn’t allow representing enough information, why we have the StyleValueHandle and pool here The styling system should not live in Yoga. As calc() should be supported for more than just layout (eg for animating transforms). This lets the layout interface, rely on relatively simple point structure, but the consumer must be able to provide context for Yoga to be able to evaluate calc(). My original intention was to add a YGValueCallback style interface, that may be called for just-in-time evaluation of what a style resolves to in the host, given the context Yoga has, like current layout constraints, or the dimensions of the hypothetical containing block. So YGValueCallback (YGValueDynamic?) could call back to “host”, with the current layout constraints, information about the containing block maybe, for styles which must be dynamically evaluated. That host, with more context, on what a full unit system, screen sizes, inheritance, etc means could then use Yogas context to evaluate something complicated, involving viewport units, percentages of references from Yoga, etc. I started some work here on the React Native side in /react/renderer/css, but I’m not sure if I will be able to come back to it (currently trying to get some RN Text architecture changes our the door, then not sure what). At one point I had thought this sort of thing would be better interface for Yoga in general. Eg in React Native, we have separate sources of all this style information already, and spend a lot of time trying to creatively maintain separate Yoga and ShadowNode state. |
|
In general, I also wanted to move Yogas interface, or at least its internals, closer to “computer style”, where host performs resolution ahead of time. Yoga spends a silly amount of time doing that dynamically, because its most internal interface is before resolution. |
|
Thanks so much @NickGerleman for your input! It totally make sense to separate Yoga from styling system, therefore keep I have some follow-up questions to be sure we are on the same page:
Thanks in advance! |
|
I think it would be easier to store the expression on the RN side. According to CSS spec, we have a phase during evaluation where we simplify as much as possible, then evaluate anything dynamic, but IDK what it looks like in practice. Re callback, I was originally planning to look at the callback to be per style value, instead of per node. IIRC with StyleValueHandle, we could place a 64 bit pointer in the value pool? Then I'm guessing we would need context from Yoga node in callback as well. The idea is, for simple cases, RN can do what it is already doing, but when it has a dynamic value that cannot be fully simplified ahead of time, it sets callback/dynamic style, which will hook into RN to evaluate. |
|
Great - thanks for the answers. Generally I like this approach. I've already made attempts to implement some parts of it and succeeded. I don't have any more open questions right now. I will make changes to this PR and, at the same time open a PR to RN repo in order to more clearly see, how these two interacts with each other. I am taking some vacations in the following weeks, so I'm not sure about the timeline, but I will definitely keep you updated! 😊 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Vacation period has ended and I pushed the update to this PR. This implementation includes just-in-time evaluation by calling back to the host - new I am looking forward for your feedback! |
yoga/YGValue.h
Outdated
| /** | ||
| * Callback + identifier pair for internal storage. | ||
| */ | ||
| struct YGValueDynamicData { |
There was a problem hiding this comment.
This probably shouldn't be in YGValue.h
There was a problem hiding this comment.
I have moved this out of this file to StyleLength.h and StyleSizeLength.h
yoga/YGValue.h
Outdated
| /** | ||
| * Host-defined identifier for a dynamic style value. | ||
| */ | ||
| typedef uint8_t YGValueDynamicID; |
There was a problem hiding this comment.
I looked at how this is used in facebook/react-native#56162
"List of all the properties in the world" tends not to scale well.
I wondered, could we use something like void*, then point directly to host native structure?
There was a problem hiding this comment.
I think that using void* would lead to problems when handling cloned properties - that's why I introduced enum as a key - to properly identify and overwrite property with the new value. But I agree that "list of all properties" is not the best solution. That's I have changed implementation and the new solution uses fnv1a hashed value of property name - I think it's cleaner.
NickGerleman
left a comment
There was a problem hiding this comment.
Could we get some unit tests for this?
Also, sanity checking yoga microbenchmark.
Overall, looks good though!
|
Any optimization ideas you have for Some stuff like this is hard to make 100% pay-for-play, but it starts to really add. E.g. Note that, layout time is more important than tree creation time. I suspect that is slow, because it includes expensive JSON parsing code inside of it (I really ought to see if Claude can fix that...) |
Please take a look at this: #1922. Idea behind it is very simple but it does the trick. However I am aware it's not the cleanest optimization - I am happy to hear your thoughts. |

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.
RN PR link
Summary
This PR intentionally focuses on core engine behaviour and keeps binding/JNI/Java/JS/API completion separate to keep this PR relatively small. I can try to split it even further, if the size will turn out to be too big.
Add core Yoga support for host-owned dynamic style values (including
calc()), resolved just-in-time during layout via callback into React Native.Yoga does not evaluate expressions in its style system. It asks the host to resolve a value using Yoga layout context (node + context + callback id).
Scope
This PR includes core C++ changes for dynamic value storage, callback plumbing, and layout-time resolution.
YGValueCallback+YGValueCallbackIDinterface for host-evaluated dynamic values.StyleLength/StyleSizeLengthto represent dynamic values and resolve them through callback during layout.StyleValuePool,StyleValueHandle) to store/retrieve callback pointer + id efficiently.Future Scope
Follow-up PRs will finalise work on this feature. Below is the list of missing scopes:
Testing
Tests will be delivered in separate PR.