Hans Sprecher

Making Purple: Pretty Gutters in CSS3

Hans Sprecher, @honzie

2.2.2011

Today, while watching a combination of Twitter channels on CSS, I saw that LifeHacker had a bounty out on CSS. The basic challenge was to make purple as one of their commenters said.

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.

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.

Some Valient Attempts

Many commenters wanted to simple 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. We can dismiss both these.

Some commenters tried clever 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 seems to have some icky ways, 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, I heard back from LifeHacker. 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.

Thoughts? Hit me up on Twitter @honzie.