Menu

The “Inside” Problem

May 20th, 2019

So, you’re working on a design. You need a full-width container element because the design has a background-color that goes from edge-to-edge horizontally. But the content inside doesn’t necessarily need to be edge-to-edge. You want to:

  1. Limit the width (for large screens)
  2. Pad the edges
  3. Center the content

It’s “the inside problem” in web layout. It’s not hard, it’s just that there are lots of considerations.

The classic solution is an outer and inner container.

The parent element is naturally as wide as it’s own parent, and let’s assume that’s the <body> element, or the entire width of the browser window. That takes the background-color and pads the left and right sides. The inside element is what limits the width inside and centers.

<footer>
  <div class="inside">
    Content
  </div>
</footer>
footer {
  --contentWidth: 400px;
  background: lightcoral;
  padding: 2rem 1rem;
}

.inside {
  max-width: var(--contentWidth);
  margin: 0 auto;
}

This is what my brain reaches for first. Doesn’t use anything fancy and feels perfectly understandable. That “inside” element isn’t wonderfully desirable, only because it feels like busywork to remember to add it to the markup each time this pattern is used, but it does the trick with few other downsides.

See the Pen
Classic “inside” element
by Chris Coyier (@chriscoyier)
on CodePen.

What if you only can use a single element?

These type of limitations aren’t my favorite, because I feel like a healthy project allows designers and developers to have whatever kind of control over the final HTML, CSS, and JavaScript output they need to do the best possible job. But, alas, sometimes you’re in a weird position as a contractor or have legacy CMS issues or whatever.

If you only have a single element to work with, padding sorrrrrta kinnnnda works. The trick is to use calc() and subtract half of the content’s maximum width from 100%.

<footer>
  Content
</footer>
footer {
  --contentWidth: 600px;
  
  background: lightcoral;
  padding: 2rem calc((100% - var(--contentWidth)) / 2);
}

See the Pen
VOYxOa
by Chris Coyier (@chriscoyier)
on CodePen.

The problem here is that it doesn’t prevent edge-touching, which might make this entirely unacceptable. Maybe you could select elements inside (paragraphs and whatnot…) and add padding to those (with a universal selector, like footer > *). It’s tempting to put padding way up on the <body> or something to prevent edge-touching, but that doesn’t work because we want that edge-to-edge background color.

What if you’re already inside a container you can’t control and need to break out of it?

Well, you can always do the ol’ full-width utility thing. This will work in a centered container of any width:

.full-width {
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

But that leaves the content inside at full width as well. So, you’d need to turn to an inside element again.

See the Pen
Full width element with inside element
by Chris Coyier (@chriscoyier)
on CodePen.

Also, as soon as you have a vertical scrollbar, that 100vw value will trigger an obnoxious horizontal scrollbar. Some sites can pull off something like this to get rid of that scroll:

body { overflow-x: hidden; }

That’s pretty nice. If you can’t do that, though, you might need to set an explicit width on the scrollbar, then subtract that from 100vw.

body {
  scrollbar-width: 20px; /* future standards way */
}

body::-webkit-scrollbar { /* long-standing webkit way */
  width: 20px;
}

.full-width {
  width: calc(100vw - 20px);
}

Even that kinda sucks as it means the full-width container isn’t quite full width when there is no vertical scrolling. I’d love to see CSS step it up here and help, probably with improved viewport units handling.

There are a variety of other ways of handling this full-width container thing, like Yanking to the edges with margins and such. However, they all ultimately need viewport units and suffer from the same scrollbar-related fate as a result.

If you can definitely hide the overflow-x on the parent, then extreme negative-margin and positive-padding can do the trick.

This is kinda cool in that it uses nothing modern at all. All very old school CSS properties.

See the Pen
Full Width Bars Using Negative Margins
by Chris Coyier (@chriscoyier)
on CodePen.

Can CSS Grid or Flexbox help here?

Meh. Not really.

I mean, sure, you could set up a three-column grid and place the content in the center column, while using the outside columns as padding. I don’t think that’s a particularly compelling use of grid and it adds complication for no benefit — that is, unless you’re already using and taking advantage of grid at this scope.

Fake the edges instead.

There is no law that the background-color needs to come from one single continuous element. You could always “fake” the left and right sides by kicking out a huge box-shadow or placing a pseudo element wherever needed.