Blog

CSS New features View transitions

Being lazy with view-transition-old and -new

July 13, 2025 by Cyd Stumpel Reading time: 4 minutes

One of the most important lessons you can learn as a developer is that being lazy is often a good thing. Students sometimes laugh sheepishly at me when I tell them that, but especially if it leads to writing less (or less complicated) code, being lazy is smart.

Link to:

A short intro to View Transitions

View Transitions let you animate between two DOM states (or even between pages) using CSS keyframes. If you need a more in-depth introduction, have a look at my earlier blog or the second half of my Beyond Tellerrand talk.

Link to:

The anatomy of a view transition

If you open DevTools in Chrome and inspect a View Transition, you’ll see a structure like this:

::view-transition
  ::view-transition-group(root)
    ::view-transition-image-pair(root)
      ::view-transition-old(root)
      ::view-transition-new(root)
HTML

A quick breakdown of each part:

  1. ::view-transition
    All named view transitions 1 will end up in this parent pseudo element
  2. ::view-transition-group
    Each named view transition element will get its own view transition group, ‘root’ will be equal to the given view-transition-name. In the vt-group all transforms are automatically calculated.
  3. ::view-transition-image-pair
    This element is set to isolation: isolate so the default blend modes animation of the old and new view transitions don’t mix with anything but eachother.
  4. ::view-transition-old and ::view-transition-new
    These represent, as you hopefully already expected, a snapshot of the old and new state of the named element.
Link to:

Being lazy with view-transition-old and view-transition-new

The -old and -new state of a view transition can be very useful, it’s easiest to explain with an example of filtering and sorting.

Link to:

Sorting

If you start a View Transition while changing the DOM order of items (e.g. sorting a list), the browser will automatically animate them to their new position with the help of the calculations in the view-transition-group.

Because the items exist both before and after the DOM change, they will each have a ::view-transition-old and ::view-transition-new state.

Sorting items (The old state is represented by the red rectangle, the new state by the green rectangle)
Link to:

Filtering

If you’re filtering items out of a list, the ones that disappear exist in the old state but not in the new one. They will only have a ::view-transition-old state.

Filtering items out (The old state is represented by the red rectangle, the new state by the green rectangle)

If you add items back with the same filter, the opposite is true. They only exist in the new state, so they’ll only have a ::view-transition-new.

Filtering items in (The old state is represented by the red rectangle, the new state by the green rectangle)

This might sound useless at first (it did to me), but with CSS we can check if there is only a view transition old or new state and style based on that.

::view-transition-old(.filter-item):only-child {
  animation-name: animate-out;
}

::view-transition-new(.filter-item):only-child {
  animation-name: animate-in;
}
CSS

This means you can create custom -in and -out animations for items in for example:

See the Pen View transitions – CSS only by Cyd Stumpel (@Sidstumple) on CodePen.

Link to:

Using it between pages

You can take this one step further and use it to help you animate between overview and detail pages. That’s what I did when building this website for my CSS Day talk. This way you can create smooth transitions between different pages with minimal extra work.

When going from an overview with speakers to a detail page for example, only the speaker that is clicked will have an old and a new state, the other speakers will only have an old state. When going from a detail page back to an overview only the speaker that was active will have both states. It would be even more useful if the :has selector would work on pseudo elements because then we could also adjust the stacking order of the parent group to make sure it is in front of all the other elements.

It doesn’t look like browsers will implement :has on pseudo elements: *

“Pseudo-elements are also not valid selectors within :has() and pseudo-elements are not valid anchors for :has(). This is because many pseudo-elements exist conditionally based on the styling of their ancestors and allowing these to be queried by :has() can introduce cyclic querying.”

MDN

::view-transition-group(.filter-item):has(::view-transition-old(.filter-item):only-child) {
  z-index: 1; /* Won't work 🙁 */
}
CSS

Using delays can help you avoid stacking the items on top of each other, or you could give the active item a different view transition class on click with JavaScript and use that to give the group a higher stacking context.

1 When I talk about named view transitions or named elements I mean elements that have a view-transition-name.

Cyd Stumpel

Cyd is a Freelance Creative Developer and teacher from Amsterdam. She teaches at the Amsterdam University of Applied Sciences and occastionally speaks at conferences and meetups.

Update: An issue has been opened on the CSS Working Group, we might be able to do this in the future 🤩 https://github.com/w3c/csswg-drafts/issues/12630

Last updated: September 18, 2025

15 Webmentions

Join the conversation on Bluesky Bluesky Mastodon Mastodon

Replies 2

Chris Coyier Mastodon Mastodon

Chris Coyier@chriscoyier

7 months ago on Mastodon

@Cyd These posts are great. I remember from your talk that you used `:only-child` with view transition stuff in CSS and it was related to like “incoming only” or “outgoing only” transitions right? That syntax wasn’t intuitive to me, you got a post on that?

…. errrrr wait that’s literally what this is, isn’t it

Cyd Mastodon Mastodon

Cyd@Cyd

7 months ago on Mastodon

@chriscoyier hahaha yes this is exactly that, view transitions are a whole new mental model and so abstract, took me a while to get used to, too

Reposts 4

Stefan Matei Bluesky Bluesky

Stefan Matei

View source
Krijn Hoetmer Mastodon Mastodon

Krijn Hoetmer

View source
Wenzel ‍ Mastodon Mastodon

Wenzel ‍

View source
David O'Brien Mastodon Mastodon

David O’Brien

View source

Webmentions are a way to connect with other people who have shared your work.