Marching to a Vertical Cadence

Regular readers will notice that this blog got a bit of a face-lift today. The obvious change is the addition of a background texture, but actually the biggest change is that I updated all of the elements to fit into a vertical em grid. Actually, I’m not all that thrilled with the background image, but it was killing me having such a bland vanilla design, so I whipped up something as fast as possible. (For comparison, here’s the old version.)

If you turn on the lines, you can see how each line of text, every heading and block, is now lined up so that it will “resonate” with a consistent vertical spacing. I’m certainly not the first one to do something like this. However, in the process of figuring out how this could be done, I came up with a few shortcuts to make the math easier. You’ll probably need to have a calculator handy, and building a decent grid tile graphic is key.

First step: Build the grid image

To make the process somewhat sane, come up with a font-size that’s pretty reasonable, and build a background baseline graphic to hold you to that size. Since I favor readability over teensy designery text, I chose 16px, and built this image. It’s 32px tall, with a horizontal line every 8px. (The color choices are purely for nostalgic reasons. I can practically smell the #2 pencil in my hand.)

Going forward, every block element must begin just below a line, and end exactly on a line. If it’s hanging over, there’s a problem. (Adding and removing background colors is a good technique to check things.)

Set the font size to 16px in the HTML element, and add the background image:

html {
  background:#fafafa url(line.png) top repeat;
  font-size:16px;
}

Once everything works, you can pull out the line and the fixed px font-size, and let it resize to whatever the user has set in their browser. Since everything will be based on ems, it will all scale up and down to any size. If you sniff the CSS on this page, you’ll see that I apply this rule whenever the “show-lines” class is on the HTML element.

The Rules

  1. Never set font-size without setting line-height (or vice versa.)
  2. For simplicity, vertical padding and margin should be avoided when they’re not needed, or at the very least not set on an element that has an altered font-size, and must be specified in ems.
  3. Top and bottom borders get hairy. Use outline instead, and let it be a little broken for IE.
  4. The horizontal direction is fair game, outside the scope of this article. Use px or ex or whatever units you want.
  5. The big one: font-size * (line-height + vertical padding + vertical margin) must be a whole number. If you’re going to use a double-spacing cadence, then it should ideally also be an even number most of the time.
  6. Go to 5 decimal places when you get numbers that repeat. Browsers only look at the first 4, but most of them will round rather than truncate, so the additional number is worth having.

So, for example, on the body text, I wanted to have simple double-spacing. So, this rule was fine:

p {
  font-size: 1em;
  line-height:2;
}

On some elements, I wanted to drop down the font size a bit, say to 70% of normal body text (or 0.7em.) If the element had a line-height of 1, and its font-size is 0.7em, then that’s a problem, because 0.7 * ( 1 ) = 0.7, which is not a whole number. If we want to make this element take up 1 row on the baseline grid, then we can say that 0.7 * ( x ) = 1, and solve for x, the line-height. In this case, 1/0.7 = 1.428571, so we set the line-height to that value, and end up with small text that takes up 1 line on the grid.

The same technique can be done if you wanted to go even smaller. The table below has values for various font sizes and double- and single-spacing:

Font-size (in ems) Line-height (single) Line-height (double)
0.5 2 4
0.6 1.666667 3.333333
0.7 1.428571 2.857142
0.8 1.25 2.5
0.9 1.111111 2.222222
1 1 2
1.1 0.909090 1.818181
1.2 0.833333 1.666667
1.3 0.769231 1.538461
1.4 0.714285 1.428571
1.5 0.666667 1.333333
1.6 0.625 1.25
1.7 0.588235 1.176471
1.8 0.555555 1.111111
1.9 0.526315 1.0526315
2 0.5 1

As you might expect, some of these work better than others. The funkier looking a decimal is, the greater the chance that it will be improperly rounded.

Effects on Layout

As you can see in the table above, without the ability to put pixel borders on the tops and bottoms of things, it presents interesting challenges in some cases. A gradient image can be a little nicer than borders for table cells, and is certainly a lot nicer than not having any kind of vertical breaks.

Form elements are particularly tricky. I didn’t want to have them borderless, since borderless forms are just visually weird to me. However, a border on the top and bottom messes with the spacing. So, I’ve used outline, and just passed a border rule to IE 6 via the underscore hack. It’s not a great solution, but it works for now, since outline is drawn outside the element, instead of pushing it further down the page. When I come up with a more clever way to do this, I’ll share it.

Input elements seemed to need just a shade less padding than the math would indicate. They seemed to work properly using 0.41em instead of the 0.421758em that my formula would have predicted. Not sure why that is, but I’m OK with the hack.

Margins and Padding

If you ever have to move something up or down, I find it’s useful to figure out how many ems it needs to move, and then convert by the font size. To keep things easy, you can restrict any font-size changes to inner elements, and apply padding and margin on the container (which always has a font-size of 1em.) However, some adjustments are just easier to make on an element that has a font size, so the conversion comes in handy.

For example, let’s say that we have an element with a font size of 0.6em, and we want to move it down 1.5ems. (In the baseline graphic, that would be moving it down 1.5 “red lines”.) So, the ratio looks like this:

1.5 * 1 = x * 0.6
1.5 / 0.6 = x = 2.5

so we have to give the element a padding or margin of 2.5em. The simplified formula is:

distance / font-size = correct em measurement

I find that, as I have this style live and notice little errors that need correcting, I’m using that formula quite a bit.

Update

Some browsers do this better than others, of course. Gecko browsers (Firefox, Camino, etc.) are impeccable with their em-spacing and line-height calculations. Safari seems to always be a bit off of the lines in the background image, as does MSIE. Opera is better than those two, but not quite as good as the Geckos. Even when it’s not perfect, though, getting close does seem to feel more “right” to me.