Making Purple: Pretty Gutters in CSS3

4.1.2011

CSS3 selectors can help make proper gutters in floated content. This problem was posed by LifeHacker.

Today, LifeHacker had a bounty out on CSS. The basic challenge was to make purple as one of their commenters said. By this, they meant creating a gutter in text next to an ad that also allowed for an image.

Why is this Hard?

Making a gutter that can push text out of the way and allow it to flow naturally is an everyday action in CSS. Float an image left, give it right, top, and bottom margins. Or float an image right and give it top, left, and bottom margins.

However, the tricky bit comes when you try to give the left image a margin and the right image a margin, and have these images overlap without pushing one or the other out of the way. Float margins do not collapse like other margins in CSS.

In the LifeHacker example, they have a 620px wide column, which is really two 300px wide columns with a 20px middle gutter. They don't want to give 10px margins to the right and left ads, since that would mean 310px columns, and it would also mean that the gutters wouldn't actually line up.

That's why they wanted to make purple.

Valiant Attempts

Many commenters wanted to add the 20px left and right margins and call it a day, maybe clearing out the images and ads in between. Others wanted to do the 10px margins for sloppy gutters. Neither of these address the problem.

Some commenters tried relative positioning. However, in relative positioning, the object is rendered in place and then shoved aside, so the exact same problems occur as without relative positioning.

Similarly, absolute positioning doesn't address the problem since it removes the object from the document flow entirely, allowing text to wrap beneath it.

CSS3 Selectors to the Rescue

Here's my solution. It works by assuming a couple things:

This all works by making the paragraphs in between the ad and the image 300px wide. All paragraphs after the image have a width of auto. The CSS3 trick here is to use ~, the general sibling combinator (http://www.w3.org/TR/css3-selectors/).

.right ~ p {width:300px;}
.left ~ p {width:auto;}

This makes the P's following the ad (.right) 300px wide and the P's following the image (after the add, .left) auto wide. Thus making a pretty gutter down the middle of the page.

The whole thing, of course, breaks down in example four. It also only works in CSS3 compliant browsers. However, if you're willing to progressively enhance, this is a beautiful way to get two columns with neat gutters without using more complex CSS3 layout techniques.

Update

The above example works great when you want to tile images next to images and make purple. However, LifeHacker commeted back. It turns out most of their ads are 600px tall, and they wanted to inject a much shorter image into their left hand column.

Again, they wanted it completely automated. They wanted also the ability to stick a paragraph or two of text right before the image.

This actually makes things easier. Here is the new example.

Also nice is this system requires no image to 'reset' the text paragraphs. Finally, if no ad is present, the image still retains its padding from the start.

The new example uses the adjascent sibling selecter (E + F) in order to strip out the right-hand padding for the first image following an ad, when the image and ad are separated by 0 to 3 paragraphs.

.ad + .image,
.ad + p + .image,
.ad + p + p + .image,
.ad + p + p + p .image {
  position:static;
  margin:0 0 20px;
  clear:none;
}

There are a few rules with this code, for example, if you have three really long paragraphs and then an image, the image will not have any padding. However, in looking over a few dozen LifeHacker posts, they don't do a lot of long paragraphs at the beginning of their articles. So this system should work.