Skip to content

Conversation

@axyz
Copy link

@axyz axyz commented Nov 28, 2025

Close #3

This is a POC to polyfill CSS animations using css.keyframes.

I used the implementation of transitions as a rough guideline, as well as trying to adhere to (part) of the CSS specs. LLMs were extensively used to fill the gaps and add extensive testing, I went trough many iterations until the code worked correctly and looked good to me in terms of structure and overall architecture, but it definitely needs some extra pairs of eyes.

In particular:

  • I'm not very confident on using Flow types and I'm sure some things can be simplified (e.g. those safety wrappers and conversions to make the typechecker happy)
  • I do not have a backround in native development and not sure if the way animations are created and handled using the Animated API is idiomatic and performant in all its aspects
  • I'm not 100% confident on the logic to create (or recreate) Animated.View element based on style change. Adding or removing a class can lead to switching from animated to not animated with possible performance costs, should the usage of dynamic styles be recommended instead (e.g. operating on the playState rather than using react state for the whole animation state)?
  • running the whole test suites freezes my machine and I have to restart (also on main), so I'm not able to run it locally and have to select specific tests individually. I suspect the repo is mostly developed on mac machines (I use linux), and may have to do with how v8 garbage collection differs (kind of a known issue with jest). '--runInBands' somehow helps, but still runs out of memory, may be worth explore how to split and handle memory cleanup better on the very large css-create test that seems to be the culprit

On a high level the polyfill works like this:

  • css.keyframes calls store keyframes as js objects in a registry, and a unique id is returned
  • useStyleProp calls useStyleAnimation (similar approach as for transitions) and check if the style gets changed in any way by the hook and if true uses an Animated.View
  • useStyleAnimation parses animation attributes and gets the keyframe on animationName from the registry, then translates these properties to Animated APIs and returns the Animated values to be used in the style of the Animated View
  • an AnimationController is used as in between layer to easily handle the lifecycle of the animation
  • likely need to extend the test suite, not sure if covering all the edge cases of css specs

What is covered

  • css.keyframes()
  • 'animationName', 'animationDuration', 'animationDelay', 'animationTimingFunction', 'animationIterationCount', 'animationDirection', 'animationFillMode', 'animationPlayState' CSS properties

I added some examples in the platform-tests. Currently I was only able to test on android.

What is missing

  • 'animation' shorthand (may be tricky to parse, haven't seen examples on stylex, but I assume on web is just passed through, but not sure if is handled in the merge logic)
  • multiple animations. (does stylex supports it? Would it be an array of keyframe objects?)
  • Web animation API. (could be a next separate step, having the animation controller and the keyframe registry should make it possible to add the animate() method to elements refs, but would need to check the full specs
  • documentation update

Sorry for the size and if I missed any LLM's slop while reviewing it and iterating on it. I mostly wanted to validate the approach and get an early feedback.

@axyz axyz requested a review from necolas as a code owner November 28, 2025 11:23
@meta-cla meta-cla bot added the cla signed label Nov 28, 2025
* Generate a unique identifier for keyframes
*/
_generateUniqueId(): string {
return `keyframe_${++this._idCounter}`;
Copy link
Author

Choose a reason for hiding this comment

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

is this good enough? Or is there a way we could access the hash generated by stylex itself? But not sure if that would have any advantage, maybe for debugging?

@necolas
Copy link
Contributor

necolas commented Dec 5, 2025

Adding or removing a class can lead to switching from animated to not animated with possible performance costs

We might have some logic in the CSS transitions polyfill to prevent this happening for that API polyfill.

running the whole test suites freezes my machine

Sounds like this might be a problem with the implementation or tests

animation shorthand

That's ok, we don't support the shorthand animation property

multiple animations

Yes, an array of keyframes

Web animation API

That would be cool, I had the same thought. However, we might be better off trying to accelerate this being added to React Native. Totally ok not to include it here

@axyz
Copy link
Author

axyz commented Dec 8, 2025

@necolas thanks for taking a look

I'll be away during holidays, so the PR may remain open for a while, but would like to summarize the next steps to move forward.

Must have to merge

  • handling keyframe arrays (and duration and other properties as arrays)
  • update documentation
  • verify if swapping from View to Animated.View happens only when strictly necessary
  • double check and verify if test issues on linux exist also on main or are introduced by the PR. Also extra pair of eye on overall test coverage

Out of scope or possible follow up PRs

  • support for shorthands (not needed unless supported in stylex)
  • support for web aimation API (to be aligned with possible implementation in RN core)

Nice to have, but not a blocker

The API and the implementation is very close to the new CSS based API of reanimated (see: https://github.com/software-mansion/react-native-reanimated/tree/main/packages/react-native-reanimated/src/css ). While for RSD it makes more sense to use Animated and rely on it being optimized on RN core side, it would be nice to be able to share some of the logic and evaluate if at least the parsing logic could be extracted (or even more, but not sure to what extent Animated and Reanimated are exchangeable API wise) cc @MatiPl01 @tomekzaw

It would also be great if while waiting for the C++ version of Animated to become available, consumers could swap Animated with Reanimated (for both css animations and transitions) by using custom node resolution (assuming the API is the same, but realistically may be hard)

@necolas
Copy link
Contributor

necolas commented Dec 8, 2025

Sounds good! The C++ Animated will available in OSS RN early next year. We should then push for CSS transitions/animations implementation to be built into RN itself so we can delete these polyfills altogether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(feature) css.keyframes() and CSS animations

2 participants