View transitions in the real world

Introduction and Overview of View Transitions

Elly Loel introduces herself and provides a quick self-description for accessibility purposes. She discusses the topic of her presentation: view transitions. Elly defines view transitions, provides examples, and explains their importance in creating engaging user experiences and impressing stakeholders with visual impact. She emphasizes the balance needed in animations to enhance interactivity without overwhelming users.

Developer Experience with View Transitions

This section discusses the developer's perspective on using view transitions. Elly explains how the View Transition API simplifies creating animations by handling complexity and removing the need for animation libraries. She highlights how the API aligns with modern web standards and allows for lightweight, maintainable code without dependency issues.

Mechanics of View Transitions

Elly delves into the technical implementation of view transitions, differentiating between "same document" and "cross document" transitions. She describes how browser support for these transitions is designed for progressive enhancement and provides insights into the rendering process, capturing of old and new states, and the animation workflow.

Programming View Transitions

Elly discusses how to programmatically trigger view transitions, detailing the differences between programmatic and navigation-based triggers. She explains the use of pseudo elements in view transitions and the significance of named view transitions for customizing animations.

Customizing and Enhancing View Transitions

In this section, Elly elaborates on customizing view transitions, including adjusting animations and implementing view transitions across different content states. She provides detailed recommendations for tailoring these transitions to improve user engagement while maintaining clarity and usability.

Managing Complex View Transitions

Elly provides insights into handling more complex view transitions, focusing on best practices for animations and how specific use-cases, such as images and videos, should be handled for optimal user experience. She highlights the importance of using JavaScript sparingly and leveraging native browser capabilities.

Advanced Concepts and Event Handling

This section covers the use of events and types in customizing view transitions. Elly discusses various events related to page load and navigation, and demonstrates how they can be used to create seamless, engaging user interactions in web applications.

Accessibility Considerations and Benefits

Elly emphasizes the importance of accessibility in web design, discussing how view transitions should be designed with cognitive load, motion sensitivity, and visual clarity in mind. She stresses the positive impact of animations on user experience, if implemented thoughtfully, and how they can enhance accessibility beyond mere aesthetics.

Conclusion: Importance of View Transitions

In her conclusion, Elly reaffirms the value of view transitions in improving user experiences, urging developers to incorporate them into their projects. She invites attendees to leverage these tools to enhance the intuitiveness and engagement of their web applications.

Thank you.

Hi, I'm Elly Loel.

My pronouns are she, her, and a quick self description for any blind or low vision attendees.

I'm a tall white trans woman with blonde hair.

QR code.

If you want to get the slides on your own device, follow along, scan the QR code or the link is Elly dot to slash wdds24 dash slides.

Filling time so you can scan the QR codes.

Cool.

Okay.

Today I'm going to be talking to you about view transitions.

We're going to explore the technical implementation of both same document and cross document view transitions through some real world examples.

And we're also going to focus on best practices for ensuring accessibility, enhancing usability, and improving the overall experience.

So what is a view transition?

You probably have a bit of an idea in your head.

You might've heard about them, but let's have a quick look.

This is a link to another page and that is a view transition.

If it was working, classic, trust me, like everyone else has been saying, we also have, another view transition here, and this one is the same document view transition.

So if we click the button pops up in transitions in.

And transitions out.

At least that one is functioning.

Why should we use View Transitions?

Why should we care about View Transitions?

Because of our users.

We want them to have a good experience.

Animations are inherently engaging.

Seamless transitions make users feel like the app cares about their experiences, fostering a sense of polish and satisfaction.

And as we all know, non technical stakeholders love this stuff.

The visual impact really resonates with them, strongly as it's something that they can point to.

It's hard to miss compared to other technical things that might not be as visible.

Smooth transitions help to make new content from a page navigation or state change more obvious.

Instead of static jumps or flashes, transitions guide users visually, making apps feel more performant and intuitive.

Even if the underlying speed hasn't actually changed.

People love and hate animations.

Without animations, websites can feel robotic and glitchy, but when they're overdone, they can be frustrating, or they can even hurt users, if it takes too long, if there's too much motion, if it's distracting, etc.

When thoughtfully implemented though, animations make interactions, and thus the website itself feel grounded in reality, like the website's a natural extension of the physical world.

Developer Experience Nowhere near as important as user experience, but still important in terms of getting things done.

With view transitions, creating these animations is straightforward.

Developers no longer have to wrangle all of the positioning and motion logic.

The platform handles the complexity so that you don't have to.

We also don't have to worry about dependencies.

Historically, implementing animations required libraries like GSAP or Framer Motion, though it's not called Framer Motion anymore, it's re branded.

The View Transition API removes the need for any dependencies, reducing project complexity and load times, while aligning with modern web standards, which we've been hearing about non stop over the past couple of days.

It's also lightweight and maintainable.

It's easy to implement and doesn't balloon your project's bundle size.

Developers can focus on implementing the animations instead of dealing with dependency conflicts.

The API abstracts away much of the heavy lifting, so handling key parts of animations, that used to have to be implemented using dependencies or manually by yourself, that required lots of tedious work is now handled by the platform.

The easy stuff is really easy.

The hard stuff is reasonably hard, but not unreasonably so.

How do you do this?

There are two different types, like I mentioned earlier, of view transitions.

Same document and cross document.

A same document view transition.

These are often referred to as SPA view transitions, but that's really just a misnomer.

It's just because they're activated with JavaScript.

Doesn't mean they're only for SPAs.

They can be used on any website, regardless of if it's an SPA or not.

Browser support actually doesn't matter.

This is like the poster child for progressive enhancement.

You lose zero functionality if the transition aren't supported because it's just a transition.

You get a better experience if they are, but if they're not, you don't have to worry.

You can see here for this same document view transitions, you can put a conditional on whether the startViewTransition method is available on the document object.

And if it is, run your update callback inside of that.

Or if not, you can just run it without the view transition.

It's been in Chrome for over one and a half, almost coming up on two years.

It's currently being worked on by the Firefox team.

And it's been in Safari for about two months.

Like I said though, Browse support doesn't matter.

Okay, so here we've got a bit of a video showcasing this.

You can pull up the animations drawer and pause animations and scrub through the animation.

This is really helpful for when you're debugging and you can play the animations back at a slower speed so you're able to see what's going on and debug that.

You can also, when paused the animations, you can view the pseudo elements for the view transition and we'll get more into exactly what they are later.

You can view the styles on them and see what exactly is creating those view transitions.

Cross document view transitions.

You might have heard of this meta tag before.

Don't use this.

That was experimental.

These days it's been deprecated.

The correct way to do it now is with the CSS at rule.

At view transition navigation auto.

Again, browser support doesn't matter.

Just to let you know.

It's been in Chrome for almost six months.

It's not yet in Firefox, but they'll likely be working on it next year.

Their standards position on it is positive.

And it's currently being worked on by the Safari team.

So if you have a look at another video.

This is, the animation that was meant to be happening earlier, but did not, we can look at the pseudo elements, like I mentioned before.

And if you look at the URL bar up there, you can see that it's changed.

From the previous page even though it was still seeing the previous page and now we can scrub through and see that animation So we'll get into the specifics of how this all works now because it can be a bit complicated Speaking of complicated that's a lot.

So we'll have a look at a visualization to better explain this So if we come over here We have the main DOM, which is the DOM of the page.

The transition root, which is, the pseudo elements for the view transitions.

And then on the right side we've got the, what the user is seeing.

So currently they are seeing the main DOM.

The user initiate, or the developer initiates the, view transition with the document.

StartViewTransition method.

And then the current state is captured as the old state.

Rendering gets paused, so what the user is seeing is now paused.

The developer updates the DOM at state 2.

Then the current state is captured as the new state.

Then the transition pseudo elements get created.

And then rendering is unpaused.

So now instead of just seeing the DOM underneath, we're seeing that new transition route.

And then they get animated.

And then they get removed.

And there you go, that's a view transition.

Now we need to know for, cross document view transitions.

And again, holy shit, that's a lot.

Let's look at a visualization again, and this one it's gonna seem, eerily similar to what we just looked at.

User initiates at this time by navigating.

Then, old state's captured, rendering's paused, navigation proceeds.

So the new DOM is there, and then the new document is captured as the new state.

So the transition pseudo elements are created, and then rendering is unpaused, and then they animate, and then they're taken away.

Very similar.

What's the difference between these?

The key difference is the trigger.

So for, same document view transitions, we have a programmatic trigger with that document method that I mentioned document dot startViewTransition.

And then for cross document view transitions, we have navigation based trigger.

And that can be clicking a link or submitting a form or clicking the browser's back or forward button.

And then we've got the pseudo elements that I've been mentioning.

So the view transition, that's the root of the view transitions overlay.

Which contains all the view transitions and sits over the top of all other page content.

The view transition group is the root of a single view transition.

We'll get into that a little bit with named view transitions to animate different things on a page.

View transition image pair is the container for a view transitions new and old views.

Those captured states that we were looking at before.

And then we've got those captured states.

The old is a static snapshot of the view before the transition, and the new is a live representation of the new view.

They're both CSS replaced content, so like an image, you can't reach inside and, say, style that new view, even though it's a live representation.

But you can have things in there, say, for example, a video playing.

And we'll see an example of that later on.

Also, you don't necessarily, always have both an old and a new pseudo element inside of that, image pair pseudo, sometimes if you've got an element that is only entering or only exiting, you will have either only one, and you can select that with the, only child pseudo class, so you can put specific styles, dependent on whether it is, fading between two different things or, entering or exiting.

Shout out to Bramus for this awesome visualization.

This shows all of those pseudo elements that we've been looking at.

At the bottom there is the, view transition root pseudo element.

And then we've got the group, the image pair, and the new and the old fading between each other.

That, default crossfade animation.

I've been talking a little bit about creating more than one, view transition.

You can do that by putting a view transition name on a certain element.

You can name that whatever you want and then that will, create more pseudo elements.

So we've got the default root ones and then also you can see here we've got the group image pair, the new, the old and the new For this one that we've named in this example, for the title.

And again, we've got another wonderful visualization from Bramus.

The view transition root in purple at the very bottom there.

And then the group image pair old and new for both of the separate, view transitions.

And you can see the group is the size of that element that is, the box, named view transition here.

While the root is the entire, viewport.

So what should you name when you're, creating view transitions?

Let's have a look at an example here with the Wikipedia.

We can see there's lots of different stuff on this page.

And here's another page.

We want to create a view transition between these two.

What's best to name?

There's a lot of different things on these pages.

Here are the two pages overlaid and, differenced with the mixed blend mode difference.

And this shows exactly what is different between the two pages.

The title at the top there clearly is changing.

In the top right corner there is the number of languages for each page.

So that's probably another thing we want to animate.

Obviously all the content and then the table on the right side there, as well as the table of contents on the left.

So these are all things that are different between the pages.

So we probably want to animate those.

So let's have a look, a demo.

Hopefully this one works.

So we've got the one Wikipedia page, and then we've got the other Wikipedia page.

And we're going back and forth, and we can see that they're animating.

Having the things that are changing between each page smoothly animate makes it much easier to see and lead focus to what is changing things that aren't changing, like the navigation don't change at all.

They are frozen right there on the page and there's no flash even, let's have a look though, at the styles that I've injected for this, toggle on some other styles in there.

So that we can see exactly what is being selected.

Wonderful.

Okay.

So these red boxes are every single element that we're selecting and putting a name on soon.

This is going to be much easier.

I'm so sure I'm sure you saw in the tamper monkey.

When I pulled it out, there was a lot of JavaScript code in there soon.

We're going to be able to select and give each element its own view transition name.

Based on the element itself without having to come up with a unique name ourselves.

So that will be handled by the browsers, but currently you can write some JavaScript to inject that.

But as we go back and forth between the pages, we can see the boxes expanding and contracting and everything that is being selected and transitioned.

Okay, let's get back to it.

Customizing these view transitions.

You can customize them in almost any way you might want.

But before we do that, let's have a look at the user agent styles.

The default styles that come with the browser.

What are they for view transitions?

Like I've mentioned a few times now, on the root element, it is given a view transition name of root so that you get that default view transition when you've opted into them.

Then, that, root view transition pseudo element gets position fixed and inset zero so that it can layout relative, to the rest of the page.

And everything inside of it can lay out relative to it as well.

It covers the entire snapshot route, which is, as you can see, on mobile, including things like the keyboard or the URL at the top, basically the browser Chrome.

On desktop, it doesn't include the browser Chrome.

It includes things like scroll bars though.

So basically, it's that entire viewport.

Then on, the view transition group, we are setting the position absolute top and left so that, we are positioned relative, to the root view transition pseudo element.

And then we're setting the animation duration to be a quarter of a second.

And animation fill mode to both down further.

We've got things that get dynamically added to the user agent style sheet.

And basically that's a lot ignore.

Most of it transition, a transform with height backdrop filter, other things that are going to be animated.

It creates a UA view transition group animation for each transition name, and then animates those things.

So then the view-transition-image-pair, is given position absolute and inset zero, and then all of those animation properties are set to inherit.

This way we want, the, styles from the group to pass through to, the old and new pseudo elements so that you can essentially treat the old and new as children of, the group, direct children of the group without having to worry too much about the image pair.

Then if you transition old and new.

Set to position, absolute inset, block start zero inline 100%.

So on so forth.

It's pulling in all these things and we're aligning it to, the image pair, which is, and also aligned to the group.

Block size is auto so that we don't break the aspect ratio, of whatever there is.

These are the default styles.

We can change them if we want, and we can see the animation stuff is inherited from, the group.

If it's set there.

There is also the default animations that are set, on the old and new, if there's both of them, we get the fade in and out and, mix blend mode plus lighter.

If they're only entering or exiting, if there's only one of them, then it just does the fade in or fade out.

There was some stuff in there earlier about the width and height and the transform and all of that stuff.

Where do they actually come from?

The TLDR is basically the root, if it's a root view transition, then it's the snapshot containing block, width and height.

Which is, like I said, essentially the viewport.

Otherwise, it's probably going to be the width and height of the element's border box.

Unless you're still using box sizing content box, but is anyone really doing that?

That's like the first reset we all do.

If that was too short and you want more, you can come back to my slides and look at the rest of this, but that was a rabbit hole, that I, fell down one night that was, far too long.

So recommendations.

First thing that I recommend, is setting up this, prefers reduced motion, reset of sorts, so that if you're adding any named view transitions and, like we looked at the name of your transitions get given, transforms and width and height and positioning stuff so that they are moved around the page, which is obviously motion.

And we don't want that if the user has asked us to reduce that so we can reset it so that we're in case we've removed the root transition, we're giving that name back.

We're inheriting the animation on the old and new and then also everything inside of the root basically, every other element on the page, we're removing the view transition name so that there's no motion.

We're just getting the default crossfade.

This one depends heavily on the scenario.

Sometimes, and I've been doing it on my slides, if these were working.

And I've been, using the block size 100 percent on, old and new.

And that means that, the, height will transition as well.

Again, if it was working, going between these slides.

Oh, it's sometimes working, the, height of, those code blocks there is, shifting to each other, whereas if that, were not the case, it wouldn't be doing that.

That looks good on things that are relatively close in size, but if you say transitioning from one line of text to 10 lines of text, it's going to get really stretched and not look so good.

So dependent on the situation, that can be a nice one to have.

Another good one is for images with the same aspect ratio, if it's the same image with the same aspect ratio, you can remove the animation and the mix blend mode as it's already the exact same.

The one on the left is scaling almost just like you're scaling it on the same page.

The one on the right though, you can see it stretches and distorts and gets a bit funky.

Then we've also got, same image with different aspect ratios.

This time, same as before, the, animation and the mixed blend mode.

But then we're setting block size to a hundred percent and overflow to clip.

And then object fit to contain and cover.

So then when we view this example, then we go back, the one on the left with the recommended styles, you can see just expand slides out, while the one on the right is jumping and distorting and getting a bit funky.

Next one is for the same video.

You can hide the old view transition and remove the animation from the new one.

So the video, is just be showing the new one and not the old one at all as you can keep the video playing with a bit of extra JavaScript added in there.

One important thing though, at the bottom that I've added is a background image so that there's no flicker between pages because otherwise while the video is loading in initially, it can show the background behind that.

So you can get a bit of a flash, which is not great for users with things like epilepsy.

So if we go Ooh, where's my cursor?

There we are, left with recommended styles and right again, without the recommended styles, we go back that video on the left continues playing and scales perfectly.

Whereas the one on the right is yeah, stretching, squishing, freaking out a bit.

And the video keeps on playing and keeps on working even during the transition.

So I said with that video that there was some JavaScript in there as well.

How am I hooking into that?

I'm hooking into that with the page swap and page reveal events.

There are plenty of different events for page load and DOM content loaded and navigation and all of these different things and none of them are going to quite line up with your transitions like we need.

So we've got these new events that perfectly line up page swap fires right before the last frame of the page is rendered.

You can use this to do some last minute changes on the outgoing page right before the old snapshot gets taken and page reveal essentially the opposite fires on the page after it's been initialized or reactivated, but before the first rendering opportunity with it.

It can customize the page before the new snapshot gets taken.

In these event listeners, you get given some stuff.

The event object, for page swap has event dot activation, which contains a navigation activation object, and eventdot viewTransition, which is a view transition object.

Surprise, surprise.

Event dot activation entry URL, that's where you're going, and event.

Activation from URL is the current page.

Then you can also get the type of activation.

You can skip the activation.

You can await the activation being ready, which is when the animations are about to start, or when the animations are about to be finished.

Or after they're finished, sorry.

An example, you can set up no view transition when, the user is navigating backwards and forwards in history.

Glad I haven't done that for the talk, because that's the only situation where the view transitions are working.

That way you can reach in and find the event activation dot type and select, traverse.

There we go.

I can speak, event view transition, skip navigation so that, view transition, never happens.

PageReveal, essentially the same as PageSwap, but instead of getting that activation object on the event, it's a globally available object on the window, so window dot navigation dot activation or just navigation dot activation is available on that new page.

That relies on the navigation API, but these two are coming along together.

Navigation activation entry, or this time is the current page.

And the from URL is the previous page because we are on that new page now.

Everything else though, the same.

An example, injecting styles when you get onto, a new page so that you can trigger a, certain view transition based on a page that you're coming from, so you can check that from URL path name to see if it's something and then out of you transition name and await it to be finished and clean that up so that you don't get any unwanted transitions in other situations.

Types are another thing that you can set types can be hard coded in the at view transition block with types and then the name of your types.

That's a list.

You can comma separate a bunch of different names in there.

Or you can add this in either the page swap or page reveal events with, viewTransitionTypes dot add and then the name of your type.

You can style based on those types.

This is an important thing that you don't want to spend hours debugging.

That's a pseudo class, not a pseudo element.

Missing one colon can waste a lot of time focusing on that one.

Putting in the name of the view transition in there means that you can select based on the type that has been set.

So an example on these slides, if it was working, has, the, view transition, forwards and backwards based on the direction that I'm going in the slides.

And now let's jump into a demo that kind of pulls all of these different things together and shows you them.

So we've got this, pretend e commerce site over here.

And we've got view transitions for all of the different things.

So if I click and go to a product page, you can see the information scaling up into the position that it is on the individual product page and back down to the card on the list page.

You can also go through and filter and sort all of these items and you can see they all move around.

None of these positions were calculated with Javascript, any dependencies, nothing like that.

You can also add to cart and we see the item move up into the cart icon in the top corner so we know, Oh, okay, that's where all of my stuff is hiding.

And also a nice little touch on top, moving between light mode and dark mode, we can have a clip path that shows us, Oh, we're changing.

Okay, Close that demo and get back.

Ricky mentioned, I am an accessibility specialist.

So I'm going to be here and stickling about it and making sure that is there anything we need to consider about this?

Yes, there is, but does it affect screen readers?

No, it doesn't.

Screen readers will just ignore the view transition pseudo elements and read the page that's behind it.

One other thing I did notice was with the navigation API.

This is, like I said, coming alongside, the view transitions API.

NavigationEvent dot intercept is the new way for SPAs to do SPA things.

And replace the DOM without actually having a page load.

But this time, instead of, how SPAs have been doing things for the past decade, it's actually built into the browser and notifies assistive technology when that fake, page load happens.

Though in my testing, that wasn't working a hundred percent everywhere that I tested it.

So be cautious, test again, whenever you, implementing this yourself, I would say it's just browser bugs for now though, as this is, early days.

Continuity, is really important for people with cognitive disabilities.

You can transition any element into any other element, which can be really helpful.

Like I showed in that e commerce demo before.

You can have the, product card going up into the cart and showing exactly where that is moving.

But it can also cause some issues.

You can transition any element into any other element just because you can do that doesn't mean you should do that for everything.

Maybe don't transition an interactive element into a non interactive element and have them both there because and vice versa, of course, because you're definitely going to get users clicking on things that aren't interactive and being very confused.

I touched on this a bit before, but motion can trigger symptoms for people with vestibular disorders.

So we want to add things like that prefers reduced motion reset that I showed earlier.

I mentioned with the video flashing can be bad for epileptic people or people with migraines.

So we want to make sure that we add in a background image.

So there's no, flickering of, contrasting colors.

In designing safer web animation for motion sensitivity, Val Head says, On a basic level, animating an element's movement makes its path visible on screen.

Your user doesn't have to keep track of the movement in their head.

Instead, that effort is essentially offloaded from their brain to the animation you created on screen.

When you reduce cognitive load, you free up users working memory resources to focus on other things like learning new skills or retaining information.

Beyond the cognitive load benefits, other studies show that animation can improve decision making and even help people learn and remember spatial relationships.

Animating between states can also help prevent change blindness.

In short, animation can free up your brain power and make an interface easier to understand.

The benefits cannot be ignored.

Accessibility is not just a, Oh, don't use this thing.

This is important to use for the sake of accessibility.

Just need to do it right.

Before I go, why do we care about this?

We care about this because of our users.

You want to improve their experience on your website or web app, you want to make it intuitive.

As you've learned today, View Transitions are an amazing tool for that.

Add a View Transition to the next thing that you're working on.

I've motivated you today, I've showed you all of these amazing things that View Transitions can do.

Next time you're building a component or a new page go and add a View Transition.

I know we got into the complexity but it is simple.

It's just one line to go and change that.

Thank you so much for listening.

Scan the QR code, find me online.

I'm all around the place and, have a wonderful rest of the conference.

Hi, I'm Elly Loel (she/her)

elly.to/wdds24-slides
Illustration of a smiling character with long blonde hair wearing a sweater with colorful text "GOLF le FLEUR" and a flower design, making a peace sign. A QR code is also present.

View transitions in the real world

I'm a view transition!

I’m also a view transition

Users

Delightful visuals

Perception of speed

DX

Simplicity at its core

Simple to Use*

*There is definitely a reasonably steep learning curve for the more complex stuff.

How do you do this?

Same-document view transitions

Can be used on any web page, regardless of if it's an SPA.

document.startViewTransition();

Browser support doesn't matter

It’s the poster child of progressive enhancement. You lose zero functionality if view transitions aren’t supported.

document.startViewTransition
   ? document.startViewTransition(() => updateCallback())
   : updateCallback();
Illustration of a browser window showing JavaScript code related to view transitions, with icons depicting browser support levels for Chrome, Firefox, and Safari.

I'm also a view transition!

Screenshot of a browser displaying a slide titled "I'm also a view transition!" with a developer tools window open to the Elements tab showing no match and the Animations tab displaying "Waiting for animations...".

Cross-document view transitions

Meta tag

<meta name="view-transition" content="same-origin">

CSS at-rule

@view-transition { navigation: auto; }

Again, browser support doesn't matter!

It's the poster child of progressive enhancement. You lose zero functionality if view transitions aren’t supported.

Browser support
Illustration of browser support showing icons for Chrome, Firefox, Safari, and other browsers with corresponding indicators.

I'm a view transition!

Screenshot of a browser's developer tools showing the Elements panel with HTML structure and an Animations tab displaying a progress bar with options for 100%, 25%, and 10%, labeled "Waiting for animations..."

How does it work?

The lifecycle of a same-document view transition

  1. Developer calls document.startViewTransition(updateCallback).
  2. Current state captured as the “old” state.
  3. Rendering paused.
  4. Developer’s updateCallback function is called, which updates the document state.
  5. viewTransition.updateCallbackDone promise fulfils.
  6. Current state captured as the “new” state.
  7. Transition pseudo-elements created.
  8. Rendering unpaused, revealing the transition pseudo-elements.
  9. viewTransition.ready promise fulfils.
  10. Pseudo-elements animate until finished.
  11. Transition pseudo-elements removed.
  12. viewTransition.finished promise fulfils.

Same-document view transition lifecycle diagram

Main DOM

Transition root

User sees

Developer calls document.startViewTransition()

Step backward Step forward
Diagram showing three stages of a document view transition: Main DOM with "State 1" label, Transition root blank, and User sees with "State 1" label.

Same-document view transition lifecycle diagram

Main DOM
State 1

Transition root

User sees
State 1

(Paused render)

Rendering paused

Step backward Step forward

Diagram showing a three-part lifecycle: Main DOM and User Sees both display "State 1", while Transition Root is empty.

Same-document view transition lifecycle diagram

Main DOM

Transition root

User sees

State 2

Step backward Step forward
Diagram showing the lifecycle of a same-document view transition with three stages: Main DOM, Transition root, and User sees, including state changes and navigation buttons.

The lifecycle of a cross-document view transition

  1. In the old document:
    1. The user initiates a navigation.
    2. When the new page is ready the pageswap event is fired.
      • You can now customise the transition, e.g. by mutating its types, or skip it altogether.
    3. Current state captured as the “old” state.
    4. Rendering paused.
    5. The navigation proceeds.
  2. Then, in the new document:
    1. The pagereveal event is fired.
      • This is another opportunity for you to customise the transition.
    2. Current state captured as the “new” state.
    3. Transition pseudo-elements created.
    4. Rendering unpaused, revealing the transition pseudo-elements.
    5. viewTransition.ready fulfils.
    6. Pseudo-elements animate until finished.
    7. Transition pseudo-elements removed.
    8. viewTransition.finished fulfils.

Cross-document view transition lifecycle diagram

Main DOM

Transition root

User sees

Old page

The user initiates a navigation

Step backward Step forward
Diagram depicting the lifecycle of a cross-document view transition with three stages: Main DOM, Transition root, and User sees.

Cross-document view transition lifecycle diagram

Main DOM

Transition root

User sees

Old page

Old page

The old document is captured as the "old" state

Step backward

Step forward

Diagram illustrating a cross-document view transition with three panels labeled "Main DOM," "Transition root," and "User sees."

Cross-document view transition lifecycle diagram

Main DOM

Transition root

User sees

New page

Old page

(Paused render)

The navigation proceeds

Step backward

Step forward

Diagram showing the lifecycle of a cross-document view transition, with elements labeled as Main DOM, Transition root, and User sees. Includes labels 'New page', 'Old page', and '(Paused render)'. Contains buttons for 'Step backward' and 'Step forward'.

Cross-document view transition lifecycle diagram

Main DOM

Transition root

User sees

The new document is captured as the "new" state

Step backward

Step forward

Diagram showing the transition lifecycle with three columns labeled "Main DOM," "Transition root," and "User sees." The "Main DOM" contains a box labeled "New page" in orange. The "User sees" contains a box labeled "Old page" in green and the text "(Paused render)." Steps to move backward and forward are indicated.

Cross-document view transition lifecycle diagram

Main DOM

Transition root

User sees

New page

Old page

Old page

(Transition root)

Rendering unpaused, revealing the transition pseudo-elements

Step backward

Step forward

Diagram showing the cross-document view transition lifecycle with stages: Main DOM, Transition root, User sees. Includes labels 'New page', 'Old page', and buttons 'Step backward', 'Step forward'.

What's the difference?

The key difference is the trigger

Programmatic trigger

document.startViewTransition();

Navigation trigger

<a href="/new-page/">New page</a>

or submitting a form, pressing the browser’s back button, etc.

Pseudo-elements

::view-transition
└── ::view-transition-group(root)
    └── ::view-transition-image-pair(root)
        ├── ::view-transition-old(root)
        └── ::view-transition-new(root)
A diagram depicting a hierarchy of pseudo-elements related to view transitions, showing connections between group, image-pair, old, and new states within a stylized browser window.

Only child

/* Entering */
::view-transition-new(…):only-child {}
/* Exiting */
::view-transition-old(…):only-child {}

Original pen by Bramus

Illustration of layered view transitions demonstrating how visual elements transition with labels such as 'view-transition-new(root)', 'view-transition-image-pair(root)', and 'view-transition-group(root)'.

Named view transitions

view-transition-name: your-vt-name;

Pseudo-elements

::view-transition
├── ::view-transition-group(root)
│   └── ::view-transition-image_pair(root)
│       ├── ::view-transition-old(root)
│       └── ::view-transition-new(root)
└── ::view-transition-group(title)
    └── ::view-transition-image_pair(title)
        ├── ::view-transition-old(title)
        └── ::view-transition-new(title)

Original pen by Bramus

Diagram illustrating CSS view transition pseudo-elements with overlapping colored layers labeled as "old", "image-pair", and "group".

What should you name?

Screenshot of a Wikipedia page for the album "Chromakopia" by Tyler, The Creator. Includes an infobox with album details and a track listing.
The two pages overlaid on each other to show differences

Demo

Screenshot of a Wikipedia page about the album "Chromakopia" by Tyler, The Creator with album information pane on the right featuring a monochrome photo of Tyler, The Creator in a suit.
Screenshot of a web page showing a code editor with a userscript for Tampermonkey.
Screenshot of a webpage at Wikipedia with highlighting of the page elemnts.

Customise view transitions

You can customise them in almost any way you might want

User-agent view transition styles

:root {
    view-transition-name: root;
}
:root::view-transition {
    position: fixed;
    inset: 0;
}

Snapshot containing block

Diagram showing two browser windows with the label "Snapshot root".
:root::view-transition-group(*) {
    position: absolute;
    top: 0;
    left: 0;
    animation-duration: 0.25s;
    animation-fill-mode: both;
}
@keyframes -ua-view-transition-group-anim-transitionName {
    from {
        transform: oldTransform;
        width: oldWidth;
        height: oldHeight;
        backdrop-filter: oldBackdropFilter;
    }
}
Illustration showing two blocks of CSS code related to view transitions. The first block defines a root view transition group with properties like position and animation duration. The second block defines keyframes for a transition animation with properties such as transform, width, height, and backdrop-filter.
@keyframes -ua-view-transition-group-anim-transitionName {
    from {
        transform: oldTransform;
        width: oldWidth;
        height: oldHeight;
        backdrop-filter: oldBackdropFilter;
    }
}
:root::view-transition-group(transitionName) {
    animation-name: -ua-view-transition-group-anim-transitionName;
    transform: newTransform ?? oldTransform;
    width: newWidth ?? oldWidth;
    height: newHeight ?? oldHeight;
    backdrop-filter: newBackdropFilter ?? oldBackdropFilter;
    color-scheme: newColorScheme ?? oldColorScheme;
    text-orientation: newTextOrientation ?? oldTextOrientation;
    writing-mode: newWritingMode ?? oldWritingMode;
    mix-blend-mode: newMixBlendMode ?? oldMixBlendMode;
    animation-timing-function: ease;
}
:root::view-transition-image-pair(*) {
    position: absolute;
    inset: 0;
    animation-duration: inherit;
    animation-fill-mode: inherit;
    animation-delay: inherit;
}
Screenshot of a code snippet demonstrating CSS for a view-transition image pair using pseudo-elements. Includes positioning and animation properties.
:root::view-transition-old(*),
:root::view-transition-new(*) {
    position: absolute;
    inset-block-start: 0;
    inline-size: 100%;
    block-size: auto;
    animation-duration: inherit;
    animation-fill-mode: inherit;
    animation-delay: inherit;
}
/* If there's both new and old */
:root::view-transition-old(transitionName) {
    animation-name: -ua-view-transition-fade-out, -ua-mix-blend-mode-plus-lighter;
}
:root::view-transition-new(transitionName) {

Image showing CSS code within a stylized code editor interface.

/* If there's both new and old */
:root::view-transition-old(transitionName) {
    animation-name: -ua-view-transition-fade-out, -ua-mix-blend-mode-plus-lighter;
}
:root::view-transition-new(transitionName) {
    animation-name: -ua-view-transition-fade-in, -ua-mix-blend-mode-plus-lighter;
}
/* Or if there's only new aka entering */
:root::view-transition-new(transitionName) {
    animation-name: -ua-view-transition-fade-in;
}
/* Or if there's only old aka exiting */
:root::view-transition-old(transitionName) {
    animation-name: -ua-view-transition-fade-out;
}
A screenshot of a code editor window showing CSS code for view-transition animations with comments describing conditions for new and old transitions.

Dynamic styles

Where do the old/new content width/height come from?

  • ▶ TL;DR
  • ▶ TS;WM

Dynamic styles

Where do the old/new content width/height come from?

  • ▶ TL;DR
  • ▶ TS;WM

Recommendations


&::view-transition-new(root) &::view-transition-old(root) {
    animation-duration: inherit;
    animation-fill-mode: inherit;
}
&::view-transition-new(root) {
    animation-name: -ua-view-transition-fade-in,
    -ua-mix-blend-mode-plus-lighter;
}
&::view-transition-old(root) {
    animation-name: -ua-view-transition-fade-out,
    -ua-mix-blend-mode-plus-lighter;
}
& * {
    view-transition-name: none;
}

Old & new block size

I’ve been using this on these slides:

::view-transition-old(*),
::view-transition-new(*) {
block-size: 100%;
}

If the old and new block sizes are similar it looks really great, so much better than without it. But, if the block sizes aren’t similar then it looks not so great.

Illustration of a code snippet with a CSS rule for view-transition elements.

Same image with the same aspect ratio

::view-transition-old(identical-image),
::view-transition-new(identical-image) {
  animation: none;
  mix-blend-mode: normal;
}
Illustration comparing two logos, both labeled "ANTI ARIA ARIA CLUB," showing differences with and without recommended styles.
Comparison of two graphics labeled "With recommended styles" and "Without recommended styles," both displaying the same text "ANTI ARIA ARIA CLUB" in a different style.

Same image with different aspect ratios

::view-transition-old(identical-image),
::view-transition-new(identical-image) {
animation: none;
mix-blend-mode: normal;
block-size: 100%;
overflow: clip;

::view-transition-old(identical-image) {
object-fit: contain;
}
::view-transition-new(identical-image) {
object-fit: cover;
Two side-by-side photographs of a black cat peeking through garden foliage. The left image is labeled "With recommended styles," and the right image is labeled "Without recommended styles."
Two images showing a black cat sitting behind a laptop displaying code. Left image with text 'With recommended styles'; right image with text 'Without recommended styles'.

Same video

::view-transition-old(video) {
display: none;

::view-transition-new(video) {
animation: none;

video {
background-image: url('/Placeholder.webp');
background-position: center;
background-size: contain;

Image of a stylized browser window containing CSS code blocks.

Same video

::view-transition-old(video) {
display: none;

::view-transition-new(video) {
animation: none;

video {
background-image: url('/Placeholder.webp');
background-position: center;
background-size: contain;
Two side-by-side images show a dog in a green spinning bowl. The left image is labeled "With recommended styles" and the right "Without recommended styles."

pageswap & pagereveal events

  • The pageswap event fires before the last frame of a page is rendered. You can use this to do some last-minute changes on the outgoing page, right before the old snapshots get taken.
  • The pagereveal event fires on a page after it has been initialised or reactivated but before the first rendering opportunity. With it, you can customise the new page before the new snapshots get taken.

pageswap

window.addEventListener('pageswap', async (event) => {
    event.activation.entry.url; // Where you're going
    event.activation.from.url; // Current page URL
    event.activation.type; // 'push' | 'replace' | 'reload' | 'traverse'
    event.viewTransition.skipTransition(); // Skip the view transition.
    await event.viewTransition.ready; // The animations are about to start.
    await event.viewTransition.finished; // The animations are finished.
});

Illustration of a code snippet within a stylized browser window showcasing event handling in JavaScript using the 'pageswap' event with async functions.

No view transition on back/forward navigations

window.addEventListener('pageswap', (event) => {
  if (event.activation.type === 'traverse') {
    event.viewTransition.skipTransition();
  }
});
Illustration of a code snippet in a web browser window styled element.

pagereveal

window.addEventListener('pagereveal', async (event) => {
-   event.activation;
+   navigation.activation;
navigation.activation.entry.url; // Current page URL
navigation.activation.from.url; // Where you came from
navigation.activation.type; // 'push' | 'replace' | 'reload' | 'traverse'
event.viewTransition.skipTransition(); // Skip the view transition.
await event.viewTransition.ready; // The animations are about to start.
await event.viewTransition.finished; // The animations are finished.
});

Screenshot of a code editor with JavaScript code demonstrating a transition from event.activation to navigation.activation, and handling view transitions.

Inject styles

window. addEventListener('pagereveal', async (event) => {
if (levent.viewTransition) return;
const sidebar = document.querySelector(' sidebar');
const fromURL = new URL(navigation.activation.from.url);
if (fromURL.pathname === '/certain-page/') {
	sidebar.style.viewTransitionName = 'sidebar";
	await event.viewTransition.finished;
	sidebar.style.viewTransitionName = "';
}
3) ;
Illustration of a browser window with JavaScript code to inject styles based on page reveal events.

View transition types

Hardcoded types

@view-transition {
      navigation: auto;
      + types: your-amazing-type;
}

Injected types

window.addEventListener('pageswap', (event) => {
      // also works on 'pagereveal'
      if (!event.viewTransition) return;
      + event.viewTransition.types.add('your-amazing-type');
});
Illustration of code blocks showing definitions and use of view transition types with examples of hardcoded and injected types.

Style based on types

html:active-view-transition-type(your-amazing-type) {}

Forwards and backwards types in my slides

const extractSlideNumberFromPath = (pathname) => (
	/\{1, 3}(\. \d)*/. exec(pathname)?.at(0);
) ;
const determineTransitionType = ({ url: fromURL 3, { url: toURL 3) => {
	const fromSlideNumber = extractSlideNumberFromPath(fromURL.pathname);
	const toSlideNumber = extractSlideNumberFromPath(toURL.pathname);
	if (fromSlideNumber > toSlideNumber) return 'backwards';
	if (toSlideNumber < toSlideNumber) return 'forwards';
	return 'unknown' ;
} ;
window. addEventListener('pagereveal', async (event) => {
if (levent.viewTransition) return;

Illustration of a code snippet in a simulated web page window with grey borders and three circular icons at the top left resembling a browser window. The code demonstrates functions for determining slide transitions.

Demo

Screenshot of an online shop webpage displaying various clothing items including hoodies and jackets, with options to sort and filter items. Elly narrates the effect as view transitions occur between pages.

Demo

Accessibility considerations

Continuity is important for people with cognitive disabilities

You can transition any element into any other element.

Just because you can doesn’t mean you should.

Maybe don’t transition an interactive element into a non-interactive element (and vice versa), as there’s a good chance it’ll confuse your users.

Motion can trigger symptoms in people with vestibular disorders

"Animation can free up your brain power and make an interface easier to understand—the benefits can’t be ignored."

— Val Head in Designing Safer Web Animation For Motion Sensitivity

Why?

Thanks for listening!

ellyloel.com
@elly@front-end.social
@ellyloel.com
Illustration of a person with long blonde hair wearing headphones, sitting at a computer. Next to them is a QR code linking to ellyloel.com. There is also a cartoon figure of a character with long yellow hair using a laptop, depicted as an animal-like creature.
  • startViewTransition
  • @view-transition
  • pseudo elements
  • document.startViewTransition
  • DOM
  • view transition group
  • CSS replaced content
  • clip-path
  • object-fit, contain, cover
  • page swap
  • page reveal
  • viewTransition
  • event.viewTransition
  • pseudo class
  • addEventListener
  • Navigation API
  • JavaScript
  • visibility: hidden