Let’s say you’re making a blog post layout. Content is entered into a CMS inside a WYSIWYG editor field. You echo that content to the page. You pull it up on a mobile device and notice the paragraphs go edge-to-edge. Yikes, it’s a little uncomfortable. So you add some kind of left/right padding maybe using a div.container .

This works great until the client asks for the images and video to go full bleed. Your universal padding solution no longer works well. You have a few options:

Write </div><div class="container"> open/close container markup in your WYSIWYG. God help you if you choose this route. Split $post->content into separate chunks in the CMS. If you choose this route, you write front-end and backend logic to render each content chunk (containered and container-less) accordingly. This is more to maintain. You’ve also fractured your content in the database just to achieve a visual style which is not a great separation of concerns. No container, but explicitly add padding to each individual child element except images. If you go the this route, your CSS ends up looking like this:

.post > h1, .post > h2, .post > h3, /* Repeat for every block level element in the HTML */ .post > p { margin-left: auto; margin-right: auto; max-width: 50rem; padding-left: 5%; padding-right: 5%; }

This works but there’s always something you didn’t consider. Some images go full-width, some don’t. All iframes or just YouTube and Vimeo? You manage both the inclusions and exclusions to the rule. A few client requests down the line and your CSS is super bloated.

Let’s try using *:not() instead

Because I’m lazy, I started thinking about a cheaper way to write all that CSS. I found a simple and elegant solution using the CSS Level 3 :not() selector.

.post > *:not( img ):not( video ) { margin-left: auto; margin-right: auto; max-width: 50rem; padding-left: 5%; padding-right: 5%; }

Now I’m only managing exceptions. I can see myself using this trick on my own blog to break out of the boring but dependable “tube of content”. This could extend to lots of elements:

.post > *:not(img):not(video):not(table):not(iframe[src*="codepen"]) { /* ... */ }

Anything I want to embiggen to break out of the container, I just add it to the list. Using :not() feels really versatile.

Colored background tiers

This technique could be extended to give a section or div a different colored background (as marketing sites tend to do) AND then be recycled so that children of that div get the same rules as the faux-container.

.post > *:not( .colored-bg ), .colored-bg > * { margin-left: auto; margin-right: auto; max-width: 50rem; padding-left: 5%; padding-right: 5%; } .colored-bg { background: lightgray; padding-top: 5%; padding-bottom: 5%; }

Demo: Blog post layout

To test this theory out, I started on a generic blog post layout that at ~70 lines of CSS is pretty close to meeting all of my layout needs.

See the Pen Universal padding using CSS :not for full bleed exceptions by Dave Rupert (@davatron5000) on CodePen.

I like where this is going

I want to be clear, this has some limitations. * -selecting can be expensive in CSS if your page is huge. While you specify CSS selectors left-to-right, CSS evaluates selectors right-to-left. Overriding a :not() selector is kinda hard (see blockquote in the demo). The resize calculation might also be costly to browsers.

All that said, I think I’m going to use the hell out of this. I like that I’m not opening and closing utility containers. I like that I can be as explicit as needed. I like that it’s a relatively cheap and easy way to break away from a boring tube of content, even if just ever so slightly.

I’m on a couple projects right now where wrapper divs are really causing a ruckus and impeding the flexibility of our code. Chris Coyier and I even talked about this in a recent Shop Talk Show rapidfire. This seems like a nifty workaround for those problems.

And because we’re all thinking it…