I mentioned recently that I had tried Framer Motion, a React library for animations.
I used it in Operator Lookup ( https://joshwcomeau.com/operator-lookup/), a project I launched earlier this week.
In this thread, I'll share some of the tricks I learned to create animations like this
I used it in Operator Lookup ( https://joshwcomeau.com/operator-lookup/), a project I launched earlier this week.

In this thread, I'll share some of the tricks I learned to create animations like this

Probably the most obvious animation on the whole project is the slide-up effect, shown in the above tweet.
There are two parts to it:
⢠The header fades out
⢠The text input slides up, to occupy this space.
Here's the code for it!
(view in text: https://pastebin.com/fTaeDwJK )
There are two parts to it:
⢠The header fades out
⢠The text input slides up, to occupy this space.
Here's the code for it!
(view in text: https://pastebin.com/fTaeDwJK )
The trick is, I'm unmounting the header when a search result is matched. I'm not just hiding it with CSS.
The `AnimatePresence` helper ( https://www.framer.com/api/motion/animate-presence/) allows you to specify "exit" animations. Here I'm fading it out before it gets removed from the DOM.
The `AnimatePresence` helper ( https://www.framer.com/api/motion/animate-presence/) allows you to specify "exit" animations. Here I'm fading it out before it gets removed from the DOM.
After the element finished fading out, there's a hole in the DOM. The search input will naturally move up to fill that space, but normally, it would happen instantly.
That's where <AnimateSharedLayout> ( https://www.framer.com/api/motion/animate-shared-layout/) comes in, which is honestly pure magic
That's where <AnimateSharedLayout> ( https://www.framer.com/api/motion/animate-shared-layout/) comes in, which is honestly pure magic

It uses a FLIP animation to figure out where this fella needs to move to, and then applies a transition to make it so. To "activate" it, all you need to do is set `layout={true}` to any descendant of `AnimateSharedLayout`.
This would be *such* a hard problem otherwise.
This would be *such* a hard problem otherwise.
In my snippet, you may have noticed:
layout={!prefersReducedMotion}
The idea here is that this layout animation doesn't happen for folks who have expressed a preference against motion.
I've written about this: https://joshwcomeau.com/tutorials/accessibility/
layout={!prefersReducedMotion}
The idea here is that this layout animation doesn't happen for folks who have expressed a preference against motion.
I've written about this: https://joshwcomeau.com/tutorials/accessibility/
I'm honestly kinda floored by how little code was required to do this animation. Animating layout shifts is a Hard Problem. I solved for it in React Flip Move ( https://github.com/joshwcomeau/react-flip-move), an animation library I created years ago.
It nearly destroyed my brain
It nearly destroyed my brain

One other neat animation in this project is the staggered fade on these suggestions.
This works thanks to the `staggerChildren` prop, https://www.framer.com/api/motion/types/#orchestration.staggerchildren
This works thanks to the `staggerChildren` prop, https://www.framer.com/api/motion/types/#orchestration.staggerchildren
View code in text: https://pastebin.com/C9FprFus
TBH, I haven't gone deep enough to 100% understand this stuff
so I may be slightly off in my explanation.
Each list item has an object matching a "variant", either hidden or visible. These are custom names I came up with.
TBH, I haven't gone deep enough to 100% understand this stuff

Each list item has an object matching a "variant", either hidden or visible. These are custom names I came up with.
The parent `ul` controls animating from the default `hidden` variant to the `visible` one, and it also includes "transition" information. The critical bit is this:
transition: {
when: 'beforeChildren',
staggerChildren: 0.02,
}
transition: {
when: 'beforeChildren',
staggerChildren: 0.02,
}
Every child's transition is 20ms after the previous one, which creates the nice domino effect. It also, counter-intuitively, seems to be a bit of a perf win! Because the animation itself is so quick, a smaller number of total transitions occur at any given moment.
Similarly, I use `prefersReducedMotion` to disable this animation when appropriate. I don't disable _all_ animationsâthere are still some subtle fadesâbut nothing that conveys a sense of movement, since that is what triggers certain vestibular disorders.
I've only scratched the surface with this library, but it's really neat. I'm excited to keep exploring!
Big thanks to @mattgperry and team, this library is a treasure trove
Big thanks to @mattgperry and team, this library is a treasure trove

(whoops â I meant to link directly to this article, for more info on Prefers Reduced Motion: https://joshwcomeau.com/react/prefers-reduced-motion/)