Because View Transitions animate snapshots, you can morph any element into another element. But for some type of transitions, the use of snapshots can sometimes work against you. For example: when the border-radius
changes between the old and the new snapshot, you most likely want the border-radius
to nicely animate from the old to the new state instead of seeing two snapshots fade.
~
🌟 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 Problem
The problem can be seen in the following demo that morphs a .card
using a View Transition. The .card
has a view-transition-name
and animates its border-radius
from 0.25rem
to 3rem
and back, amongst a few other properties that change along with it (such as the font-size
and aspect-ratio
).
See the Pen
View Transitions with a Border Radius (1/3 – Problem) by Bramus (@bramus)
on CodePen.
Pay close attention the corners of the .card
element as it transitions: because View Transitions fade snapshots, the border-radius
does not nicely animate but simply fades from the old state to the new state.
Note that the text inside the .card
also has a view-transition-name
set to it, so that it gets captured separately from the .card
itself. This will turn out to be key later on.
~
# The (partial) Solution
The solution to this problem is to manipulate the ::view-transition-group
which contains the snapshots. The thing you need to do is add an extra animation the ::view-transition-group
which performs the smooth animation you want. In my case, that is an animation of the border-radius
. Don’t forget to set the overflow
to clip
to make sure the snapshots don’t bleed out.
@keyframes adjust-border-radius {
from {
border-radius: 0.25rem;
}
to {
border-radius: 3rem;
}
}
::view-transition-group(card) {
animation-name: -ua-view-transition-group-anim-card, adjust-border-radius;
overflow: clip;
background: #ccc;
}
:active-view-transition-type(shrink)::view-transition-group(card) {
animation-direction: normal, reverse;
}
The adjust-border-radius
animation – which animates the border-radius
– gets added to the existing -ua-view-transition-group-anim-card
animation. The newly added animation gets reversed when the card shrinks, which is communicated from JS to CSS using View Transition Types.
See the Pen
View Transitions with a Border Radius (2/3 – Workaround) by Bramus (@bramus)
on CodePen.
~
# Dealing with changing backgrounds
In the previous demo I cheated a bit by duplicating the background-color
onto the group, this to cover up some inaccuracies. You can see these inaccuracies more clearly when the background-color of the .card
also changes. Pay close attention to the corners of the following demo: you can see it’s not 100% perfect.
See the Pen
View Transitions with a Border Radius (2/3 – Workaround, with random text) by Bramus (@bramus)
on CodePen.
(While at it, I updated the code to also change the text inside the card).
The solution here too is to move more properties that get animated onto the ::view-transition-group
. In this case, it’s the background-color
.
To actually see the background animating as part of a transition, the snapshots that make up the card (but not its contents) need to be hidden while the transition runs. This can be done by setting the ::view-transition-image-pair(card)
to display: none;
. This can safely be done because of the fact that the text content of the card gets captured separately.
@keyframes adjust-group {
from {
background: #ccc;
border-radius: 0.25rem;
}
to {
background: lightblue;
border-radius: 3rem;
}
}
::view-transition-group(card) {
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;
}
See the Pen
View Transitions with a Border Radius (2/3 – Workaround, with random text + background animation –fixed) by Bramus (@bramus)
on CodePen.
If the snapshot with the foreground stuff of the element bleeds out of the snapshot with the background stuff, you also need to duplicate the border-radius
and clip
onto that snapshot’s ::view-transition-group
.
In the future Nested View Transition Groups will solve this. This feature is currently getting implemented in Chrome.
~
# Dealing with changing borders
A tricky thing to incorporate in the solution is animating borders. The trickiness comes from the fact that the snapshots are taken using the border-box
. This means that when duplicating a border onto the ::view-transition-group
you need to make sure its box-sizing
is set to border-box
, regardless of the box-sizing
of the snapshotted element.
With this set, you can safely duplicate the border onto the ::view-transition-group
and then animate it as part of its keyframes.
@keyframes adjust-group {
from {
border-radius: 0.25rem;
background: #ccc;
border-width: 2px;
}
to {
border-radius: 3rem;
background: lightblue;
border-width: 8px;
}
}
::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;
}
See the Pen
View Transitions with a Border Radius (2/3 – Workaround, with random text + border) by Bramus (@bramus)
on CodePen.
~
# In Summary
To smoothly animate things like borders as part of a View Transition, you need to duplicate that animation onto the ::view-transition-group
. For best effect, have the View Transition separately capture the background and foreground of the element you’re animating. This can be done by giving each a view-transition-name
.
~
# Spread the word
Feel free to reshare one of the following posts on social media to help spread the word:
~
🔥 Like what you see? Want to stay in the loop? Here's how: