{"id":1541,"date":"2024-04-03T11:50:59","date_gmt":"2024-04-03T17:50:59","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=1541"},"modified":"2024-04-03T12:01:32","modified_gmt":"2024-04-03T18:01:32","slug":"the-view-transition-api","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/the-view-transition-api\/","title":{"rendered":"The View Transitions API"},"content":{"rendered":"\n<p>Like anyone, I love a good &#8220;native-feeling&#8221; experience on the web. I&#8217;ve really enjoyed creating that vibe with the new <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/View_Transitions_API\">View Transitions API<\/a>. Luckily it&#8217;s is pretty clean to work with, both with the <a href=\"https:\/\/astro.build\/\">Astro<\/a> framework, and out of the box!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">View Transition Options<\/h2>\n\n\n\n<p>Transitioning UI between states has been something that developers have been doing for ages, and yes, you can use things like the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Animations_API\">Web Animation API<\/a> as well as CSS <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_transitions\/Using_CSS_transitions\">transitions<\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_animations\/Using_CSS_animations\">animations<\/a> for that. But, handling transitions when elements are <em>removed<\/em> from the page, or you navigate <em>away<\/em> from a given page, is much harder, particularly when you&#8217;re thinking about assistive tech, scroll position, and focus\u2026 it&#8217;s gnarly.<\/p>\n\n\n\n<p>Untiiiiil now! The View Transitions API has been evolving in the browser both as a proper <a href=\"https:\/\/drafts.csswg.org\/css-view-transitions-1\/\">W3C Candidate Recommendation<\/a>, and as of this week, <a href=\"https:\/\/drafts.csswg.org\/css-view-transitions-2\/\">the draft for cross-document navigation<\/a> is in a public working draft!<\/p>\n\n\n\n<p>What does that mean? It means you can have that &#8220;native&#8221; animation experience, right in the browser! But more specifically, currently you can use the View Transitions API:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>For transitions on normal page-to-page loading<\/li>\n\n\n\n<li>For transitions on a single page application (SPA)<\/li>\n\n\n\n<li>For transitions of the DOM changes without a page change<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">View Transitions + Astro<\/h2>\n\n\n\n<p>My favorite way to actually see this implemented so far is using the <a href=\"https:\/\/astro.build\">Astro<\/a> framework. They have a really great API that allows you to use View Transitions on a single page, or turn on &#8220;SPA mode&#8221; and navigate across your entire application and have transitions between pages and elements.<\/p>\n\n\n\n<p>Out of the box, Astro supports:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Out of the box animations like <code>fade<\/code> and <code>slide<\/code><\/li>\n\n\n\n<li>Navigation forwards and backwards<\/li>\n\n\n\n<li>Support for <code>prefers-reduced-motion<\/code><\/li>\n\n\n\n<li>And moooore<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Using View Transitions in Astro<\/h2>\n\n\n\n<p>Adding the View Transitions API to an Astro page is just two lines of code: importing it in your frontmatter, and adding it to the <code>&lt;head&gt;<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">---\nimport { ViewTransitions } from 'astro:transitions';\n---\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- ... --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ViewTransitions<\/span> \/&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- ... --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If you put these lines in a layout component that&#8217;s shared across pages, that automatically enables View Transition-powered client-side navigation, fading between pages.<\/p>\n\n\n\n<p>Astro automatically corresponds elements between two pages by their type and location within the DOM, so if you want something simple, this is all you need!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customize more with directives<\/h2>\n\n\n\n<p>You can have more fine-grained control of what your transitions do by using the <code>transition:*<\/code> directives.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>transition:name<\/code> overrides the default matching that Astro does between elements. Make sure when you use this one, you use unique names, kind of like an <code>id<\/code>.<\/li>\n\n\n\n<li><code>transition:animate<\/code> overrides the default fade animation, and you can customize what animations you want.<\/li>\n\n\n\n<li><code>transition:persist<\/code> overrides how Astro replaces elements across pages and lets you persist components and elements across pages.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Example<\/h2>\n\n\n\n<p>I built <a href=\"https:\/\/view-transitions-demo.netlify.app\/\">a little demo<\/a> for you to see the View Transitions API in action!<\/p>\n\n\n\n<p>There&#8217;s two main pages to make this little &#8220;city directory&#8221; site, <code>index.astro<\/code>, and <code>[city].astro<\/code>, which pull from a <code>locations.json<\/code> file.<\/p>\n\n\n\n<p>In <a href=\"https:\/\/github.com\/cassidoo\/astro-vt-demo\/blob\/main\/src\/pages\/index.astro\"><code>index.astro<\/code><\/a>, we loop through the locations:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;ul&gt;\n    {locations.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">city<\/span>) =&gt;<\/span> {\n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">transition:name<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">city.emoji<\/span>} <span class=\"hljs-attr\">emoji<\/span>`}&gt;<\/span>{city.emoji}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{<\/span>`\/<span class=\"hljs-attr\">city<\/span>\/${<span class=\"hljs-attr\">city.city.toLowerCase<\/span>()}`} <span class=\"hljs-attr\">transition:name<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">city.city<\/span>} <span class=\"hljs-attr\">link<\/span>`}&gt;<\/span>\n                    {city.city}, {city.country}\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span><\/span>\n        )\n    })}\n&lt;<span class=\"hljs-regexp\">\/ul&gt;    <\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In this, note the two transition names on the emoji and on the link, unique for each city.<\/p>\n\n\n\n<p>In <a href=\"https:\/\/github.com\/cassidoo\/astro-vt-demo\/blob\/main\/src\/pages\/city\/%5Bcity%5D.astro\"><code>[city].astro<\/code><\/a> (which is a <a href=\"https:\/\/docs.astro.build\/en\/guides\/routing\/#dynamic-routes\">dynamic route<\/a> generated per city), we map those transition names to different elements:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">transition:name<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">city<\/span>} <span class=\"hljs-attr\">link<\/span>`}&gt;<\/span>{city}, {country}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">transition:name<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">city<\/span>} <span class=\"hljs-attr\">emoji<\/span>`}&gt;<\/span>{emoji}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span> {description}\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So now, when we navigate between the home page and a city page (and back), the heading transitions from the link, and the emoji moves from the link to the paragraph!<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/view-transitions-demo.netlify.app\/\">See Multi Page Demo<\/a><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Using View Transitions without Astro<\/h2>\n\n\n\n<p>If your framework of choice is not Astro, but you still want to try out the View Transitions API, it does work in Chrome, Edge, and Opera right now out of the box (Safari and Firefox require polyfilling).<\/p>\n\n\n\n<p>You can <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/View_Transitions_API\">check out the documentation here<\/a>. It&#8217;s a bit more manual when you want to build with it from scratch, but you do get a lot of control!<\/p>\n\n\n\n<p>Here&#8217;s a very basic example of transitioning between some text values with the API:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_NWmXrjb\" src=\"\/\/codepen.io\/anon\/embed\/NWmXrjb?height=550&amp;theme-id=47434&amp;slug-hash=NWmXrjb&amp;default-tab=js,result\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed NWmXrjb\" title=\"CodePen Embed NWmXrjb\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Note that the CSS lets you control the transition, similar to how traditional CSS animations work! You assign an animation with <code>view-transition-name<\/code>, and then you give the view transition pseudo-elements get assigned animations in and out (via <code>::view-transition-old<\/code> and <code>::view-transition-new<\/code>)!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Read more<\/h2>\n\n\n\n<p>This is just scratching the surface of what the View Transitions API can do! You can find some more resources here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/cassidoo\/astro-vt-demo\">The city guide demo repository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.astro.build\/en\/guides\/view-transitions\/\">The Astro View Transition documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/notacult.social\/@bramus@front-end.social\/112169845528821286\">Overview of what&#8217;s in the cross-document navigation draft<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.chrome.com\/docs\/web-platform\/view-transitions\">Chrome developer guide on View Transitions<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Like anyone, I love a good &#8220;native-feeling&#8221; experience on the web. I&#8217;ve really enjoyed creating that vibe with the new View Transitions API. Luckily it&#8217;s is pretty clean to work with, both with the Astro framework, and out of the box! View Transition Options Transitioning UI between states has been something that developers have been [&hellip;]<\/p>\n","protected":false},"author":17,"featured_media":1547,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[144,101],"class_list":["post-1541","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-astro","tag-view-transitions"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/transition-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1541","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=1541"}],"version-history":[{"count":6,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1541\/revisions"}],"predecessor-version":[{"id":1555,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1541\/revisions\/1555"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1547"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=1541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=1541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=1541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}