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:
- Limit the width (for large screens)
- Pad the edges
- 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 "inside" issue. Centered, max-width/padding containers inside full-width color bands.
What's your favorite pattern?
(Just a fun every day thing with many possible solutions to consider!) pic.twitter.com/pNYf5YsQMp
— Chris Coyier (@chriscoyier) March 14, 2019
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.
I’ve accomplished this before with pseudo
::before
and::after
elements. It was a little hacky and has some horizontal overflow caveats, but got the job done in some very restrictive situations where I had no control over the structure. Normally, I achieve this with a<section>
element that has a child container for content, similar to your “classic” solution, and the parent section elements are always full width.I think grid is perfectly fine for this. See the updated pen for an example: https://codepen.io/tmassman/pen/JqryVZ
Why do you want to do the math if browsers can do it for you? The techniques you show are perfectly fine for browsers not supporting grid.
One issue with the demo as-is:
Ah I totally missed that. Thanks for the hint! I updated the pen to take the left/right padding into account:
Great approach exactly what I was going to comment on as well, grid can definitely solve this issue fairly easily.
For the
.full-width
utility, I believe you can skip the transform altogether (though it doesn’t address your following point concerning scrollbars). I think it was Ana Tudor who made a note of this on Twitter:That’s great article, Chris. Thank you for lumping these different methods together in one piece. I’ll definitely use these hints in my future work :)
An invisible borders could be added to that single element to prevent edge touching. Far from ideal but if you don’t have control of your html you sometimes have to make compromises.
For my last play around project I wanted to avoid using margin or padding at all while achieving the following:
The parent element holds different stuff: headings, paragraphs, images, quotes, etc. It could be an article element.
Most of the child elements will have a max-width in order to prevent too long text lines.
Theses textual elements need a margin to the left and right screen border on mobile screens.
Some child elements will go full width or a wider width, e.g. images that should “pop” or big fancy text quotes, etc.
I ended up with following solution that worked for me:
What do you think about my approach?
Not really what do you mean by edge-touching
For the single element option, you could include a padding value in your calculations to ensure there’s no edge touching on smaller screens. And for the version where you’re breaking out of an existing container, you could apply the background color to a pseudo element and give that that 100vw width and negative transforms, leaving the regular element as-is (except for applying position: relative as needed).
This is how we currently do it:
The only annoyance is creating the max-width breakpoint for contain, which needs to be calculated based on the max-width you want plus the previous breakpoints left and right padding, so that you have a clean transition and no touching edges.
I was able to accomplish this pretty easily by absolutely positioning a
::before
element without specifying itstop
: https://codepen.io/mpetrovich/pen/zQpjrYAndy Bell provides a nice write-up, Creating a full bleed CSS utility, at his blog: https://andy-bell.design/wrote/creating-a-full-bleed-css-utility/
I’ve been doing it like this for the last 4 or 5 years… no extra markup needed.
Works anywhere that supports viewport units and fails nicely.
Sometime needs z-index and overflow adjustments on the body for good measure but otherwise pretty robust.
What about using the padding to calculate the breakpoint?
I needed a layout to allow for a contained 2 column row with a full-width 2 column/color background. I basically used a responsive
::before
full-width utility on the columns.