This post was originally published at CSS Grid for UI Layouts
CSS Grid is a great layout tool for content-driven websites that include long passages of text, and it has tremendous value for a variety of traditional UI layouts as well. In this article I’ll show you how to use CSS Grid to improve application layouts that need to respond and adapt to user interactions and changing conditions, and always have your panels scroll properly.
CSS Grid builds website layouts. It lets web designers create beautiful dynamic layouts using just a tiny bit of supported code instead of the endless float hacks we’ve had to use for years. My friend and co-worker Jen Simmons has been talking about CSS Grid for years, tirelessly pushing to get it implemented in browsers, and her work has paid off. As of the end of last year, the current version of every major browser, desktop and mobile, supports CSS Grid.
CSS Grid really is powerful, and you can build dynamic content driven websites easily, like in these examples. However, Grid is good for more than laying out pretty blocks of content. Grid gives you full control over both dimensions of your layout, including scrolling. This means features we take for granted in native applications like collapsing side-panels and fixed toolbars are now trivial to implement. No more hacks and no more debugging. Grid just works.
I’ve been building web tools for years. Here’s a screenshot of a game building tool I made for my retro RPGs. When Flexbox first appeared I immediately started using it. I built complex layouts using nested horizontal and vertical boxes, with a few utility classes for things like scrolling and stretching.
Flexbox has certainly made me more productive than absolutely positioned divs and float hacks, but it still has problems. Look at this closeup where panels come together. See how the footers on the left and right don’t line up?
Here’s another screenshot. The toolbar is at the top of the drawing canvas, and according to my framework it should be fixed at the top, but the minute you start scrolling this happens. The toolbar disappears:
Each of these problems can be fixed with more positioning and float hacks, but the result is always fragile. Every time I add a new panel I have to debug my layout all over again; searching to identify which
div is grabbing the extra space during a resize. And the markup is ugly. The nested horizontal and vertical boxes become very complicated, and this example is only two levels deep. As interaction and functionality become more complex the design becomes even more challenging.
<div class='hbox'> <div class='vbox'> <div class='hbox'>header</div> <div class='scroll'> <div class='sidebar'>sidebar</div> </div> <div class='footer'>footer</div> </div> <div class=vbox> <div class='hbox'>button button spacer label spacer button button </div> <div class='center'>main content</div> <div class='hbox'>the footer</div> </div> <div class=vbox> <div class=’hbox’>header</div> <div class=’scroll’> <div class=’sidebar’>sidebar</div> </div> <div class=’footer’>footer</div> </div> </div>
Entering the Second Dimension
The fundamental problem with Flexbox is that it is one dimensional. This makes Flexbox great for one dimensional uses, like toolbars and navbars, but it begins to fail when I need to align content both horizontally and vertically at the same time. Instead I need real two dimensional layout, which is why I need CSS Grid. Fundamentally Grid is 2D.
Here’s a similar kind of layout built with CSS Grid.
Look closely at the bottom footers. They come together perfectly. And by using the
grid-gap for the lines instead of adding borders to each panel, I don’t have to worry about inconsistent grid line widths. Everything just works.
If you’ve studied CSS Grid before then you know you can define your layout using templates for the rows and columns. A template like
200px 1fr 200px will give you 200px wide sidebars with a middle content area taking up the rest of the space. But what happens if the panel should collapse? Right now the column would stay at 200px, even though the content has shrunk. Instead we can use
minmax with the
min-content keyword for the max parameter.
#grid display: grid; box-sizing: border-box; width: 100vw; height: 100vh; grid-template-columns: [start] minmax(auto, min-content) [center]1fr [end] minmax(auto,min-content); grid-template-rows: [header]2em [content]1fr [footer]2em; grid-gap: 1px; background-color: black;
Now the grid column will be always be just wide enough to hold whatever is in any of the columns using their minimum width. Thus if one part of the column (say the header) is wider than the others, the column will expand to fit them all. If they become skinnier or disappear altogether, then the column will adjust accordingly. Essentially we have replicated the expanding/contracting behavior of Flexbox, but made it work with everything in the column together, not just one item. This is real 2D layout.
Here is the code for the rest of the demo.
.start grid-column: start; .center grid-column: center; .end grid-column: end; header grid-row: header; footer grid-row: footer; .sidebar overflow: auto;
<div id="grid"> <header class="start">header</header> <header class="center"> <button id="toggle-left">toggle left</button> ... </header> <header class="end">header</header> <div class="start sidebar">sidebar</div> <div class="center content">the center content</div> <div class="end sidebar"> sidebar<br/> ... </div> <footer class="start">left footer</footer> <footer class="center">center footer</footer> <footer class="end">right footer</footer> </div>
To make the toggle buttons in the upper header actually hide the sidebars I added this code. Note that with modern DOM APIs and arrow functions we can essentially replicate JQuery in just a few lines:
const $ = (selector) => document.querySelector(selector) const $$ = (selector) => document.querySelectorAll(selector) const on = (elem, type, listener) => elem.addEventListener(type,listener) on($('#toggle-left'),'click',()=> $$(".start").forEach((elem) => elem.classList.toggle('closed')) ) on($('#toggle-right'),'click',()=> $$(".end").forEach((elem) => elem.classList.toggle('closed')) )
Also note that CSS Grid does not deprecate Flexbox. We still use Flexbox in the cases where it makes sense: namely one dimensional content like toolbars. Here are the styles that I’m using for my toolbars made out of headers:
<header class="center"> <button id="toggle-left">toggle left</button> <button>open</button> <button>save</button> <span class="spacer"></span> <span>filename.txt</span> <span class="spacer"></span> <button>delete</button> <button id="toggle-right">toggle right</button> </header> header background-color: #ccc; display: flex; flex-direction: row; .spacer flex: 1;
spacer class makes an element take up all of the extra space. By using two spacers between the buttons I can make my toolbar shrink and grow as needed with the filename always in the middle. This is similar to native toolbars.
You can try out a demo live at this Codepen, then remix it to poke and prod.
CSS Grid is wonderful for designing interactive applications with two-dimensional complexity. We can keep the markup semantic. Panels and toolbar line up properly. The
Jen Simmons has started a new YouTube channel, Layout Land to help you grok how Grid works. If you work on web apps or any kind of richly interactive website, you should try out CSS Grid.
This post was originally published at CSS Grid for UI Layouts