View Transitions Applied: Smoothly animating a border-radius with a View Transition, revisited

Instead of duplicating an animation on the ::view-transition-group pseudo, you can also rely on CSS transitions on the original element … if you’ve set it up correctly.

~

🌟 This post is about View Transitions. If you are not familiar with the basics of it, check out this 30-min talk of mine to get up to speed.

~

# The animation approach

In https://brm.us/view-transitions-border-radius I shared how you can nicely animate things like a border-radius with View Transitions. To recap, View Transitions animate snapshots and because of that things like a changing border won’t nicely animate but they will fade from the old to the new snapshot.

To work around that I suggested to replicate the border animation and put it onto the ::view-transition-group pseudo. For example, if the border-radius of your #card element changes from 0.25rem to 3rem, you’d create such an animation and apply it to the ::view-transition-group as follows:

@keyframes adjust-group {
	from {
		border-radius: 0.25rem;
	}
	to {
		border-radius: 3rem;
	}
}
::view-transition-group(card) {
	box-sizing: border-box;
	border: 2px solid black;
	animation-name: -ua-view-transition-group-anim-card, adjust-group;
}
:active-view-transition-type(shrink)::view-transition-group(card) {
	animation-direction: normal, reverse;
}
::view-transition-image-pair(card) {
	display: none;
}

Live demo (that animates more than only the border-radius):

See the Pen
View Transitions with a Border Radius (2/3 – Workaround, with random text + border)
by Bramus (@bramus)
on CodePen.

A key aspect to making this actually work, is that you have to individually capture the background part of the #card and the content/foreground part of it. It’s only the background part that you animate.

#card {
	view-transition-name: card;
}

#card > #card-content {
	view-transition-name: card-content;
}

Check out the following visualization which, upon hovering, shows the two layers that get captured. It’s the background layer that gets animated:

See the Pen
Untitled
by Bramus (@bramus)
on CodePen.

~

# A simpler approach

On BlueSky, Martin Trap – from “The Bag of Tricks for View Transitions”let me know that there is a simpler approach.

When such things get too complicated for me, I usually drop the old image along with the new one’s entry animation and rely on the fact that the new image is a live representation of the original element.

That way, I can simply use CSS transitions on the original element during the #ViewTransitions morph and be done with it.

So instead of creating an animation on the ::view-transition-group pseudo, Martin’s approach is to show only the new state of the box – through the ::view-transition-new pseudo – and rely on CSS transitions to have it transition the border with a duration that is set to the same duration + timing-function as the View Transition. Nice!

In code, it becomes this:

#card {
	view-transition-name: card;
	transition: all 2s ease; /* Same duration and easing as the View Transition */
}

#card > #card-content {
	view-transition-name: card-content;
}

::view-transition-group(card) {
	animation-duration: 2s;
	animation-timing-function: ease;
}
::view-transition-old(card) {
	display: none;
}
::view-transition-new(card) {
	animation: none;
	width: 100%;
	height: 100%;
}

Here’s the demo that Martin shared:

See the Pen
View Transitions with a Border Radius (original by Bramus)
by Martin Trapp (@martrapp)
on CodePen.

This approach too requires you to decompose the element into a background (which you animate) and a foreground layer. What you can drop from the animation-based approach is the extra logic to determine the type for the View Transition + the CSS to reverse the animation that goes along with it.

One (minor) nit with Martin’s code is that instead of transitioning all, I’d transition only the properties that need to transition. The downside is that it can result in quite a big list, such as for this demo:

#card {
	transition-property: border, background-color, border-radius, aspect-ratio, font-size; /* Yeah, that’s a lot of properties … */
	transition-duration: 2s;
	transition-timing-function: ease;
}

Thanks for sharing your ideas, Martin!

~

🔥 Like what you see? Want to stay in the loop? Here's how:

I can also be found on 𝕏 Twitter and 🐘 Mastodon but only post there sporadically.

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.