Monday 13 June 2005

Negative Margins

Following an article on SimpleBits about negative margins, here is a quick explanation of how the same CSS feature is used on this site to float photographs and figures in the left margin.

Close up of the maple leaf pattern of the fountain

Fountain dedicated to Canadians soldiers who died during WWII, Green Park, London Posted by Hello

To demonstrate the feature, I decided to use this photograph I have of a fountain you can find in Green Park in London and that is dedicated to Canadian soldiers who died during the second World War. This fountain is a beautiful low key water feature located close to the exit to Buckingham Palace but that most tourists miss nonetheless. It consists of a slanted plane with a maple leaf pattern on which water flows slowly. It is surrounded by real maple trees that mix their real leaves to the stylised ones in autumn.

Specification

I wanted to be able to insert photographs in the text and have them bleed off the left edge of the page, while having the text flow around the right edge, leaving a decent gap to keep the text clear. In addition, I wanted to be able to add a caption below the photograph that would be completely in the margin but would leave a small gap on the left so as not to be flush against the page's border. It was also essential that the caption be close to the photograph so that it was obvious it belonged with it. Finally, the last requirement was to be able to have several pictures one after the other with no accidental clash between them in terms of positioning.

Markup

To make my markup semantically sensible, I decided to have a top level enclosing tag around the image and its associated caption. So it would look something like this:

<div class="figure">
  <img class="body" ... />
  <p class="caption">...</p>
</div>

What this would also allow me to do is define styles for the figure as a whole.

Styles

Considering the body of the figure (the image) and its caption behaved differently towards the surrounding text, they needed to be floated separately. They also needed different margins.

Calculating margins

To offset the two elements to the left, I needed to calculate negative left margins in such a way that the left edge of the photograph would coincide with the left edge of the window, knowing that my point of reference was the left edge of the main column. What makes the calculation a bit tricky is that this is a liquid layout where the columns are defined in terms of percentages of the body. The main column has a width of 60% of the total body width and a left margin of 15%. So, a negative margin of -15% should do, right? No. It needs to be calculated as a percentage of the width of the main column, not the body. This is because the last element that has been given an explicit width in the hierarchy of the site is the div that defines the main column and it is therefore the reference in all relative horizontal calculations. So I needed to know what 15% of the body width represents as a percentage of the main column width. This is quite easily done: if the body is 100 pixels wide, the main column is 60 pixels wide and the left margin is 15 pixels. 15 pixels is a quarter of 60 or 25%. The left margin for the image therefore needs to be -25%. In addition to this, I decided to apply a margin of 1em on the 3 other edges to leave reasonable white space between the image and the surrounding text.

The caption's left margin was easy: I decided to leave a small gap between the left edge of the window and the left edge of the caption so applied a left margin of -24%. Similarly, I applied a 1em margin right and bottom to make sure there was decent white space. However, the caption needed to be as close as possible to the image and the image already had a 1em bottom margin. So, to move the caption closer, I applied a top margin of -1em.

Floating and clearing

Floating sounds straightforward. Just float the whole container left? No because in this case, the box model means that the edges of the floated box are dictated by the wider of the two boxes and you end up with a big white space right of the caption. So you have to float both object separately. However, if you do this, the caption ends up colliding with the image so the caption has to also clear left in order to be forced below the image. A final touch is to make the image clear left as well so that you can have several figures in succession without the image of the second figure colliding with the caption of the first one.

So here is the full code:

.figure {
    font-family:Arial,sans-serif;
    font-size:70%;
    text-align:left;
}
.figure img {
    border:none;
}
.figure .body {
    clear:left;
    float:left;
    margin:1em 1em 1em -25%;
}
.figure .caption {
    clear:left;
    float:left;
    width:20%;
    margin:-1em 1em 1em -24%;
    font-weight:bold;
}

You will note that I only used general class selectors, I didn't force the use of div, img or p. This is to give the style more flexibility. For instance, I could then apply the figure and caption classes to span tags rather than div and p thus enabling me to insert a figure in the middle of a paragraph rather than between paragraphs. Similarly, it means the body of the figure can be anything, such as a table or any other complex container.

No comments: