Thoughts & Writings

Don't contain yourself

Cleaner html with just a few extra lines of beautiful css

Published on

For years I've been using the same structure for responsive containers. I used some sort of container class as to keep the main content from growing over a specific treshold. Maybe it was the use of frameworks like Bootstrap that had blinded me, but it's not the only way. But now I see the light and I'm ditching the container as default solution :-)

Bootstrap is really something. It helped me overcome the first years of the responsive web, giving me a solid base to build on while finding out my own path in the new web. But using a pre-built system it's key to understand what's going on and to consider if that's really what you want. I haven't used bootstrap for a long time now, but one bit of bootstrap residue was still in the systems that I build.

Everything in bootstrap was in this .container class. It sets a max-width for the content. One of the first things I did was making it fully repsonsive something like max-width: 50em; margin: 0 auto;. But still I used the container in every site I made.

And that is ok.. But there's something with the designers I work with. They're nice and all, but they just don't want to be contained (positive thing ;-). So they make designs where the top navigation is from edge to edge, the textual content is contained in the center of the page but halfway down the page there's suddenly a quote that reaches from edge to edge in the viewport. Something like this:

Now the way bootstrap dictates building this is using multiple containers. Something like this:

<nav>
    <div class="container">
        ...nav content...
    </div>
</nav>

<div class="container">
    ...text content...
</div>

<blockquote>
    <div class="container">
        ...quote content...
    </div>
</blockquote>

<div class="container">
    ...more text...
</div>

<footer>
    <div class="container">
        ...footer content...
    </div>
</footer>

The nav and blockquote can now be given a colored background and all is fine. Right? Right?!?!

It does the trick, but it's far from perfect. A major caveat would be that if a blob of text and elements comes from a CMS it's pretty tough to interupt the blob container to make way for the fullwidth blockquote or figure.
And then there's semantics: These container divs don't do anything for the content.

Break free

An element can be pulled out of it's container by applying negative margins to the left and right. Let's consider some markup like this:

<div class="container">
    ...text...
    <blockquote>...</blockquote>
    ...text...
</div>

By applying negative margins to the blockquote we can create a highlighted section without interupting the parent container.

blockquote {
    margin-left: -5em;
    margin-right: -5em;
}

This would result in something like this:

Pretty nice, but not so much for screensizes smaller than the container size. The box will be pulled out of the viewport. A fix to prevent the horizontal scrollbar on these smaller screens would be to add a media query to only apply the negative margins when this would fit. If the container size is 50em and the margin-left and right is -5em we add both the margin amounts plus the container width and then we have the viewport width needed to fit the pullout in: 5em + 5em + 50em = 60em.

@media screen and (min-width: 60em) {
    .pullout-slightly {
        margin-left: -5em;
        margin-right: -5em;
    }
}

I like this trick for highlighting a video or figure in column of text. It gives some rythm to the layout of pages with larger amounts of text. But in the end, it's pretty static. It does not grow further when the viewport grows.

Step it up – stretch to the edge

By using the css calc() value in combination with the vw unit it's possible to stretch the pullout element to exactly the edge of the viewport. If we subtract the container width (50em) from the viewport width (100vw) we know how much both margins are together. But for the margin-left and margin-right value we need to divide this by two. I do this by cutting both values in half. For the media query, the pullout can be done as soon as the viewport is wider than the container. So with a container of 50em the styling can be like this:

@media screen and (min-width: 50em) {
    .stretch-to-the-edge {
        margin-left: calc(25em - 50vw);
        margin-right: calc(25em - 50vw);
    }
}

Unfortunately this is not the whole story :-/ There's something to consider when using the vw unit: It includes the scrollbar width (usually 1em). So 100vw is the body width plus the scrollbar width. To make up for this force the scrollbars on the body and divide the 1em and add it to the calculation:

@media screen and (min-width: 50em) {
    body {
        overflow-y: scroll;
    }
    .stretch-to-the-edge {
        margin-left: calc(25.5em - 50vw);
        margin-right: calc(25.5em - 50vw);
    }
}

Restore padding

Stretching the element to the viewport edges looks nice for a <figure> but usually the content itself must be aligned with the the parent container. Like the quote in the above design.

To accomplish this apply the same amount of padding as the negative margin.

@media screen and (min-width: 50em) {
    .stretch-and-pad-back {
        margin-left: calc(25.5em - 50vw);
        margin-right: calc(25.5em - 50vw);
        padding-left: calc(50vw - 25.5em);
        padding-right: calc(50vw - 25.5em);
    }
}

Drop the container

With this setup, we only need one container! Yay!

<body>
    <div class="container">
        ...all content goes here...
    </div>
</body>

Now here's the thing: the body is very well fit to be the container itself. And since I'm pretty sure there's really only one <body> element in the page, giving it a .container class isn't even needed to style it.

<body>
    ...all content goes here...
</body>
body {
    max-width: 50em;
    margin: 0 auto;
    padding: 1em;
}

Now that's the html that I like! Clean and easy to handle. Remember the first code example I mentioned? Ditching all the container classes gives a much more :

<nav class="stretch">
    ...nav content...
</nav>

...text content...

<blockquote class="stretch">
   ...quote content...
</blockquote>

...more text...

<footer class="stretch">
    ...footer content...
</footer>

Playground on Codepen

If you like to play around with these code examples check out this codepen. I have been testing these techniques and these experiments are in this other codepen.

p.s. If you're doing things with padding in the element itself, it's also possible to create a ::before or ::after element, place that absolute behind the element to be stretched, and pull that to the edges of the screen. Check out the last example in this codepen

p.p.s.This stuff is not fully tested in all browsers. One issue I do know exists is that chrome on windows needs one extra pixel of spacing.

...

Every now and then, rethink what you're doing & Make things better :-)