Prevent clipping issues (and more) in View Transitions by using Nested View Transition Groups

Screenshot of an ongoing View Transition in Chrome with DevTools open. It’s using Nested View Transition Groups to make sure the text inside the card is animating along with the card and gets properly clipped by the card.

A new View Transitions-related feature we shipped in Chrome 140 is the ability to nest ::view-transition-group() pseudos. This is useful for retaining visual effects such as clipping, move elements as part of a group, etc.

~

In the following demo, you can add/remove cards to/from a list. The animations are handled by View Transitions.

See the Pen
Add/Remove Cards with View Transitions in scroller (with nested view transition groups)
by web.dev (@web-dot-dev)
on CodePen.

While the View Transition runs, the cards already in the list should remain clipped by the scroller. This can be achieved by using Nested View Transition Groups, which allow you to place the snapshots of each individual card inside the snapshot of the cards wrapper. The card that gets added/removed does not need to be nested inside that wrapper, because that one doesn’t get clipped by the cards wrapper.

Not using Chrome 140? Here’s a recording of the demo, with view-transition-group: contain first off and then on:

Pay close attention to the left of the scroller: without Nested View Transition Groups, the individual cards bleed out of the scroller during the transition.

The CSS to make sure the individual cards remain clipped by the scroller during the View Transition this is the following:

.cards {
	view-transition-name: cards;
	view-transition-group: contain;
}

.card {
	view-transition-name: match-element;
	view-transition-class: card;
}

#the-card-that-gets-added-or-removed {
	view-transition-name: the-card-that-gets-added-or-removed;
	view-transition-group: none;
}

The result is a pseudo-tree that looks like this, with the ::view-transition-group(*.card) pseudos of the individual cards nested inside the ::view-transition-group(cards). They are held together using the new ::view-transition-group-children(…) pseudo.

html
  ├─ ::view-transition
  │  ├─ ::view-transition-group(cards)
  │  │  ├─ ::view-transition-image-pair(cards)
  │  │  │  ├─ ::view-transition-old(cards)
  │  │  │  └─ ::view-transition-new(cards)
  │  │  └─::view-transition-group-children(cards)
  │  │    ├─ ::view-transition-group(card1.card)
  │  │    │  └─ ::view-transition-image-pair(card1.card)
  │  │    │     ├─ ::view-transition-old(card1.card)
  │  │    │     └─ ::view-transition-new(card1.card)
  │  │    ├─ ::view-transition-group(card2.card)
  │  │    │  └─ ::view-transition-image-pair(card2.card)
  │  │    │     ├─ ::view-transition-old(card2.card)
  │  │    │     └─ ::view-transition-new(card2.card)
  │  │    └─ ::view-transition-group(card3.card)
  │  │       └─ ::view-transition-image-pair(card3.card)
  │  │          ├─ ::view-transition-old(card3.card)
  │  │          └─ ::view-transition-new(card3.card)
  │  └─ ::view-transition-group(the-card-that-gets-added-or-removed)
  │     └─ ::view-transition-image-pair(the-card-that-gets-added-or-removed)
  │        ├─ ::view-transition-old(the-card-that-gets-added-or-removed)
  │        └─ ::view-transition-new(the-card-that-gets-added-or-removed)
  ├─ head
  └─ body
        └─ …

By setting overflow: clip on the new ::view-transition-group-children(.cards) pseudo, the individual card snapshots properly get clipped (without affecting the ::view-transition-group(the-card-that-gets-added-or-removed) one).

::view-transition-group-children(.cards) {
	overflow: clip;
}

Note that clipping can also be achieved by using Scoped View Transitions. That approach would not work for this demo because of the #the-card-that-gets-added-or-removed that is allowed to bleed out of the scroller.

~

Nested View Transition Groups can do more than just clipping. Take this demo in which the paragraphs animate along with the card (but the avatar/name do not):

See the Pen
Nested View Transition Groups: People (4/4)
by web.dev (@web-dot-dev)
on CodePen.

To achieve this, Nested View Transitions Groups are used to make sure the ::view-transition-group(.text) pseudos get nested inside the ::view-transition-group(card) pseudo.

Not using Chrome 140? Here’s a recording of the demo, with view-transition-group: contain first off and then on:

Notice how with Nested View Transition Groups, the text animate along with the card – including a (obnoxious) 3D rotation.

~

To learn all about Nested View Transition Groups, go read the guide I wrote for developer.chrome.com: Prevent clipping issues (and more) in View Transitions by using Nested View Transition Groups →.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.