{"id":8067,"date":"2025-12-16T20:40:39","date_gmt":"2025-12-17T01:40:39","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=8067"},"modified":"2025-12-16T20:40:40","modified_gmt":"2025-12-17T01:40:40","slug":"different-page-transitions-for-different-circumstances","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/different-page-transitions-for-different-circumstances\/","title":{"rendered":"Different Page Transitions For Different Circumstances"},"content":{"rendered":"\n<p>I feel like common usage for multi-page view transitions is to set up a general system for them that applies generally to all pages and elements, then let it ride. <\/p>\n\n\n\n<p>But I just recently saw the DOM events in JavaScript and how they can be used to set a &#8220;type&#8221;. So check out the events first:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ The old \/ unloading page<\/span>\n<span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">'pageswap'<\/span>, <span class=\"hljs-keyword\">async<\/span> (e) =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (e.viewTransition) {\n \n  }\n}\n\n<span class=\"hljs-comment\">\/\/ the new \/ loading page<\/span>\n<span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">'pagereveal'<\/span>, <span class=\"hljs-keyword\">async<\/span> (e) =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (e.viewTransition) {\n \n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>You can do anything you might want in there, but an especially interesting thing to me is that you can set the view transition type, and do so <em>conditionally. <\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customize the View Transition Type for One Particular URL<\/h2>\n\n\n\n<p>Just to clearly illustrate the point, say you want one particular page to have a different transition animation than all the rest of them. Says it&#8217;s the &#8220;Shows&#8221; page on a website at the relative URL <code>\/shows<\/code>. Then we&#8217;d watch for the <code>pagereveal<\/code> event <em>and<\/em> test that URL and if it&#8217;s a match we&#8217;ll set the type:<\/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\"><span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">\"pagereveal\"<\/span>, <span class=\"hljs-keyword\">async<\/span> (e) =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (\n    e.viewTransition &amp;&amp; \n    <span class=\"hljs-built_in\">document<\/span>.location.pathname === <span class=\"hljs-string\">\"\/shows\"<\/span>\n  ) {\n    e.viewTransition.types.add(<span class=\"hljs-string\">\"toShowsPage\"<\/span>);\n  }\n});<\/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>That <code>toShowsPage<\/code> is just an arbitrary name we&#8217;re making up to use in CSS to customize the animation when it&#8217;s set. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The &#8220;Default&#8221; View Transition<\/h2>\n\n\n\n<p>We&#8217;ve got a custom type being set, but let&#8217;s set up the default first. Something like this is neat:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">::view-transition-old(main)<\/span> {\n  <span class=\"hljs-attribute\">animation-name<\/span>: slide-out-to-left;\n  <span class=\"hljs-attribute\">animation-duration<\/span>: <span class=\"hljs-number\">1s<\/span>;\n}\n<span class=\"hljs-selector-pseudo\">::view-transition-new(main)<\/span> {\n  <span class=\"hljs-attribute\">animation-name<\/span>: slide-in-from-right;\n  <span class=\"hljs-attribute\">animation-duration<\/span>: <span class=\"hljs-number\">1s<\/span>;\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> slide-out-to-left {\n  <span class=\"hljs-selector-tag\">to<\/span> {\n    <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">150px<\/span> <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">scale<\/span>: <span class=\"hljs-number\">0.5<\/span>;\n  }\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> slide-in-from-right {\n  <span class=\"hljs-selector-tag\">from<\/span> {\n    <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">100<\/span>vi <span class=\"hljs-number\">0<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In my example here, it assumes a <code>&lt;main><\/code> content area with <code>view-transition-name: main;<\/code> so that is the element being targeted specifically here. Now when I move pages (by just clicking regular ol&#8217; links) I get this effect:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player aligncenter wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 380px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='567' src='https:\/\/videopress.com\/embed\/ekBvCWFC?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">Using the Custom Type for a Custom Animation<\/h2>\n\n\n\n<p>When the &#8220;Shows&#8221; link is clicked and the <code>\/shows<\/code> page is loaded, we&#8217;re setting the &#8220;toShowsPage&#8221; type, and this is the magic moment we can use it in CSS:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">html:active-view-transition-type(toShowsPage) {\n  &amp;::view-transition-<span class=\"hljs-keyword\">new<\/span>(main) {\n    <span class=\"hljs-attr\">animation<\/span>: to-shows-page <span class=\"hljs-number\">1<\/span>s forwards;\n  }\n}\n\n@keyframes to-shows-page {\n  <span class=\"hljs-keyword\">from<\/span> {\n    <span class=\"hljs-attr\">scale<\/span>: <span class=\"hljs-number\">1.1<\/span>;\n    translate: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">-200<\/span>px;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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>Because of the extra specificity over just <code>::view-transition-new<\/code>, this gives us an opportunity to <em>override<\/em> the default <code>animation<\/code> here with a new set of keyframes. Now <em>just<\/em> the Shows page will come down from the top instead. See the difference:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player aligncenter wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 406px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='700' src='https:\/\/videopress.com\/embed\/rE06avRv?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1739540970'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">Notes<\/h2>\n\n\n\n<p>I think it&#8217;s cool we have this level of control and interplay between JavaScript and CSS. <\/p>\n\n\n\n<p>I first saw this in Bramus&#8217; <a href=\"https:\/\/developer.chrome.com\/docs\/web-platform\/view-transitions\/cross-document\">Cross-document view transitions for multi-page applications<\/a>, which is a good resource and covers &#8220;forwards&#8221;, &#8220;backwards&#8221;, and &#8220;reload&#8221; view transition types which seems extremely practical and makes me wish were something we have native CSS access to detect. <\/p>\n\n\n\n<p>There is a native CSS way to <a href=\"https:\/\/css-tricks.com\/almanac\/rules\/v\/view-transition\/#aa-limiting-view-transitions-with-the-types-descriptor\"><em>declare<\/em> the types<\/a>, but I&#8217;m not quite understanding why that is useful or important to do. All I understand so far is that any type that <em>isn&#8217;t<\/em> listed there when you <em>do <\/em>declare them invalidates them, so maybe that&#8217;s useful somehow?<\/p>\n\n\n\n<p>I would have thought the &#8220;types&#8221; stuff would have been a bit newer, and thus lower browser support, than other view transitions stuff, but that&#8217;s wrong. MDN has <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/ViewTransitionTypeSet\">JavaScript type setting<\/a> as well as CSS <code><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Selectors\/:active-view-transition-type\">:active-view-transition-type()<\/a><\/code> as the same level of browser support as multi-page view transitions in general, that is to say, Chrome, Safari, and flagged in Firefox.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In JavaScript, you can detect a view transition happening, set a type, and have CSS do unique things based on that type. <\/p>\n","protected":false},"author":1,"featured_media":8076,"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":[],"class_list":["post-8067","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/12\/view-transition-thumb.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8067","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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=8067"}],"version-history":[{"count":3,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8067\/revisions"}],"predecessor-version":[{"id":8075,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8067\/revisions\/8075"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/8076"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=8067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=8067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=8067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}