Breaking Out With CSS Grid Layout

Written by Tyler Sticka on

A while back I shared a simple technique for allowing certain elements to fill the full viewport width from within a fixed-width container:

.u-containProse {
  max-width: 40em;
  margin-left: auto;
  margin-right: auto;
}

.u-release {
  margin-left: calc(-50vw + 50%);
  margin-right: calc(-50vw + 50%);
}

As concise as this technique was, it had one big, dumb caveat: On some platforms, you’d see a horizontal scrollbar unless you set overflow-x to hidden on the body element.

It’s now a year later and every modern browser supports (or will soon support) CSS Grid Layout (hooray!!). I thought it might be fun to reexamine this problem with a fancy set of modern layout tools at our disposal.

Our Goal

In my last article about Grid, I mentioned how important it was to approach layout problems with fresh eyes so you aren’t misguided by years of workarounds and CSS muscle memory. So let’s review what we hope to accomplish from a design standpoint:

  • We want to limit the width of prose content to promote readability.
  • But sometimes, the reader may benefit from an element being full-width. For example, a nice photograph or a detailed chart.
Sketch of our intended responsive layout

If we sketch this out, we can see that what we’re really after isn’t so much a fixed-width container with margins… it’s more of a three-column layout, with a main column flanked by two gutters. Most content will fill only the middle portion, but our super-fancy visual content will span all three.

With that in mind, it’s time to write some CSS!

Column Like You Sees ‘Em

Let’s start by styling our containing element, which will wrap the article content:

.Prose {
  display: grid;
  grid-template-columns: 
    [full-start] minmax(1em, 1fr) 
    [main-start] minmax(0, 40em) [main-end]
    minmax(1em, 1fr) [full-end];
}

Here we’ve defined three columns. The first and last are our gutters, with a minimum size of 1em but the ability to stretch to fill one unit of available space. Between them is our main content column, which can stretch to a maximum width of 40em.

Graphic showing relationship between column template and end result

We’ve also assigned names (in brackets) to the dividing lines between columns. These are entirely optional, but they’ll make the rest of our styles easier to read, write and maintain.

Next, we instruct all child elements of Prose to neatly occupy our main column:

.Prose > * {
  grid-column: main;
}

And finally, we make a class for elements we’d like to span the full container width:

.Prose-splash {
  grid-column: full;
}

And that’s it! Here’s the end result, no overflow-x or negative margins required:

Animation of demo resizing

But that’s not all…

Cool New Tricks

Because full-width elements are based on their container and not the viewport, this technique works great for asymmetric layouts:

Screenshot of second demo

This also means full-width elements may reliably align with content above and below, which provides some fun opportunities for styling special elements like block-quotes or code blocks:

Screenshot of third demo

We can also consider utilizing the gutter space in novel ways that previously required much more hackery:

Screenshot of fourth demo

Inevitable Downsides

While this technique has some really promising qualities, there are also some drawbacks to consider:

  • You can’t float immediate children of an element with display: grid.
  • Grid is being adopted very rapidly, but it’s still not as well supported as what I shared last year.
  • Many aforementioned benefits also apply to using *:not() selectors instead.

Those caveats may be deal-breakers for many, but I predict these sorts of techniques will grow more attractive as CSS Grid Layout becomes more and more prevalent. It seems only natural for our content to extend the composition of our interfaces.

Special thanks to Erik Jung for his demo assistance and technical review of this article.

Tyler Sticka

Tyler Sticka is Cloud Four's Creative Director, allowing him to think about responsive design patterns every day. When he isn’t sketching on sticky notes, experimenting with SVG or nitpicking design details with his coworkers, he enjoys reading comics, making video games and listening to weird music. He tweets as @tylersticka.

Never miss an article!

Get Weekly Digests


Comments

Add a comment

full-width elements may reliably align with content above and below

How is this achieved? In the screenshot with the full-width code block, how do you align the code with the content above/below?

Replies

The screenshot links to a working CodePen demo where you can try it out, but here's the meat of it:

.Prose > figure {
  align-items: end;
  display: inherit;
  grid-template-columns: inherit;
}

.Prose > figure > div {
  grid-column: main / full;
}

.Prose > figure > figcaption {
  order: -1;
  text-align: right;
}

Wow! This looks like a great way to set up a website. I will definitely keep this in mind when working on future designs.

I did notice a bug on Safari 10.1 for Mac: the navigation on the left column falls below the splash image, at least when viewed in CodePen.

How would this work if you had multiple columns in the main section? Is that possible without a sub grid? I tried something similar with gutter - 12 columns - gutter but couldn't get the column elements to tile normally inside the gutters.

I've always wandered why web designers prefer to put two gutters on both sides of copy over just increasing font size (font-size: 2vw). This must drive people with poor sight crazy. Big font does not mean childish looking page.

Replies

I agree that many designers rely on font sizes that are too small to read comfortably, and viewport units provide some solutions to that. Unfortunately, it's not quite that simple. Although a font-size of 2vw looks perfectly readable on a wide screen, here's how that text would look at 320px wide:

If we bump that up to font-size: 5vw we'll get 16px text on narrow screens, but wide screens end up looking like a Snellen chart:

Here at Cloud Four, we've been using some variation of techniques described in detail by Zell Liew and Michael Riethmuller to get the benefits of viewport-based type sizes while avoiding those extremes. But that means line lengths can still extend past what's readable, which is where fixed-width containers (using proportional units like em, rem or ch that will scale with the font-size) still come in handy.

I was really intrigued on how you've done the background stretch of the first example, because it wasn't just an image with width="100%", it was something else.

Let me tell you that your solution with linear-gradients for the background is so clever that it feels you are cheating! 👏

This is a fantastic technique! My problem became the hierarchy of the HTML. Grid, at this point, requires a flat markup structure. My approach was to us a CSS custom property to store (and adjust with media queries) the grid template columns. This allows me to easily re-apply the same structure to children elements.

Replies

That's a fine approach! In some of this post's demos I rely on inherit values to pull off something similar.

The ideal solution would be Subgrid, but it was moved to Level 2 of the CSS Grid specification. You can find out more about that decision and what you can do to help prioritize the feature in this post by Rachel Andrew.

Yes - from what I could tell position: grid (and there's some FF-only property like it, I believe) would also work. But they aren't supported right now.

I had a two-column grid with content spilling out of the second column when the grid was centered on the page with margins. Following your example and adding columns for the gutter stopped the spillage. Thank you.

Leave a Comment

Please be kind, courteous and constructive. You may use simple HTML or Markdown in your comments. All fields are required.


Let’s discuss your project! Email Us