While I was trying to remember the name of this concept where a list of items uses animation to “open up” and make room for another list item to animate in, I thought of the name “Intuitive Transitions.”
The idea is that your brain understands exactly what has happened to this list. If the new list item was instantly there, it would be confusing and likely take some time to figure out what has changed. The animation makes it intuitive.
It’s certainly not my idea. I remember Val Head talking about this during her animations talks years ago. Then I remembered another name: “Transitional Interfaces”, which is what Pasquale D’Silva called them a ways back and I took the opportunity to code up. Mike Bostock would call it Object Constancy.
My old examples were jQuery .slideToggle()
based (which was capable of measuring how large something needed to animate to), but it was re-done years later by Luke Courtney in a more modern jQuery-free way as well.

This is all coming to mind again, because of new technology arriving in browsers. Straight up: the most interesting new API to play with in 2023 is the View Transitions API. Jake has the best article on it so far. It’s extremely cool that we get FLIP animations essentially “for free”, which includes animating elements to new unknown dimensions ✨.
What’s extra cool, is that the API is extremely simple to use (well, at first):
document.startViewTransition(() => {
// Just update the DOM
});
Code language: JavaScript (javascript)
That’s… it. You update the DOM and it will animate the changes. And if you want to control how those animations happen, you have that control in CSS:
/* Make the outgoing DOM go out fast, the incoming slow */
::view-transition-old(root) {
animation-duration: 0.5s;
}
::view-transition-new(root) {
animation-duration: 5s;
}
Code language: CSS (css)
The root
keyword there, as you might imagine, applies to the entire page. But you aren’t always animating the entire page. In the case of our list, we’re only animating the list items, so we need to do like…
<ul id="list">
<li style="view-transition-name: item-0">Apple</li>
<li style="view-transition-name: item-1">Banana</li>
<li style="view-transition-name: item-2">Guanabana</li>
</ul>
Code language: HTML, XML (xml)
To note there:
- See how they all have different
view-transition-name
s? That’s very important. Each element with a name will be animated separately. We can’t target the entire list, because the entire list doesn’t all move together. Some list might animate up, some might animate down, some might not move at all, etc. - If any element has the same
view-transition-name
, it breaks the entire thing, nothing animates. - This all feels highly annoying to me, as it means we’ll need to have generated
view-transition-name
s quite a bit and that makes it much harder to apply custom animations in CSS since we can’t write selectors like::view-transition-old(list-*)
or any similar wildcard approach.
But anyway, as they all have unique names now, as soon as we add another list item to the list dynamically, all the list items will automatically move to make room, which is awfully cool. No measuring things, no library needed, no exotic approach.
To belabor the annoyance a little…
Say the new list item is like…
<li style="view-transition-name: item-[DYNAMIC]">Guanabana</li>
Code language: HTML, XML (xml)
I can’t really target that with ::view-transition-new
in CSS because I don’t know what [DYNAMIC] is. Fortunately, for new/incoming elements, it’s not a terrible limitation because I can just add a class name (e.g. incoming
) and apply animation to that class. But I have no such luxury for old/outgoing elements. A class name to animate them away is not possible because the element is removed from the DOM immediately and an animation will not run, plus I still don’t know what [DYNAMIC] is on the CSS side. At best, I’d have to figure out [DYNAMIC] on the JS side and inject a CSS rule using ::view-transition-new([DYNAMIC])
just-in-time. Another possibility is changing the viewTransitionName
in JavaScript just in time to something you do know on the CSS side, but that is limiting because multiple elements might receive that at the same time based on user interactions and thus break the animations entirely.
Here’s a look at it all working. Remember: at the time of writing this is a very new API that has already changed dramatically once. It works in Chrome Canary only with viewTransition
API flag turned on.
Points:
- I’m using zero libraries here. None are required. That’s pretty cool.
- If you are using a DOM-manipulating library (React, Vue, etc) this will all still work fine, it just might be tricky because those libraries update the DOM on their own schedule. You’ll need to figure out where best to jack in the
startViewTransition
call. I gave it a shot in React but I’m afraid my React-foo is too weak to get it right. Plz fix thx. - It looks like we’re getting “animate to auto” functionality here. It doesn’t matter what size the new list item is, the perfect amount of room will be made. Will this be cracking the nut on getting that in regular CSS animations?
- The View Transitions API for on-page FLIP is friggin sweet, but pales in comparison to this API being used for cross-page animations. Like literally animations between full page reloads. That’s gonna be wild. I gotta try playing with that soon. I’ve already seen some compelling plays.
Special thanks to Adam for back and forthing with me on all this.
https://twitter.com/mattgperry/status/1615014958395424769