Full Width Containers in Limited Width Parents

Avatar of Chris Coyier
Chris Coyier on

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

I bookmarked a tweet a few months ago, as it was a bonafide CSS trick and that’s what we love around here.

The issue is: how do we make a full-browser-width container when we’re inside a limited-width parent?

Starting Point

This image is 100% wide as it’s limited-width parent.

We want to stretch it exactly as wide as the browser window.

Here’s some minimal assumed markup:

<!-- parent -->
<main>

  <p>Stuff.</p>

  <!-- container we want to be full width -->
  <figure class="full-width">
    <!-- could be whatever content -->
    <img src="dog.jpg" alt="">
  </figure>

</main>

Considerations

If we could use absolute positioning, we could set the container to be at left: 0; and width: 100%; – but we can’t, because we want the container to remain in flow.

Can’t we just apply some negative margin to each side and pull it outwards? In some circumstances, we can!

With Known % Width

Let’s say the parent container was 60% wide and centered. That means there is 20% width on either side of it. But margin is calculated based on the parent element, so to pull it to the left 20% of the browser window, you’d need 1/3 of the width of the parent, so…

main {
  width: 60%;
  margin: 0 auto;
  /* creates 20% margins on either side */
}
.full-width {
  /* 1/3 of 60% = the 20% margin on either side */
  margin-left: -33.33%;
  margin-right: -33.33%;
}

With Known Non-% Parent Width

Under most other circumstances, we just don’t have enough information to know exactly how far to pull out the full width container with negative margins.

Well, unless…

We could use the width of the browser window in our CSS math. The amount we want to “pull” to the left and right is half the width of the browser window plus half the width of the parent. (Assuming the parent is centered.)

So, so our parent is 500px wide:

.full-width {
  margin-left: calc(-100vw / 2 + 500px / 2);
  margin-right: calc(-100vw / 2 + 500px / 2);
}

A fixed width like that feels a little red-flaggy (i.e. what happens on narrower screens?), all this would likely be wrapped in a media query that makes it only apply on larger screens:

@media (min-width: 500px) {
  main {
    width: 500px;
    margin: 0 auto;
  }
  .full-width {
    margin-left: calc(-100vw / 2 + 500px / 2);
    margin-right: calc(-100vw / 2 + 500px / 2);
  }
}

Since our demo was an image, you might also do something like .full-width img { width: 100%; } too, to get that full coverage.

If it’s less brain bending, you might have luck reducing to:

@media (min-width: $max-width) {
  .full-width {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
  }
}

Translate

Since animations (probably) aren’t involved here, it’s probably not super necessary, but you could pull the container back to the edge with transforms instead.

@media (min-width: 40em) {
  .full-width {
    width: 100vw;
    transform: translateX(calc((40em - 100vw)/2));
  }
}

@supports()

The above idea was taken from a Pen by Brandon Mathis where he combined that idea with wrapping it all in @supports.

/* See warning below */
@supports (width: 100vw) {
  .full-width {
    width: 100vw;
  }
  @media all and (min-width: 40rem) {
    .full-width {
       transform: translateX(calc((40rem - 100vw)/2));
    }
  }
}

The idea here is that none of this would apply if the browser didn’t support viewport units. That way you could pull fallback behavior above this in the CSS and let this override it if it can.

Seems like a good idea, but in my testing only Firefox got this right. Chrome would sometimes incorrectly apply the media query when it wasn’t supposed to (screenshot). (I say “sometimes” as it seemed like a redraw would “fix” it, only to not apply the media query the other direction.) Edge also didn’t seem to apply the media query rules at all (screenshot). Maybe nested @-rules are a bit buggy yet.

No calc() needed

Sven Wolfermann followed up Jon Neal’s idea with a clever variation that doesn’t need calc():

.full-width {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

The idea here is: push the container to the exact middle of the browser window with left: 50%;, then pull it back to the left edge with negative -50vw margin.


Very clever! This way you don’t need any information about the parent width at all. Do note that both this and the calc() version require the parent to be exactly centered in the browser.

You might even think: why bother with the right and margin-right? It’s true you don’t really need it on a left-to-right site, but if there is any chance of direction: rtl; happening, you’ll need the right properties, so having both is more bulletproof.

I’m gonna put a fork of this method here, as it seems the most useful.

See the Pen full viewport width image (container) inside article by Chris Coyier (@chriscoyier) on CodePen.