CSS

Full-Bleed Layout Using CSS Grid

An elegant solution to a tricky modern layout

Back in the day, there was a gold-standard website layout that everyone strived to create, but that was notoriously difficult to get right: the .

Header
Nav
Content
Ads
Footer

It doesn't seem like it would be so tricky, right? But this was an era before flexbox existed; our tools for the job were tables and floats, and neither were really up to this task. It was technically possible, but some shenanigans were required.

Once flexbox achieved mainstream browser support, this layout went from "holy grail" to "fountain drink"; it was everywhere, because it offered a great user experience, and was within reach for all developers.

As the web has evolved, I've discovered a new aspirational layout. It offers a fantastic user experience, especially for long-form text content like news articles or documentation. But, like its predecessor, it's been deceptively hard to achieve; most implementations require obscure hacks or counterintuitive tricks.

I recently discovered an elegant solution to this problem using CSS Grid. In this post, we'll learn how it works!

Have you ever tried to read Wikipedia on a very large screen? It looks like this:

Screenshot of a wikipedia article about Orange Shirt Day, a Canadian event raising awareness about the Indian residential school system. The text is very wide, since it was taken on a large screen.

Those paragraphs are so wide! Wikipedia doesn't constrain the container width at all. This leads to lines that are hundreds of characters in length.

It's hard for our eyes to wrap around when we reach the and of a line. If you're like me, you wind up using your mouse to assist:

In addition to the line-wrapping concern, it's just generally hard to read lines of text that are so wide; it fatigues the eye.

Research has shown that the ideal line length is about 65 characters. Anywhere between 45 and 85 is generally seen as acceptable, in the context of a roman alphabet. Reading is a complex process, and we should strive to make it as easy as possible.

The standard solution to this problem is to create a single fixed-width column in the center of the page. You've seen this layout everywhere: online magazines, documentation, news sites, and blogs. You're looking at it right now, on this site!

Screenshot of this website, showing that there is a centered fixed-width column, with flexible blank space on either side

There's a complicating factor, however—not all content should be constrained. We should allow images, videos, and custom widgets to break free and fill the available width:

Random photo of meerkat

This meerkat used as an example of a child that breaks free of constraints. Photo By Sean Paul Kinnear.

The common term for this kind of thing is “full-bleed”. It's a term borrowed from the publishing world; when something is printed full-bleed, it extends to the very edge of the paper.

This new requirement makes the problem considerably more tricky. It's relatively easy to constrain all children, but CSS doesn't really have a mechanism to selectively constrain some children.

Let's start at the end, with our solution:

css

These styles are assigned to markup in this shape:

html

There's a lot to unpack here, so let's go through it step by step.

css

If you're not familiar with CSS Grid, this might seem like a lot of random characters and keywords. Never fear! All will be explained.

grid-template-columns is a property that lets us define the shape of our grid. By providing 3 discrete values, we're indicating that we want 3 columns.

The values define the width of each column. The first column is 1fr, same as the last column. The fr unit is a flexible unit that fills available space. It's similar in principle to flex-grow; it's a ratio of how much of the free space the column should consume.

Our center column is a fixed width. We use the min helper to pick whichever value winds up being smaller. On large screens, it will take up 65ch width. On smaller screens, where there isn't enough horizontal space for 65 characters, it is clamped to 100% of the available container width.

Here's what this looks like, in practice:

1fr
65ch
1fr

Link to this heading
Assigned children column

We've defined a 3-column grid, and now it's time to assign children to it.

By default, children will be slotted into the first available grid cell. We want to override this default behaviour though; all children should sit in the center column, leaving the first and third columns empty.

css

In CSS Grid, columns are 1-indexed, so 2 is a reference to the middle column.

The asterisk (*) is a wildcard; it'll match elements of all types. We're saying that every child should be assigned to that second middle column. Each new child will create a new row, like so:

<h1>
<p>
<p>
<p>

Link to this heading
Full bleed children

We've seen how our grid can constrain elements of all types, but what about when we want a child to break free and fill the available width?

That's where this fella comes in:

css

This special .full-bleed class allows a specific child to bust out of that column, and span all 3 columns. 1 / 4 is a start/end syntax; we're saying the element should start on Column 1 (inclusive) and span all the way to Column 4 (exclusive).

<h1>
<p>
<img class="full-bleed">
<p>

The trick is that each child becomes its own grid row, and each child can fill as much of that row as it wants. Most elements will only ever occupy that center column, but some will instead span all 3.

The wonderful thing about this technique is that it's super flexible.

Sometimes, you won't want a child to truly be "full-bleed"; on ultra-wide monitors, this could be quite large. With a small tweak, we can create a pseudo-full-bleed variant that clamps its width to some large value:

css
<h1>
<p>
<img class="pseudo-full-bleed">
<p>

CSS Grid is super powerful, and now that it's achieved wide browser support, it can solve so many of our problems!

Some readers have suggested that the same effect could be accomplished with flexbox, or without the use of a wrapper at all. Unfortunately, there are some trade-offs that make those alternatives unworkable. It's beyond the scope of this article, but I wrote up a couple points in this HackerNews comment.

The historical solution to this problem uses negative margins. It works perfectly well, but it feels a bit hacky to me. You can read more about that solution on CSS Tricks.

I plan to write much more about CSS Grid, so be sure to join the newsletter if you'd like to learn more. 💖

A front-end web development newsletter that sparks joy

My goal with this blog is to create helpful content for front-end web devs, and my newsletter is no different! It includes to upcoming posts and access to special bonus goodies. No spam, unsubscribe at any time.