{"id":4232,"date":"2024-10-22T11:42:53","date_gmt":"2024-10-22T16:42:53","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=4232"},"modified":"2024-10-22T11:42:54","modified_gmt":"2024-10-22T16:42:54","slug":"view-transitions-staggering","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/view-transitions-staggering\/","title":{"rendered":"View Transitions Staggering"},"content":{"rendered":"\n<p>I love view transitions. When you&#8217;re using view transitions to move <em>multiple items<\/em>, I think staggering them is cool effect and a reasonable ask for doing so succinctly. While I was playing with this recently I learned a lot and a number of different related tech and syntax came up, so I thought I&#8217;d document it. Blogging y&#8217;all, it&#8217;s cool. <a href=\"https:\/\/frontendmasters.com\/blog\/guest-writing-for-boost\/\">You should.<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example<\/h2>\n\n\n\n<p>So let&#8217;s say we have a menu kinda thing that can open &amp; close. It&#8217;s just an example, feel free to use your imagination to consider two states of any UI with multiple elements. Here&#8217;s ours:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"551\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?resize=551%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-4242\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?resize=551%2C1024&amp;ssl=1 551w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?resize=161%2C300&amp;ssl=1 161w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?resize=768%2C1428&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?resize=826%2C1536&amp;ssl=1 826w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR.png?w=924&amp;ssl=1 924w\" sizes=\"auto, (max-width: 551px) 100vw, 551px\" \/><figcaption class=\"wp-element-caption\">Closed<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"551\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?resize=551%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-4243\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?resize=551%2C1024&amp;ssl=1 551w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?resize=161%2C300&amp;ssl=1 161w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?resize=768%2C1428&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?resize=826%2C1536&amp;ssl=1 826w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/cdpn.io_pen_debug_GRVvgLR-1.png?w=924&amp;ssl=1 924w\" sizes=\"auto, (max-width: 551px) 100vw, 551px\" \/><figcaption class=\"wp-element-caption\">Open<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>View Transitions is a great way to handle animating this menu open. I won&#8217;t beat around the bush with a working example. Here&#8217;s that:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_GRVvgLR\" src=\"\/\/codepen.io\/anon\/embed\/GRVvgLR?height=850&amp;theme-id=47434&amp;slug-hash=GRVvgLR&amp;default-tab=css,result\" height=\"850\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed GRVvgLR\" title=\"CodePen Embed GRVvgLR\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That works in all browsers (<a href=\"https:\/\/caniuse.com\/view-transitions\">see support<\/a>). It animates (with staggering) in Chrome and Safari, and at this time of this writing, just instantly opens and closes in Firefox (which is fine, just less fancy). <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Unique View Transition Names<\/h2>\n\n\n\n<p>In order to make the view transition work at all, every single item needs a unique <code>view-transition-name<\/code>. Otherwise the items will not animate on their own. If you ever seen a view transition that has a simple fade-out-fade-in, when you were trying to see movement, it&#8217;s probably a problem with unique <code>view-transition-name<\/code>s. <\/p>\n\n\n\n<p>This brings me to my first point. Generating unique <code>view-transition-name<\/code>s is a bit cumbersome. In a &#8220;real world&#8221; application, it&#8217;s probably not <em>that<\/em> big of a deal as you&#8217;ll likely be using some kind of templating that could add it. Some variation of this:<\/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\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card\"<\/span>\n     <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: card-&lt;%= card.id %&gt;\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- turns into --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card\"<\/span> \n     <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: card-987adf87aodfasd;\"<\/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>But&#8230; you don&#8217;t <em>always<\/em> have access to something like that, and even when you do, isn&#8217;t it a bit weird that the only real practical way to apply these is from the HTML and not the CSS? Don&#8217;t love it. In my simple example, I use Pug to create a loop to do it. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">#grid\n  - const items = 10;\n  - for (let i = 0; i &lt; items; i++)\n    div(style=`view-transition-name: item-${i};`)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That Pug code turns into:<\/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\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"grid\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-0;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-1;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-2;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-3;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-4;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-5;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-6;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-7;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-8;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-9;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/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>Jen Simmons <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/8320#issuecomment-2023077559\">made the point about how odd this is<\/a>.<\/p>\n\n\n\n<p>This is being improved, I hear. <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/8320#issuecomment-2344208387\">The CSSWG has resolved to<\/a>&#8230;<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Add three keywords, one for ID attribute, one for element identity, and one that does fallback between the two.<\/p>\n<\/blockquote>\n\n\n\n<p>Which sounds likely we&#8217;ll be able to do something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-id\">#grid<\/span> {\n  &gt; div {\n    <span class=\"hljs-attribute\">view-transition-name<\/span>: auto; \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\">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>This makes me think that it could break in cross-document view transitions, but&#8230; I don&#8217;t think it actually will if you use the <code>id<\/code> attribute on elements and the <code>view-transition-name<\/code> ends up being based on that. Should be sweet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customizing the Animation<\/h2>\n\n\n\n<p>We&#8217;ve got another issue here. It wasn&#8217;t just a Pug loop need to pull of the view transition staggering, it&#8217;s a Sass loop as well. That&#8217;s because in order to control the animation (applying an <code>animation-delay<\/code> which will achieve the staggering), we need to give a pseudo class selector the <code>view-transition-name<\/code>, which are all unique. So&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">::view-transition-group(item-0)<\/span> {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0s<\/span>;\n}\n<span class=\"hljs-selector-pseudo\">::view-transition-group(item-1)<\/span> {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.01s<\/span>;\n}\n<span class=\"hljs-selector-pseudo\">::view-transition-group(item-0)<\/span> {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.02s<\/span>;\n}\n<span class=\"hljs-comment\">\/* etc. *\/<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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>That&#8217;s just as cumbersome as the HTML part, except maybe even more-so, as it&#8217;s less and less common we even have a CSS processor like Sass to help. If we do, we can do it like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"SCSS\" data-shcb-language-slug=\"scss\"><span><code class=\"hljs language-scss\"><span class=\"hljs-keyword\">@for<\/span> <span class=\"hljs-variable\">$i<\/span> from <span class=\"hljs-number\">0<\/span> through <span class=\"hljs-number\">9<\/span> {\n  ::view-transition-group(item-#{<span class=\"hljs-variable\">$i<\/span>}) {\n    animation-delay: <span class=\"hljs-variable\">$i<\/span> * <span class=\"hljs-number\">0.01s<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SCSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">scss<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Making Our Own Sibling Indexes with Custom Properties<\/h3>\n\n\n\n<p>How much do we need to delay each animation in order to stagger it? Well it should be a different timing, probably increasing slightly for each element. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">1st element = 0s delay<br>2nd element = 0.01s delay<br>3rd element - 0.02s delay<br>etc<\/pre>\n\n\n\n<p>How do we know which element is the 1st, 2nd, 3rd, etc? Well we could use <code>:nth-child(1)<\/code>, <code>:nth-child(2)<\/code> etc, but that saves us nothing. We still have super repetitive CSS that all but requires a CSS processor to manage. <\/p>\n\n\n\n<p>Since we&#8217;re already applying unique <code>view-transition-name<\/code>s at the HTML level, we could apply the element&#8217;s &#8220;index&#8221; at that level too, like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">#grid\n  - const items = 10;\n  - for (let i = 0; i &lt; items; i++)\n    div(style=`view-transition-name: item-${i}; --sibling-index: ${i};`) #{icons&#91;i]}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Which gets us that index as a custom property:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" 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\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"grid\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-0; --sibling-index: 0;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-1; --sibling-index: 1;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-2; --sibling-index: 2;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-3; --sibling-index: 3;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-4; --sibling-index: 4;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-5; --sibling-index: 5;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-6; --sibling-index: 6;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-7; --sibling-index: 7;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-8; --sibling-index: 8;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"view-transition-name: item-9; --sibling-index: 9;\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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<h3 class=\"wp-block-heading\">&#8230; but does that actually help us?<\/h3>\n\n\n\n<p>Not really?<\/p>\n\n\n\n<p>It seems like we should be able to use that value <em>rather<\/em> than the CSS processor value, like&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"SCSS\" data-shcb-language-slug=\"scss\"><span><code class=\"hljs language-scss\"><span class=\"hljs-keyword\">@for<\/span> <span class=\"hljs-variable\">$i<\/span> from <span class=\"hljs-number\">0<\/span> through <span class=\"hljs-number\">9<\/span> {\n  ::view-transition-group(item-#{<span class=\"hljs-variable\">$i<\/span>}) {\n    animation-delay: calc(var(--sibling-index) * <span class=\"hljs-number\">0.01s<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SCSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">scss<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But there are two problems with this:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>We need the Sass loop anyway for the view transition names<\/li>\n\n\n\n<li>It doesn&#8217;t work<\/li>\n<\/ol>\n\n\n\n<p>Lolz. There is something about the CSS custom property that doesn&#8217;t get applied do the <code>::view-transition-group<\/code> like you would expect it to. Or at least <em>*I*<\/em> would expect it to. \ud83e\udd37<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Enter <code>view-transition-class<\/code><\/h2>\n\n\n\n<p>There is a way to target and control the CSS animation of <em>a selected bunch<\/em> of elements at once, without having to apply a <code>::view-transition-group<\/code> to <em>individual<\/em> elements. That&#8217;s like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-id\">#grid<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  &gt; div {\n<\/span><\/span><mark class='shcb-loc'><span>    <span class=\"hljs-attribute\">view-transition-class<\/span>: item;\n<\/span><\/mark><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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>Notice that&#8217;s <em>class<\/em> not <em>name<\/em> in the property name. Now we can use that to select all the elements rather than using a loop.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/* Matches a single element with `view-transition-name: item-5` *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-pseudo\">::view-transition-group(item-5)<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.05s<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span><span class=\"hljs-comment\">\/* Matches all elements with `view-transition-class: item` *\/<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span><span class=\"hljs-selector-pseudo\">::view-transition-group(<\/span>*<span class=\"hljs-selector-class\">.item<\/span>) {\n<\/span><\/mark><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.05s<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>}\n<\/span><\/mark><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>That <code>*.<\/code> syntax is what makes it use the class instead of the name. That&#8217;s how I understand it at least!<\/p>\n\n\n\n<p>So with this, we&#8217;re <em>getting closer<\/em> to having staggering working without needing a CSS processor:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">::view-transition-group(<\/span>*<span class=\"hljs-selector-class\">.item<\/span>) {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--sibling-index) * <span class=\"hljs-number\">0.01s<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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>Except: that doesn&#8217;t work. It doesn&#8217;t work because <code>--sibling-index<\/code> doesn&#8217;t seem available to the pseudo class selector we&#8217;re using there. I have no idea if that is a bug or not, but it feels like it is to me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Real Sibling Index in CSS<\/h2>\n\n\n\n<p>We&#8217;re kinda &#8220;faking&#8221; sibling index with custom properties here, but we wouldn&#8217;t have to do that forever. The <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/4559#issuecomment-1642880894\">CSSWG has resolved<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><code>sibling-count()<\/code> and <code>sibling-index()<\/code> to css-values-5 ED<\/p>\n<\/blockquote>\n\n\n\n<p>I&#8217;m told Chrome is going to throw engineering at it in Q4 2024, so we should see an implementation soon. <\/p>\n\n\n\n<p>So then mayyyyybe we&#8217;d see this working:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">::view-transition-group(<\/span>*<span class=\"hljs-selector-class\">.item<\/span>) {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(sibling-index() * <span class=\"hljs-number\">0.01s<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>Now that&#8217;s enabling view transitions staggering beautifully easily, so I&#8217;m going to cross my fingers there.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Random Stagger<\/h3>\n\n\n\n<p>And speaking of newfangled CSS, <code>random()<\/code> should be coming to native CSS at some point somewhat soon as well as I belive that&#8217;s been given the thumbs up. So rather than perfectly even staggering, we could do like&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">::view-transition-group(<\/span>*<span class=\"hljs-selector-class\">.item<\/span>) {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-built_in\">calc<\/span>(random() * <span class=\"hljs-number\">0.01s<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><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>Faking that with Sass if fun!<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_JjgrNLx\" src=\"\/\/codepen.io\/anon\/embed\/JjgrNLx?height=850&amp;theme-id=47434&amp;slug-hash=JjgrNLx&amp;default-tab=result\" height=\"850\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed JjgrNLx\" title=\"CodePen Embed JjgrNLx\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Sibling Count is Useful Too<\/h3>\n\n\n\n<p>Sometimes you need to know <em>how many<\/em> items there are also, so you can control timing and delays such that, for example, the last animation can end when the first one starts again. Here&#8217;s an example from Stephen Shaw with fakes values as Custom Properties showing how that would be used. <\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_LYEEKMQ\" src=\"\/\/codepen.io\/anon\/embed\/LYEEKMQ?height=450&amp;theme-id=47434&amp;slug-hash=LYEEKMQ&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed LYEEKMQ\" title=\"CodePen Embed LYEEKMQ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>One line above could be written removing the need for custom properties:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/* before *\/<\/span>\nanimation-delay: calc(<span class=\"hljs-number\">2<\/span>s * (<span class=\"hljs-keyword\">var<\/span>(--sibling-index) \/ <span class=\"hljs-keyword\">var<\/span>(--sibling-count)));\n\n<span class=\"hljs-comment\">\/* after *\/<\/span>\nanimation-delay: calc(<span class=\"hljs-number\">2<\/span>s * (sibling-index() \/ sibling-count()));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><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<h2 class=\"wp-block-heading\">Overflow is a small bummer<\/h2>\n\n\n\n<p>I just noticed while working on this particular demo that during a view transition, the elements that are animating are moved to something like a &#8220;top layer&#8221; in the document, meaning they do not respect the <code>overflow<\/code> of parent elements and whatnot. See example:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XWveRxx\" src=\"\/\/codepen.io\/anon\/embed\/XWveRxx?height=850&amp;theme-id=47434&amp;slug-hash=XWveRxx&amp;default-tab=result\" height=\"850\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XWveRxx\" title=\"CodePen Embed XWveRxx\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Don&#8217;t love that, but I&#8217;m sure there are huge tradeoffs that I&#8217;m just not aware of. I&#8217;ve been told this is actually a desirable trait of view transitions \ud83e\udd37. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">p.s. DevTools Can Inspect This Stuff<\/h2>\n\n\n\n<p>In Chrome-based browsers, open the Animations tab and slow down the animations way down.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1012\" height=\"790\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.36.15%E2%80%AFAM.png?resize=1012%2C790&#038;ssl=1\" alt=\"\" class=\"wp-image-4248\" style=\"width:433px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.36.15%E2%80%AFAM.png?w=1012&amp;ssl=1 1012w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.36.15%E2%80%AFAM.png?resize=300%2C234&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.36.15%E2%80%AFAM.png?resize=768%2C600&amp;ssl=1 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>The mid-animation, you can use that Pause icon to literally stop them. It&#8217;s just easier to see everything when it&#8217;s stopped. Then you&#8217;ll see a <code>:view-transition<\/code> element at the top of the DOM and you can drill into it an inspect what&#8217;s going on. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"810\" height=\"416\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.37.56%E2%80%AFAM.png?resize=810%2C416&#038;ssl=1\" alt=\"\" class=\"wp-image-4249\" style=\"width:436px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.37.56%E2%80%AFAM.png?w=810&amp;ssl=1 810w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.37.56%E2%80%AFAM.png?resize=300%2C154&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/Screenshot-2024-10-22-at-9.37.56%E2%80%AFAM.png?resize=768%2C394&amp;ssl=1 768w\" sizes=\"auto, (max-width: 810px) 100vw, 810px\" \/><\/figure>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you use View Transitions on multiple elements, it can be a very nice look to stagger them out a little bit. It&#8217;s possible now, but a bit finicky. Let&#8217;s take a look at some code, present and future, that will help. <\/p>\n","protected":false},"author":1,"featured_media":4246,"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":[7,202,250,101],"class_list":["post-4232","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-overflow","tag-sibling-index","tag-view-transitions"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/pexels-photo-7269683.jpeg?fit=1880%2C1253&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4232","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=4232"}],"version-history":[{"count":12,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4232\/revisions"}],"predecessor-version":[{"id":4252,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4232\/revisions\/4252"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/4246"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=4232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=4232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=4232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}