<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hubert SABLONNIÈRE</title>
    <description>The latest articles on DEV Community by Hubert SABLONNIÈRE (@hsablonniere).</description>
    <link>https://dev.to/hsablonniere</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F493717%2F83222ff0-8d41-4e36-a2c2-c7a687fc21a8.png</url>
      <title>DEV Community: Hubert SABLONNIÈRE</title>
      <link>https://dev.to/hsablonniere</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hsablonniere"/>
    <language>en</language>
    <item>
      <title>Down the Rabbit Hole with Claude Code: My Journey to Window Key Switcher</title>
      <dc:creator>Hubert SABLONNIÈRE</dc:creator>
      <pubDate>Fri, 08 Aug 2025 14:12:48 +0000</pubDate>
      <link>https://dev.to/hsablonniere/down-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher-482l</link>
      <guid>https://dev.to/hsablonniere/down-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher-482l</guid>
      <description>&lt;p&gt;Let me tell you the story of when I finally tried out Claude Code and ended up in a multi-level rabbit hole that led me to create &lt;a href="https://github.com/hsablonniere/window-key-switcher" rel="noopener noreferrer"&gt;Window Key Switcher&lt;/a&gt;, a GNOME Shell extension for keyboard-driven window management.&lt;/p&gt;

&lt;h2&gt;👋 Hello Claude Code&lt;/h2&gt;

&lt;p&gt;I've been very skeptical about LLMs and coding agents since they appeared a few years ago.
The AI hype cycle, with all its bold claims about replacing developers entirely, and promises of instant app creation, made my eyes roll several times, especially regarding frontend code and accessibility.
I hesitated for a long time before diving into these tools, but like any good engineer, I cannot have a strong opinion without basing it on experience.
I needed to learn and understand what they could and could not do, which is actually very hard as everything evolves all the time.
Sometimes I was disappointed, sometimes I was impressed, but none of the tools I tried really clicked with me to the point of completely reconsidering my workflow.&lt;/p&gt;

&lt;p&gt;None of them until I tried Claude Code.&lt;/p&gt;

&lt;p&gt;I think it comes down to the fact that Claude Code is a terminal based interface.
With VS Code forks like &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; or &lt;a href="https://codeium.com/windsurf" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt;, I need to open a project and then, my brain automatically goes &lt;em&gt;"I'm in an IDE, I'm supposed to ask the agent to write code"&lt;/em&gt;.
With Claude Code (and competitors like &lt;a href="https://ai.google.dev/gemini-api/docs/cli" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt;, &lt;a href="https://github.com/ModelBest/OpenCode" rel="noopener noreferrer"&gt;Open Code&lt;/a&gt;, or &lt;a href="https://openai.com/research/codex" rel="noopener noreferrer"&gt;Codex&lt;/a&gt;), my brain goes &lt;em&gt;"I'm in a terminal, I can do anything I want on my computer"&lt;/em&gt;.
In his &lt;a href="https://steipete.me/posts/2025/claude-code-is-my-computer" rel="noopener noreferrer"&gt;Claude Code is My Computer&lt;/a&gt; article, Peter Steinberger described something really close to the mind shift I went through recently.
Bonus point, Claude Code can connect with WebStorm so I can keep using the IDE I love and know how to use very well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fclaude-code-webstorm.6ed99627.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fclaude-code-webstorm.6ed99627.png" alt="Screenshot showing Claude Code terminal interface on the left and WebStorm IDE on the right, demonstrating the integrated workflow" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;💰 Level -1: Buying new monitors&lt;/h2&gt;

&lt;p&gt;Most of the time, when I work at home or at my co-working space, I use two monitors.
I have my IDE on my laptop and my browser on a desktop monitor above it.
Anytime I need my terminal, I use a single hotkey to show/hide it on the monitor that has the mouse.
I've been working like this for years!&lt;/p&gt;

&lt;p&gt;Using Claude Code more and more, I started multi-tasking with several terminal tabs opened in parallel.
It was so annoying to switch between them, it resurrected an old wish I had: upgrading my monitor setup to 4 monitors.
I had this simple (and maybe dumb) thought process: if I had more screen real-estate, I could see different terminal windows at the same time and watch how each Claude Code session is doing while still being able to use my browser, IDE...&lt;/p&gt;

&lt;p&gt;That's how I convinced myself to buy two reconditioned &lt;a href="https://www.msi.com/Portable-Monitor/PRO-MP161-E2/Overview" rel="noopener noreferrer"&gt;portable 15-inch USB-C monitors&lt;/a&gt;.
They're light, I can bring them on the go.
I experimented with using them vertically, something I hadn't really tried before.
But now that I had all these terminal windows, I felt the urge to be able to split my terminal windows vertically and/or horizontally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fmonitor-setup.f999f4ca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fmonitor-setup.f999f4ca.png" alt="Four-monitor setup showing a laptop screen, desktop monitor above it, and two vertical portable monitors on the sides, creating an extended workspace" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;🔍 Level -2: The terminal emulator quest&lt;/h2&gt;

&lt;p&gt;First, I tried &lt;a href="https://github.com/tmux/tmux" rel="noopener noreferrer"&gt;tmux&lt;/a&gt;, again.
It wasn't my first attempt with it, but like the last time, I didn't click with the shortcuts.
They're too weird and complex for me.
Also, I don't need the session system, and the mouse support doesn't really work natively.&lt;/p&gt;

&lt;p&gt;So I went looking for a new terminal emulator.
I'd been using &lt;a href="https://docs.xfce.org/apps/terminal/start" rel="noopener noreferrer"&gt;xfce-terminal&lt;/a&gt; since my XFCE days and was still using it after switching back to GNOME.
That's when I stumbled upon &lt;a href="https://ghostty.org/" rel="noopener noreferrer"&gt;Ghostty&lt;/a&gt;.
It provides native window multiplexing with keyboard shortcuts to "split left" or "split down", and it's great.&lt;/p&gt;

&lt;p&gt;I've been using it for a few days now and I won't go back to xfce-terminal.&lt;/p&gt;

&lt;p&gt;By the way, Ghostty's configuration is done with a config file, no GUI, no wizard.
No problem, I told Claude Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;look for my xfce-terminal custom color palette on my system (no idea where it's stored)&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;ghostty +show-config --default --docs&lt;/code&gt; to get the list of options and docs about them&lt;/li&gt;
&lt;li&gt;use the output to ask me questions as some kind of configuration wizard&lt;/li&gt;
&lt;li&gt;create the config file for me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only problem I have for now with Ghostty is the lack of Linux (with X11) support for a "dropdown mode" (they call it "quick-terminal").
As I described before, I'm addicted to having a single hotkey to show/hide my terminal on the top half of my screen (or fullscreen sometimes).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fghostty.1392eb30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fdown-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57%2Fghostty.1392eb30.png" alt="Ghostty terminal emulator showing split panes with multiple terminal sessions running simultaneously, demonstrating its window multiplexing capabilities" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;🔨 Level -2.5: The bash script&lt;/h2&gt;

&lt;p&gt;I spent an evening trying to hack together a bash script that would mimic a dropdown behavior using &lt;a href="https://github.com/jordansissel/xdotool" rel="noopener noreferrer"&gt;&lt;code&gt;xdotool&lt;/code&gt;&lt;/a&gt;.
Again, I asked Claude Code.
It worked... more or less.
But the window placement was goofy.
It comes down to the limits of what &lt;code&gt;xdotool&lt;/code&gt; can do vs. a more precise approach.
I went to bed with a working but not that satisfying solution.&lt;/p&gt;

&lt;h2&gt;🚀 Level -3: The GNOME Shell extension&lt;/h2&gt;

&lt;p&gt;The next morning, I wanted more, I wanted a better and more powerful version of this bash script.
That's when I decided to create a GNOME Shell extension.
I also acknowledged I was now an official resident of this rabbit hole, but it was fun so I stayed.&lt;/p&gt;

&lt;p&gt;I had already built &lt;a href="https://github.com/hsablonniere/window-manager-http" rel="noopener noreferrer"&gt;one extension&lt;/a&gt; to control my desktop windows with HTTP with my custom slide tool for my &lt;a href="https://www.youtube.com/watch?v=xtpaQ8_mmKM" rel="noopener noreferrer"&gt;HTTP cache talk&lt;/a&gt;.
But I actually had very little knowledge and experience with GNOME Shell extensions.
Back then, I mostly mimicked and adapted an existing extension I saw on GitHub and I remembered going through the API docs was a mess.&lt;/p&gt;

&lt;p&gt;I started describing my idea to Claude Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ten hotkeys: &lt;code&gt;Super + number-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Assign any app to a number&lt;/li&gt;
&lt;li&gt;Press the hotkey:
&lt;ul&gt;
&lt;li&gt;If the app is not launched, launch it&lt;/li&gt;
&lt;li&gt;If the app is launched, focus the window&lt;/li&gt;
&lt;li&gt;If the app has multiple opened windows, focus the next one like Alt+Tab would do (this was not that simple)&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;Bonus: highlight the window that was just focused&lt;/li&gt;


&lt;/ul&gt;

&lt;h3&gt;💩 The first "vibe coded" attempt&lt;/h3&gt;

&lt;p&gt;I used "plan mode" to create a detailed set of steps with user experience details and code structure instructions.
I did a few iterations to refine the plan and once I was satisfied, I told it to go forward.
Claude Code took around 9 minutes to build the extension.
I was already thinking "great, I'll install it and move on to a real project".&lt;/p&gt;

&lt;p&gt;I installed it and it failed.
The basics were there, but several details were broken. Also, I forgot to tell it to use &lt;a href="https://context7.ai/" rel="noopener noreferrer"&gt;Context7&lt;/a&gt;, so it targeted an old version of GNOME Shell and it didn't use the latest ESM syntax.&lt;/p&gt;

&lt;h3&gt;👥 The pair programming sessions&lt;/h3&gt;

&lt;p&gt;Going deeper into my rabbit hole, I decided to code the extension myself (with Claude Code's assistance, of course), but using the opportunity to learn about the GNOME Shell extension ecosystem I knew very little about.&lt;/p&gt;

&lt;p&gt;I already had a similar failed "generate everything in one go" experience on another project.
My lack of knowledge and experience on the stack prevented me from being able to fix it myself.&lt;/p&gt;

&lt;p&gt;I deleted everything and asked Claude to start a pair programming session with me.
The idea was to progress step by step, learning new APIs and techniques along the way, and making sure everything worked before going to the next step.&lt;/p&gt;

&lt;p&gt;We started by creating a simple GNOME Shell extension that logs "Hello, world!" when it's enabled and this time, we used Context7!&lt;/p&gt;

&lt;p&gt;For reference, here's the &lt;a href="https://gjs.guide/extensions/" rel="noopener noreferrer"&gt;GNOME Shell Extensions documentation&lt;/a&gt; we used throughout the development process.&lt;/p&gt;

&lt;p&gt;Then we focused on the basic moves we needed for the choreography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listening to a keyboard shortcut&lt;/li&gt;
&lt;li&gt;Reading keyboard shortcuts configurations&lt;/li&gt;
&lt;li&gt;Listing opened windows&lt;/li&gt;
&lt;li&gt;Focusing a window&lt;/li&gt;
&lt;li&gt;Launching a program&lt;/li&gt;
&lt;li&gt;Highlighting a window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For some of these, Claude Code proposed multiple alternative options I had to chose from.
It found like 5 ways to launch an application.
In the end, I never browsed the API docs once and most of the code it produced worked as is.&lt;/p&gt;

&lt;p&gt;Once I had working code for these basic moves, I could have gave another chance to the "generate everything in one go" method.
Instead, I chose to continue iterating and learning...
This allowed me to try different ways to architect the code.
I wanted something really clean and organized and mostly used to browser and Node.js projects.
I also took some time to automate some tooling for the project.&lt;/p&gt;

&lt;h2&gt;🎉 A satisfying outcome&lt;/h2&gt;

&lt;p&gt;In the end, I have a working GNOME Shell extension that does exactly what I wanted.
The first time I had the right behavior, I smiled like a child with an ice cream.&lt;/p&gt;

&lt;p&gt;Now, I can focus my terminal with one hotkey.
I don't really need the dropdown behavior anymore.
I don't need to hide it, I just need to focus another application like my browser or my IDE with a single hotkey.
I'm still in the "muscle memory shift" phase, I actually did a few Alt+Tabs between my markdown editor and the browser while writing this article.&lt;/p&gt;

&lt;p&gt;I pushed the code on &lt;a href="https://github.com/hsablonniere/window-key-switcher" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; but as I'm writing this, I haven't published it on the GNOME Extensions site yet.
I want to take some time to use it for a while first.
If you're curious, you can install it locally and test it, please give feedback.&lt;/p&gt;

&lt;h3&gt;⏭️ The future of this project&lt;/h3&gt;

&lt;p&gt;I guess, I'll see how I use it in different situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on-the-go with laptop only&lt;/li&gt;
&lt;li&gt;classic setup with laptop + monitor&lt;/li&gt;
&lt;li&gt;new mega setup with laptop + monitor + 2 portable vertical monitors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also have a few ideas for improvements and next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to automate test and run them with GJS and/or Node.js with mocks.&lt;/li&gt;
&lt;li&gt;Experiment with having the same app on multiple shortcuts. So if I have 2 terminal windows, I could focus the one I want with just one shortcut. I'm not sure I really need that, and I don't know how I would implement it.&lt;/li&gt;
&lt;li&gt;Refine the README and the CONTRIBUTING guidelines.&lt;/li&gt;
&lt;li&gt;This project gave me a lot of ideas for other extensions, and even to restart my &lt;a href="https://github.com/hsablonniere/window-manager-http" rel="noopener noreferrer"&gt;Window manager HTTP&lt;/a&gt; extension and publish it!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;🤯 Learning new things&lt;/h3&gt;

&lt;p&gt;As I already mentioned, I learned a lot on this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have a better understanding of what GNOME Shell extensions can and cannot do with their APIs (keybindings, windows management...).&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://gjs.guide/" rel="noopener noreferrer"&gt;GJS&lt;/a&gt; JavaScript runtime now supports ESM.
&lt;ul&gt;
&lt;li&gt;No more legacy &lt;code&gt;const MyModule = Me.imports.myModule&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;Some people created &lt;a href="https://github.com/gjsify/ts-for-gir" rel="noopener noreferrer"&gt;type definitions for GJS&lt;/a&gt; and &lt;a href="https://github.com/gjsify/gnome-shell" rel="noopener noreferrer"&gt;GNOME Shell extensions&lt;/a&gt;.&lt;/li&gt;


&lt;li&gt;You can't import from &lt;code&gt;node_modules&lt;/code&gt; directly, but you can bundle your code before installing your extension.

&lt;ul&gt;
&lt;li&gt;I used &lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; and bundled with &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbuild&lt;/a&gt;, and it works great!&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;

&lt;p&gt;In the end, I mostly architected the project myself.
Most of the code was generated and then I reworked it to my tastes.
Claude Code was a really good pair programming buddy and a GNOME Shell extension know-it-all (once I told it to use Context7 and to look up a few extension repos on GitHub).&lt;/p&gt;

&lt;p&gt;I used this project as an opportunity to improve my understanding of LLMs and coding agents
I tried to find a good balance between full blind vibe-coding and typing each character in the &lt;code&gt;.js&lt;/code&gt; files.
I really think my workflow was improved through this process.&lt;/p&gt;

&lt;h3&gt;⚠️ Struggling with challenges&lt;/h3&gt;

&lt;p&gt;This should have been a simple project but I stumbled upon a few difficulties along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The iteration cycle is a bit annoying. Each time I want to test the code, I need to build the extension and reload GNOME Shell. I'm used to live-reload in the browser or automatic restarts with Node.js. Even with the scripts I created, it's still a pain.&lt;/li&gt;
&lt;li&gt;Listening to keyboard shortcuts is kinda limited but I guess it's for security reasons.&lt;/li&gt;
&lt;li&gt;I mentioned there are type definitions for extensions but most people use them in &lt;code&gt;.ts&lt;/code&gt; files. I prefer writing &lt;code&gt;.js&lt;/code&gt; files with JSDoc and use TypeScript CLI &lt;code&gt;tsc&lt;/code&gt; to typecheck. This was a bit harder to understand how to configure.&lt;/li&gt;
&lt;li&gt;It took me some time to figure out what I wanted for the window focus cycle behavior.&lt;/li&gt;
&lt;li&gt;I almost rage-quitted once I realized someone already made &lt;a href="https://extensions.gnome.org/extension/6057/happy-appy-hotkey/" rel="noopener noreferrer"&gt;a very similar extension&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;I don't know how I missed it in my early research.&lt;/li&gt;
&lt;li&gt;I'm glad I built my own solution, it's closer to what I need and I learned a lot.&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;

&lt;h2&gt;🏁 Conclusion&lt;/h2&gt;

&lt;p&gt;What started as simply using Claude Code more intensively led to buying monitors, switching to a new terminal emulator, and ultimately building a GNOME Shell extension.
It even pushed me to write this article, which is so rare, this blog/site  almost became a joke.&lt;/p&gt;

&lt;p&gt;Sometimes the best projects come from scratching your own itch, following the rabbit hole wherever it leads, whatever how deep, especially if you learn along the way.&lt;/p&gt;

&lt;p&gt;Window Key Switcher might be a simple extension, but it perfectly solves my specific workflow needs.
And the journey of building it taught me about an entirely new ecosystem I barely explored before.
It also improved my understanding of LLMs and coding agents.&lt;/p&gt;

&lt;p&gt;That's the beauty of side projects - they're never just about the end result.&lt;/p&gt;



&lt;br&gt;&lt;br&gt;
    &lt;a href="https://github.com/hsablonniere/hsablonniere.com/blob/master/pages/down-the-rabbit-hole-with-claude-code-my-journey-to-window-key-switcher--2qyq57/index.md" rel="noopener noreferrer"&gt;Edit this page on GitHub&lt;/a&gt;&lt;br&gt;

</description>
    </item>
    <item>
      <title>A clock based on Cistercian numerals</title>
      <dc:creator>Hubert SABLONNIÈRE</dc:creator>
      <pubDate>Wed, 17 Mar 2021 00:39:14 +0000</pubDate>
      <link>https://dev.to/hsablonniere/a-clock-based-on-cistercian-numerals-2bbh</link>
      <guid>https://dev.to/hsablonniere/a-clock-based-on-cistercian-numerals-2bbh</guid>
      <description>&lt;p&gt;Let me tell you the story of when I tried Twitch for the first time to live-code a Web Component that displays numbers with Cistercian numerals, so I could build a clock like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fcistercian-clock-time.7e551bea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fcistercian-clock-time.7e551bea.png" alt="Clock displaying the time at 23:38:51 with colored Cistercian numerals" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Yet another side project idea&lt;/h2&gt;

&lt;p&gt;Back in January 2021, I saw &lt;a href="https://twitter.com/Rainmaker1973/status/1348307518045507584" rel="noopener noreferrer"&gt;a tweet&lt;/a&gt; about Cistercian numerals.
I was very intrigued.
If you don't know about them, here's the introduction from the &lt;a href="https://en.wikipedia.org/wiki/Cistercian_numerals" rel="noopener noreferrer"&gt;Wikipedia page&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The medieval Cistercian numerals, or "ciphers" in nineteenth-century parlance, were developed by the Cistercian monastic order in the early thirteenth century at about the time that Arabic numerals were introduced to northwestern Europe. They are more compact than Arabic or Roman numerals, with a single character able to indicate any integer from 1 to 9,999.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fwikipedia-cistercian-numerals.1c0f05d8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fwikipedia-cistercian-numerals.1c0f05d8.png" alt="Some numbers represented with cistercian numerals: 1, 20, 400, 4000, 5555, 6789, 9394" width="688" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I really like how compact and elegant it is to display a big number with just one symbol, especially when there is some symmetry.&lt;/p&gt;

&lt;p&gt;A few days later, I saw &lt;a href="https://twitter.com/aqandrew/status/1349762018639638528" rel="noopener noreferrer"&gt;a tweet&lt;/a&gt; from &lt;a href="https://twitter.com/aqandrew" rel="noopener noreferrer"&gt;Andrew Aquino&lt;/a&gt; saying he created a React component to display any number with Cistercian numerals.
My &lt;a href="https://twitter.com/hsablonniere/status/1357079582499876867" rel="noopener noreferrer"&gt;first reaction&lt;/a&gt; was to tweet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;sudo make web-component&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was expecting someone to create a Web Component, so we could all have a standard compatible way to display Cistercian numbers on our websites, even if we don't use React.
Instead, out of distaste for &lt;code&gt;sudo&lt;/code&gt;, my wonderful colleague &lt;a href="https://twitter.com/Keruspe/" rel="noopener noreferrer"&gt;Marc-Antoine Perennou&lt;/a&gt; created &lt;a href="https://github.com/Keruspe/sudo-ng" rel="noopener noreferrer"&gt;sudo-ng&lt;/a&gt;.
Nothing happened, so I guess I was left with yet another side project idea.
Although this time, I decided to do it live with an audience.&lt;/p&gt;

&lt;h2&gt;Trying Twitch for the first time&lt;/h2&gt;

&lt;p&gt;In the pre-pandemic world, I used to give talks at conferences and share my knowledge and passion for Web development.
When you code in front of hundreds of people in a conference room, everything is well-prepared and rehearsed many times.
There is a kind of illusion of simplicity, but it's the best way to go straight to the point and focus on the new stuff the audience wants to learn about.
The minor drawback is that people miss everything that happens in real life when you write code.
You read some docs, you make mistakes, you try something, you go back, you get stuck on a silly bug...&lt;/p&gt;

&lt;p&gt;Watching someone thinking out loud and writing code without preparation is a wonderful way to learn.
You discover new IDE keyboard shortcuts.
You learn about some unknown language pattern or feature.
It feels a bit like pair-programming with a mentor.&lt;/p&gt;

&lt;p&gt;Following a few &lt;a href="https://www.twitch.tv/nullpointeur" rel="noopener noreferrer"&gt;fellow&lt;/a&gt; &lt;a href="https://www.twitch.tv/k33g_org" rel="noopener noreferrer"&gt;speakers&lt;/a&gt; that got into Twitch during the first lockdown, I decided to create &lt;a href="https://www.twitch.tv/hsablonniere" rel="noopener noreferrer"&gt;my channel&lt;/a&gt; and try the platform for this side project idea.&lt;/p&gt;

&lt;p&gt;My main goal was to create a Web Component to display a number with Cistercian numerals and then reuse it to create a clock component that displays three Cistercian numbers: hours, minutes and seconds.
Bonus points if I could animate the lines of the symbols.&lt;/p&gt;

&lt;h2&gt;The live-coding session&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Ftwitch-session-screencap.4bffd3a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Ftwitch-session-screencap.4bffd3a5.png" alt="Screenshot of the Twich session with an IDE on the left, the browser on the right, and my webcam in the bottom right corner" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested, I posted a &lt;a href="https://www.youtube.com/watch?v=88AKamcJ-bY" rel="noopener noreferrer"&gt;recording of the session&lt;/a&gt; on YouTube but beware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👂 It's in French.&lt;/li&gt;
&lt;li&gt;⌛ It's 2 hours long (120 minutes)&lt;/li&gt;
&lt;li&gt;💬 The chat is NOT displayed.&lt;/li&gt;
&lt;li&gt;😱 I spent way too much time on Inkscape trying to generate the initial SVG design.&lt;/li&gt;
&lt;li&gt;🚧 I should have built both components before thinking about animating them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite those, I really enjoyed giving this session.
Mistakes were made, but we finished with some kind of achievement.
The clock design was a bit clunky, but it worked.
It looked like this:&lt;/p&gt;


  
  Sorry, your browser doesn't support embedded videos.


&lt;p&gt;If I were to do it again, I would prioritize the different steps, so the progress is more linear for the audience.
I would also improve the way I timebox steps, so I would stop insisting on some details.
This retrospective analysis reflects the constructive feedbacks I got from audience members afterwards.&lt;/p&gt;

&lt;p&gt;As a host, this session was really pleasant to give, and it almost didn't require any preparation.
Between 60 and 70 people attended the stream, and some of them really helped me through the chat.
Apart from the code and tips, I think I managed to share the fun I had working on this side project.&lt;/p&gt;

&lt;p&gt;I really want to do it again with other side projects ideas.
I could try more interactivity with the audience through the chat (and maybe some sounds/music).
It's very different from what I'm used to with conference talks.
The number of interesting information per minute is clearly not the same, but I don't think it's a problem in itself.
I think it's just a different way of sharing knowledge with others.
A bit slower, more interactive, less magic but definitely more authentic.&lt;/p&gt;

&lt;h2&gt;Post-stream improvements&lt;/h2&gt;

&lt;p&gt;After the stream, I decided to improve the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the numbers after 99&lt;/li&gt;
&lt;li&gt;Fix the positioning of the different lines&lt;/li&gt;
&lt;li&gt;Add some animations using the &lt;a href="https://jakearchibald.com/2013/animated-line-drawing-svg/" rel="noopener noreferrer"&gt;animated line drawing SVG&lt;/a&gt; technique&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few weeks after, I added a few more features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use CSS custom properties to allow color customization of each lines&lt;/li&gt;
&lt;li&gt;Use CSS custom properties to allow line width customization&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;date&lt;/code&gt; mode for the clock to display year, month, day before hours, minutes, seconds&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;no-seconds&lt;/code&gt; mode for the clock to remove the seconds&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;inline&lt;/code&gt; mode for the number, so you can use a number inside a paragraph of text (as featured in this article if you're reading it from my site)&lt;/li&gt;
&lt;li&gt;Try to improve the accessibility (any feedbacks on this is welcome)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I published the source on GitHub at &lt;a href="https://github.com/hsablonniere/cistercian-numerals" rel="noopener noreferrer"&gt;hsablonniere/cistercian-numerals&lt;/a&gt; if you're curious.&lt;/p&gt;

&lt;h2&gt;Web Components are awesome!&lt;/h2&gt;

&lt;p&gt;Because they're exposed as Web Components, you can use them in any Web project, whatever the stack/framework you're using.
You can install them &lt;a href="https://www.npmjs.com/package/cistercian-numerals" rel="noopener noreferrer"&gt;from npm&lt;/a&gt; but it's not mandatory at all.&lt;/p&gt;

&lt;p&gt;As described in the &lt;a href="https://github.com/hsablonniere/cistercian-numerals/blob/master/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;, you can use these components in your HTML without any &lt;code&gt;npm install&lt;/code&gt;, without any build step or bundler.
All you need is a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag sourcing a modern smart CDN like jspm:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- put this in &amp;lt;head&amp;gt; --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://jspm.dev/cistercian-numerals"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you'll be able to use the number component in your HTML like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;cistercian-number&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/cistercian-number&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also be able to use the clock component in your HTML like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;cistercian-clock&amp;gt;&amp;lt;/cistercian-clock&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because it's just HTML, it's also very easy to integrate in a Markdown document.
It's exactly what I'm doing right now with this article, but you need to browse it from &lt;a href="https://www.hsablonniere.com" rel="noopener noreferrer"&gt;my site&lt;/a&gt;.
Here's the date + time (without seconds) example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fcistercian-clock-datetime.9d5904f9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fa-clock-based-on-cistercian-numerals--hptit8%2Fcistercian-clock-datetime.9d5904f9.png" alt="Clock displaying the date at 2021-3-16 and the time at 23:39 with colored Cistercian numerals" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please see the &lt;a href="https://github.com/hsablonniere/cistercian-numerals/blob/master/README.md#components" rel="noopener noreferrer"&gt;README&lt;/a&gt; for more details about how to use and customize the components.&lt;/p&gt;

&lt;h2&gt;References &amp;amp; Links&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Cistercian_numerals" rel="noopener noreferrer"&gt;Cistercian numerals&lt;/a&gt; on Wikipedia&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=88AKamcJ-bY" rel="noopener noreferrer"&gt;Recording of the Twitch session&lt;/a&gt; on YouTube&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hsablonniere/cistercian-numerals" rel="noopener noreferrer"&gt;Source code&lt;/a&gt; on GitHub
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/hsablonniere/cistercian-numerals/blob/master/src/cistercian-number.js" rel="noopener noreferrer"&gt;Source code for &amp;lt;cistercian-number&amp;gt;&lt;/a&gt; on GitHub&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hsablonniere/cistercian-numerals/blob/master/src/cistercian-clock.js" rel="noopener noreferrer"&gt;Source code for &amp;lt;cistercian-clock&amp;gt;&lt;/a&gt; on GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://www.npmjs.com/package/cistercian-numerals" rel="noopener noreferrer"&gt;JavaScript package&lt;/a&gt; on npm&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://jakearchibald.com/2013/animated-line-drawing-svg/" rel="noopener noreferrer"&gt;Animated line drawing SVG&lt;/a&gt; by Jake Archibald&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit (formerly LitElement)&lt;/a&gt;, the library I used to build the Web Components&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://modern-web.dev/docs/dev-server/overview/" rel="noopener noreferrer"&gt;Web Dev Server&lt;/a&gt;, the dev server I use for live preview&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://adrianroselli.com/2021/02/cistercian-svg.html" rel="noopener noreferrer"&gt;Cistercian SVG&lt;/a&gt; by Adrian Roselli&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://ednl.github.io/cistercian-clock/?size=80" rel="noopener noreferrer"&gt;p5js based cistercian clock&lt;/a&gt; by E. Dronkert&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://hackaday.io/project/178131-monklock" rel="noopener noreferrer"&gt;Arduino + dot matrix cistercian clock&lt;/a&gt; by Danjovic&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://getwatchmaker.com/watchface/cistercian-clock" rel="noopener noreferrer"&gt;Smart watch face cistercian clock&lt;/a&gt; by Philip McGeehan&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://matthewfelgate.wordpress.com/2023/06/26/the-revival-of-cistercian-numerals-a-journey-from-the-15th-century-to-modern-smartwatch/" rel="noopener noreferrer"&gt;Smart watch face cisterian clock (inspired by my design)&lt;/a&gt; by Matthew Felgate&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://twitter.com/aqandrew/status/1349762018639638528" rel="noopener noreferrer"&gt;React based cistercian number component&lt;/a&gt; by Andrew Aquino&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://mz8i.com/cistercian" rel="noopener noreferrer"&gt;React based cistercian number component&lt;/a&gt; by Maciej Ziarkowski&lt;/li&gt;


&lt;li&gt;

&lt;a href="https://github.com/TiroTypeworks/Clairvo" rel="noopener noreferrer"&gt;Clairvo&lt;/a&gt;, a proof-of-concept font that uses OpenType Layout to implement Cistercian numerals&lt;/li&gt;


&lt;/ul&gt;

&lt;h2&gt;Thank you&lt;/h2&gt;

&lt;p&gt;😍 Thank you wonderful people who attended the stream and thank you wonderful reviewers for your time: &lt;a href="https://alexandre.berthaud.me/" rel="noopener noreferrer"&gt;Alexandre Berthaud&lt;/a&gt;.&lt;/p&gt;



&lt;br&gt;&lt;br&gt;
    &lt;a href="https://github.com/hsablonniere/hsablonniere.com/blob/master/pages/a-clock-based-on-cistercian-numerals--hptit8/index.md" rel="noopener noreferrer"&gt;Edit this page on GitHub&lt;/a&gt;&lt;br&gt;

</description>
    </item>
    <item>
      <title>Prevent layout shifts with CSS grid stacks</title>
      <dc:creator>Hubert SABLONNIÈRE</dc:creator>
      <pubDate>Tue, 20 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/hsablonniere/prevent-layout-shifts-with-css-grid-stacks-445e</link>
      <guid>https://dev.to/hsablonniere/prevent-layout-shifts-with-css-grid-stacks-445e</guid>
      <description>&lt;p&gt;There are two reasons why people use CSS grid:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;😎 CSS is awesome! It's a fact, deal with it.&lt;/li&gt;
&lt;li&gt;🛠️ Grid is a great tool to build complex two-dimensional layouts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I sometimes have a third reason to use CSS grid: prevent &lt;strong&gt;layout shifts&lt;/strong&gt;.
I tried to find a fancy acronym for this technique but all I got was ALSGST: &lt;em&gt;"Anti Layout Shift Grid Stacking Technique"&lt;/em&gt;.
I guess I won't be receiving many skill endorsements for "coining tech terms" on my LinkedIn profile so you better send me some suggestions.&lt;/p&gt;

&lt;p&gt;Let me explain the technique using real examples.
In this article, I'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The real layout shift problem I faced with a component I worked on.&lt;/li&gt;
&lt;li&gt;The limitations of solving it with absolute positioning.&lt;/li&gt;
&lt;li&gt;The benefits of solving it with grid.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Wait, what is a layout shift anyway?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A layout shift is what happens when an update in a web page causes something to move significantly.
Most of the time, layout shifts occur during page load while the contents progressively renders.
For example, when an image with an unspecified height gets inserted in the page, just before the very paragraph you were reading, the text moves away and it gets pretty frustrating.
This is also very common when third party scripts (ad services or partnered contents) append new unknown elements in the page.&lt;/p&gt;

&lt;p&gt;This article will focus on layout shifts occuring when toggling state on a UI component.
I won't cover layout shifts happening during page load.&lt;/p&gt;

&lt;h2&gt;Let's get some context&lt;/h2&gt;

&lt;p&gt;I work for &lt;a href="https://www.clever-cloud.com/en/" rel="noopener noreferrer"&gt;Clever Cloud&lt;/a&gt;, an IT automation platform.
Our clients push their code and we handle the rest for them: build, deploy, host, scale, maintain, recover and so on.
When they need to configure their applications, databases and other services, they use our web UI: "the console".
I spend most of my time on this project and it looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fconsole-overview.4895247a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fconsole-overview.4895247a.png" alt="Screenshot of Clever Cloud's Web console displaying the overview of a Node.js application" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you see in the screenshot above is the overview of my own website, a Node.js application, hosted on our platform.
On the right, you can see a pie chart representing the repartition of HTTP response codes returned by the application in the last 24 hours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcc-tile-status-codes-chart.0320b8e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcc-tile-status-codes-chart.0320b8e0.png" alt="UI Component displaying a pie chart" width="400" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I worked on this component, I tried to keep things simple.
I imagined our users would implicitly understand the chart is related to the current application.
To unclutter the display, I decided to move some details like the exact number of requests in tooltips, accessible via mouse or touch.
With the chart all cleaned up, I faced a problem.
How would I explain the following details?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The chart only displays data from the last 24 hours.&lt;/li&gt;
&lt;li&gt;Each item in the legend can be clicked to show/hide the different status code categories. Thank you &lt;a href="https://www.chartjs.org/" rel="noopener noreferrer"&gt;Chart.js&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The available space was too small to add a detailed title at the top or bottom of the chart.
So to help our users figure this out, I added an information button ℹ️ in the top right corner.
When this button is clicked, the chart is hidden and a short text is displayed instead.
It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcc-tile-status-codes-help.4b474b65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcc-tile-status-codes-help.4b474b65.png" alt="UI Component displaying a short text" width="400" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the close button is clicked, the short text is hidden and the chart displayed again.
Implementing this toggle behaviour was not that hard.
Let's take a simplified version of the template:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    HTTP response codes &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;toggle chart/info&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- chart here --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- short text here --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this HTML, I can, when the button is clicked, simply toggle the component state between &lt;code&gt;.chart&lt;/code&gt; and &lt;code&gt;.info&lt;/code&gt; and hide the appropriate panel with &lt;code&gt;display: none&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Layout shift problems&lt;/h2&gt;

&lt;p&gt;This approach has one big problem, it is subject to &lt;strong&gt;layout shifts&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why is that?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I work on such components, I tend to follow what I consider a good practice: don't set a fixed height on the component, let it adapt to its own content.
This is even more important in this situation because I have no way to know how much space is needed to display the short text (without scrollbars).
The minimum required height will depend on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The unknown width of the component, which depends on usage context&lt;/li&gt;
&lt;li&gt;The length of the short text, which depends on the language (English or French)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is, the component height is a bit smaller containing the chart than containing the short text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fheigh-change-en.fb78e297.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fheigh-change-en.fb78e297.png" alt="Two states of the same UI Component displayed side by side. Pie chart on the left, short text on the right. English version." width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue is worse in French because the text is a bit longer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fheigh-change-fr.31e17983.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fheigh-change-fr.31e17983.png" alt="Two states of the same UI Component displayed side by side. Pie chart on the left, short text on the right. French version." width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on the situation, this innocent looking height change can have big impacts on the whole layout and the scroll position.
This kind of details can drive your users crazy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best case scenario, other parts of the layout slightly move.&lt;/li&gt;
&lt;li&gt;Worst case scenario: the button under the mouse cursor moves and something else is placed under (hello Twitter search input).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The video below shows what happens when I toggle between states.&lt;/p&gt;


  
  Sorry, your browser doesn't support embedded videos.


&lt;p&gt;You can observe a first layout shift when I go from "chart" to "info".
The bar chart moves down and the map gets bigger.&lt;/p&gt;

&lt;p&gt;Now look what happens when I scroll down the page and go back to the "chart" state.
Look carefully at the mouse cursor.
Once over the button, I don't move it, yet a simple click triggers a chain reaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The height of the component is reduced to display the chart.&lt;/li&gt;
&lt;li&gt;The height of the whole page is reduced.&lt;/li&gt;
&lt;li&gt;The whole page is scrolled up a bit.&lt;/li&gt;
&lt;li&gt;The mouse cursor is no longer above the button and is now above some text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, how can we prevent this layout shift from happening?&lt;/p&gt;

&lt;h2&gt;The absolute position solution&lt;/h2&gt;

&lt;p&gt;Before CSS grid, I would have solved this problem with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position" rel="noopener noreferrer"&gt;position CSS property&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, I would use &lt;code&gt;visibility: hidden&lt;/code&gt; instead of &lt;code&gt;display: none&lt;/code&gt; to hide the "inactive" panel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fvisibility-hidden-full-height.17735cd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fvisibility-hidden-full-height.17735cd7.png" alt="Two states of the same UI Component displayed side by side. Pie chart on the left, short text on the right." width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, with &lt;code&gt;visibility: hidden&lt;/code&gt;, the "inactive" panel is hidden but the component computes its own size as if both panels were there.
Now, I have a component with a stable height, based on the heights of &lt;code&gt;.chart&lt;/code&gt; and &lt;code&gt;.info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, I would use &lt;code&gt;position: absolute&lt;/code&gt; on the &lt;code&gt;.chart&lt;/code&gt;.
Doing this will &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position#absolute" rel="noopener noreferrer"&gt;remove the element from normal flow&lt;/a&gt;.
This means the component will compute its own size as if &lt;code&gt;.chart&lt;/code&gt; was not there.
Therefore, the component size would mostly depend on the size of &lt;code&gt;.info&lt;/code&gt;, which is exactly what I want.&lt;/p&gt;

&lt;p&gt;The only step left is to give &lt;code&gt;.chart&lt;/code&gt; exactly the same position and size as &lt;code&gt;.info&lt;/code&gt;.
Using &lt;code&gt;position: absolute&lt;/code&gt; means &lt;code&gt;.chart&lt;/code&gt; will be positioned &lt;em&gt;"relative to its closest positioned ancestor"&lt;/em&gt;.
That is, an ancestor with a &lt;code&gt;position&lt;/code&gt; different from the default value (&lt;code&gt;static&lt;/code&gt;).
Most of the time, I use &lt;code&gt;position: relative&lt;/code&gt; on the parent to trigger this.
In this situation, I would need an additional &lt;code&gt;.wrapper&lt;/code&gt; around both panels like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    HTTP response codes &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;toggle chart/info&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- chart here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- short text here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I have this HTML, the CSS solution would look like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* .wrapper is the closest positioned ancestor of .chart */&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.chart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* .chart is removed from normal flow, therefore
     .wrapper has the exact same size (and position) as .info
     because .info is the only child left in the normal flow
     .chart is positioned relatively to .wrapper */&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* same position as .wrapper (thus same position as .info) */&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* same size as .wrapper (thus same size as .info) */&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;position: absolute&lt;/code&gt; solution helps me achieve my goal.
The size of my component will now always depend on the size of &lt;code&gt;.info&lt;/code&gt;, even when it's hidden and &lt;code&gt;.chart&lt;/code&gt; is visible.&lt;/p&gt;

&lt;p&gt;🤔 No more layout shifts when I toggle between states but still a few limitations:&lt;/p&gt;

&lt;p&gt;First, I had to add a &lt;code&gt;.wrapper&lt;/code&gt; with a &lt;code&gt;position: relative&lt;/code&gt; just so I could use &lt;code&gt;position: absolute&lt;/code&gt; on &lt;code&gt;.chart&lt;/code&gt;.
CSS is awesome but it's also tricky to master.
If you're still struggling with how the position CSS property works, this approach can be scary but don't worry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's OK. You're not alone!&lt;/li&gt;
&lt;li&gt;It gets better with time and practice...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Second, I had to assume the component size would only depend on &lt;code&gt;.info&lt;/code&gt;.
In more complex situations, I could have more than just two panels where I cannot assume which one should guide the size of the whole component.&lt;/p&gt;

&lt;p&gt;Now, let's find out how grid can solve this better.&lt;/p&gt;

&lt;h2&gt;The grid solution&lt;/h2&gt;

&lt;p&gt;CSS grid are supported in &lt;a href="https://caniuse.com/css-grid" rel="noopener noreferrer"&gt;all evergreen browsers&lt;/a&gt; now.
This means we can rely on the fact that you can place and overlap multiple elements in the same grid area.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do you mean by grid area?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're not familiar with CSS grid, before continuing this article, I would advise you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch any talk from &lt;a href="https://twitter.com/rachelandrew" rel="noopener noreferrer"&gt;Rachel Andrew&lt;/a&gt;. She's awesome and in this &lt;a href="https://www.youtube.com/watch?v=g1osnSY9mSU" rel="noopener noreferrer"&gt;recent video&lt;/a&gt; she did with &lt;a href="https://twitter.com/jlengstorf" rel="noopener noreferrer"&gt;Jason Lengstorf&lt;/a&gt;, they explain CSS grid through live coding examples.&lt;/li&gt;
&lt;li&gt;Always have the CSS-Tricks &lt;a href="https://css-tricks.com/snippets/css/complete-guide-grid/" rel="noopener noreferrer"&gt;Complete Guide to Grid&lt;/a&gt; open in a tab.&lt;/li&gt;
&lt;li&gt;Practice, practice, practice...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that you're more familiar with CSS grid, we can talk about the now famous &lt;em&gt;"Anti Layout Shift Grid Stacking Technique"&lt;/em&gt; (sorry).
Let's take the initial simple template we had earlier:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    HTTP response codes &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;toggle chart/info&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- chart here --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- short text here --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we use CSS grid on &lt;code&gt;.chart-component&lt;/code&gt; like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.chart-component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our component looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcomponent-grid-all.98c57680.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcomponent-grid-all.98c57680.png" alt="UI Component displaying the pie chart and short text states in a one column grid with Firefox CSS grid inspector" width="400" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the awesome &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts" rel="noopener noreferrer"&gt;CSS Grid Inspector&lt;/a&gt; from Firefox DevTools, we have annotations for columns and rows.
We can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.title&lt;/code&gt;, &lt;code&gt;.chart&lt;/code&gt; and &lt;code&gt;.info&lt;/code&gt; are between column 1 and 2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.title&lt;/code&gt; is between row 1 and 2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.chart&lt;/code&gt; is between row 2 and 3&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.info&lt;/code&gt; is between row 3 and 4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the default behaviour in a simple 1 column grid: children are placed in order.
With &lt;code&gt;grid-column&lt;/code&gt; and &lt;code&gt;grid-row&lt;/code&gt;, we can force an element to be placed in a specific area of the grid.&lt;/p&gt;

&lt;p&gt;For example, if we place our two elements in the same area like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.chart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.info&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;grid-column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;grid-row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcomponent-grid-superposed.e9e1f9bb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fcomponent-grid-superposed.e9e1f9bb.png" alt="UI Component displaying the pie chart and short text states on top of each other with Firefox CSS grid inspector" width="400" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are sceptical, remember the appropriate panel will be hidden with a &lt;code&gt;visibility: hidden&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this technique, we actually told the CSS engine to prepare a grid where the area at column &lt;code&gt;1 / 2&lt;/code&gt; and row &lt;code&gt;2 / 3&lt;/code&gt; should adapt to what's inside.
In other words: this very area will always be as big as the biggest between &lt;code&gt;.chart&lt;/code&gt; and &lt;code&gt;.info&lt;/code&gt;.
Therefore, the height of the whole component will be the same when toggling between states.&lt;/p&gt;


  
  Sorry, your browser doesn't support embedded videos.


&lt;p&gt;😎 No more layout shifts!&lt;/p&gt;

&lt;p&gt;Compared to the absolute position solution, we improved the situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to add a &lt;code&gt;.wrapper&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Would work fine with more than two panels&lt;/li&gt;
&lt;li&gt;No need to assume which panel needs to guide the whole component size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, as a bonus, let me show you another real example where I used this technique.&lt;/p&gt;

&lt;h2&gt;Preventing horizontal shifts&lt;/h2&gt;

&lt;p&gt;The previous example was about the height of a component but you can also use this technique with width.
If you give another look at the Clever Cloud console overview, in the top right corner, you'll notice a few buttons to control your app's state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fconsole-overview.4895247a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fconsole-overview.4895247a.png" alt="Screenshot of Clever Cloud's Web console displaying the overview of a Node.js application" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Stop app" button has bigger horizontal paddings than the other buttons.
This is because, when clicking this very button, the text is replaced with "Click to cancel" for 3 seconds in case you panick changed your mind.&lt;/p&gt;

&lt;p&gt;If we didn't use the grid technique; when the button changes its text from "Stop app" to "Click to cancel", the width change would move all the other buttons like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fbuttons-shift.e0515278.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fbuttons-shift.e0515278.png" alt="Two lines of four buttons next to each other with different widths" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On narrow screens, the wrapping of the buttons could trigger a bigger layout shift like this:&lt;/p&gt;


  
  Sorry, your browser doesn't support embedded videos.


&lt;p&gt;Thanks to the  &lt;em&gt;"Anti Layout Shift Grid Stacking Technique"&lt;/em&gt; (please send naming suggestions, I'm dying), no need to assume which state will be larger.
Whatever the lengths of the &lt;em&gt;normal text&lt;/em&gt; and the &lt;em&gt;cancel text&lt;/em&gt; are, the button will always be big enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fbuttons-no-shift.18a988c1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hsablonniere.com%2Fprevent-layout-shifts-with-css-grid-stacks--qcj5jo%2Fbuttons-no-shift.18a988c1.png" alt="Two lines of four buttons next to each other with the same widths" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;


  
  Sorry, your browser doesn't support embedded videos.


&lt;p&gt;😎 No more layout shifts!&lt;/p&gt;

&lt;p&gt;I hope this article helped you consider this kind of layout shifts as a potential problem to solve.
I also hope you'll find this technique useful in your own projects.&lt;/p&gt;

&lt;h2&gt;References &amp;amp; Links&lt;/h2&gt;

&lt;p&gt;About CSS and grid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position" rel="noopener noreferrer"&gt;CSS position property reference&lt;/a&gt; on MDN&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://caniuse.com/css-grid" rel="noopener noreferrer"&gt;CSS Grid Layout support in browsers&lt;/a&gt; on Can I use&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=g1osnSY9mSU" rel="noopener noreferrer"&gt;Let’s Learn CSS Grid! (with Rachel Andrew)&lt;/a&gt; on Jason Lengstorf YouTube channel&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/snippets/css/complete-guide-grid/" rel="noopener noreferrer"&gt;Complete Guide to Grid&lt;/a&gt; on CSS-Tricks&lt;/li&gt;
&lt;li&gt;Please consider using Firefox and their awesome &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts" rel="noopener noreferrer"&gt;CSS Grid Inspector&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pie chart component and the button component are part of Clever Cloud's component library.
The code is open-sourced on GitHub and the documentation (with live preview) is published with &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find out more about the components we talked about here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.chartjs.org/" rel="noopener noreferrer"&gt;Charts.js&lt;/a&gt;: the JavaScript library used for those charts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;cc-tile-status-codes&amp;gt;&lt;/code&gt;: the pie chart component
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CleverCloud/clever-components/blob/master/src/overview/cc-tile-status-codes.js" rel="noopener noreferrer"&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.clever-cloud.com/doc/clever-components/?path=/story/%F0%9F%9B%A0-overview-cc-tile-status-codes--data-loaded" rel="noopener noreferrer"&gt;Docs with live preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;

&lt;code&gt;&amp;lt;cc-button&amp;gt;&lt;/code&gt;: the button component with "click to cancel"

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CleverCloud/clever-components/blob/master/src/atoms/cc-button.js" rel="noopener noreferrer"&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.clever-cloud.com/doc/clever-components/?path=/story/%F0%9F%A7%AC-atoms-cc-button--delay-and-outlined" rel="noopener noreferrer"&gt;Docs with live preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;

&lt;h2&gt;Thank you&lt;/h2&gt;

&lt;p&gt;😍 Thank you wonderful reviewers for your time: &lt;a href="https://twitter.com/juuduu/" rel="noopener noreferrer"&gt;Julien Durillon&lt;/a&gt;, &lt;a href="https://alexandre.berthaud.me/" rel="noopener noreferrer"&gt;Alexandre Berthaud&lt;/a&gt;, &lt;a href="https://ricaud.me/blog/" rel="noopener noreferrer"&gt;Anthony Ricaud&lt;/a&gt;, &lt;a href="https://twitter.com/mereteresa" rel="noopener noreferrer"&gt;Sarah Haïm-Lubczanski&lt;/a&gt;, &lt;a href="https://twitter.com/jlengrand" rel="noopener noreferrer"&gt;Julien Lengrand-Lambert&lt;/a&gt; and &lt;a href="https://twitter.com/RalfDMueller" rel="noopener noreferrer"&gt;Ralf D. Müller&lt;/a&gt;.&lt;/p&gt;



&lt;br&gt;&lt;br&gt;
    &lt;a href="https://github.com/hsablonniere/hsablonniere.com/blob/master/pages/prevent-layout-shifts-with-css-grid-stacks--qcj5jo/index.md" rel="noopener noreferrer"&gt;Edit this page on GitHub&lt;/a&gt;&lt;br&gt;

</description>
    </item>
  </channel>
</rss>
