{"id":4184,"date":"2024-10-14T09:26:51","date_gmt":"2024-10-14T14:26:51","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=4184"},"modified":"2024-10-14T09:26:52","modified_gmt":"2024-10-14T14:26:52","slug":"fanout-with-grid-and-view-transitions","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/fanout-with-grid-and-view-transitions\/","title":{"rendered":"Fanout with Grid and View Transitions"},"content":{"rendered":"\n<p>I got a little nerdsniped by Preethi&#8217;s post <a href=\"https:\/\/frontendmasters.com\/blog\/css-fan-out-with-grid-and-property\/\">CSS Fan Out with Grid and @property<\/a> the other day. I like the idea of a opening a menu of items where the layout is powered by CSS grid. Then it collapses back into just one cell of the grid. You can even animate the grid columns\/rows themselves to pull this off, as Preethi demonstrated. If you know how many columns\/rows you want, you can animate that number up and down.<\/p>\n\n\n\n<p>I found the animations just a <em>bit<\/em> less smooth than I&#8217;d like to see, generally. The smoothness depends on lots of factors, like how many columns\/rows there are, how long the duration is, and how big those columns\/rows are. But imagine 3 rows collapsing to 1 over a full second. Since what is being animated is an <em>integer.<\/em> The best that can do is have two keyframes (3 to 2, 2 to 1) at 500ms each. It will not feel smooth. Preethi smoothed it over by animating the heights of items too, but the column\/rows changes can&#8217;t be smoothed over (there can never be 1.5 rows, for example).<\/p>\n\n\n\n<p>My mind went right to View Transitions. Particularly the &#8220;same page&#8221; style of View Transitions you can call with the JavaScript API <code>document.startViewTransition<\/code>. With it, we actually don&#8217;t even need CSS transitions\/animations <em>at all.<\/em> Weird right?! We just alter the DOM (by changing a class and letting CSS do it&#8217;s thing) <em>inside<\/em> a <code>startViewTransition<\/code> function, and the browser will automatically tween any elements with unique <code>view-transition-name<\/code> values. <\/p>\n\n\n\n<p>Here&#8217;s me re-creating a similar layout to the fan out navigation in Preethi&#8217;s article:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ExqZPNJ\" src=\"\/\/codepen.io\/anon\/embed\/ExqZPNJ?height=550&amp;theme-id=47434&amp;slug-hash=ExqZPNJ&amp;default-tab=css,result\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ExqZPNJ\" title=\"CodePen Embed ExqZPNJ\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Above, the <code>#grid<\/code> uses CSS grid to make an 1-column 7-row grid. By default <em>all<\/em> items are placed in the 4th grid row, making a &#8220;closed&#8221; state. When the <code>open<\/code> class is applied to the grid, the <code>grid-row<\/code> is replaced with <code>auto<\/code> letting them fall to where they normally would in the grid (the &#8220;fan out&#8221;). The item in the middle position is just styled differently to look and act as a toggle.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Here&#8217;s a video if you&#8217;re on a device that doesn&#8217;t support View Transitions<\/summary>\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='495' height='750' src='https:\/\/videopress.com\/embed\/dTmkmWip?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=1725245713'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p><\/p>\n<\/details>\n\n\n\n<p>In that above example, the space the grid occupies is the same in both states, but that wouldn&#8217;t need to be the case. If you want to alter the grid columns\/rows, thus changing the dimensions and nature of the grid, then view transition between those states, you can absolutely do that too. <\/p>\n\n\n\n<p>There really is no limit to what you want to do with the grid in the open and closed state. You don&#8217;t even have to think of the &#8220;states&#8221; in that way, although I do find it satisfying myself. Here&#8217;s many <em>more<\/em> items laid out on a grid with both columns and rows:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_PoMbmGy\" src=\"\/\/codepen.io\/anon\/embed\/PoMbmGy?height=450&amp;theme-id=47434&amp;slug-hash=PoMbmGy&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed PoMbmGy\" title=\"CodePen Embed PoMbmGy\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>When I was poking around with that demo, it felt it was just <em>begging<\/em> for &#8220;staggered transitions&#8221;, that is, animations that occur with a slight time delay between each element. I&#8217;m eyeing up future CSS that looks like it&#8217;s going to help with this, but we can actually do it now even using the view transitions we already have. <\/p>\n\n\n\n<p>I used Pug to create the HTML because it&#8217;s so repetitive and a processor can help abstract that and make it easier to update, but ultimately the HTML is like 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\">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\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/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\">a<\/span>&gt;<\/span>0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n  <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\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/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\">a<\/span>&gt;<\/span>1<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n  <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\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/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\">a<\/span>&gt;<\/span>2<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\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\">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>We can target each one of those items with unique view-transition-specific CSS and apply the <code>animation-delay<\/code> there. I used a Sass loop for the same reason as above, but ultimately the CSS looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" 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-2)<\/span> {\n  <span class=\"hljs-attribute\">animation-delay<\/span>: <span class=\"hljs-number\">0.02s<\/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\">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 ends up like this:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_JjgEVgd\" src=\"\/\/codepen.io\/anon\/embed\/JjgEVgd?height=450&amp;theme-id=47434&amp;slug-hash=JjgEVgd&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed JjgEVgd\" title=\"CodePen Embed JjgEVgd\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>I find that terribly pleasing.<\/p>\n\n\n\n<p>Again I&#8217;m leaving the entire grid in place here rather than changing the amount or size of any of the columns\/rows. But you could, and it wouldn&#8217;t be terribly different. It might actually be smart so the &#8220;closed&#8221; state isn&#8217;t taking up as much space in the flow.<\/p>\n\n\n\n<p>Again if a browser doesn&#8217;t <a href=\"https:\/\/caniuse.com\/view-transitions\">support<\/a> this kind of view transition (Firefox, at the time of writing), it doesn&#8217;t matter, it will still toggle open and closed just fine, just without animation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Imagine transitioning a bunch of items all set into ONE cell of a grid, then each having a unique animation when they move from that cell into where they would naturally fall on that same grid.<\/p>\n","protected":false},"author":1,"featured_media":4196,"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,123,101],"class_list":["post-4184","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-grid","tag-view-transitions"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/10\/fan-out.png?fit=1194%2C680&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4184","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=4184"}],"version-history":[{"count":13,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4184\/revisions"}],"predecessor-version":[{"id":4204,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/4184\/revisions\/4204"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/4196"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=4184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=4184"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=4184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}