---
title: "jMetronome: Using jQuery to keep typographic rhythm"
date: 2008-08-05
url: https://fortes.com/2008/jmetronome-using-jquery-to-keep-typographic-rhythm/
summary: "Fixing broken vertical rhythm on the web with a little jQuery nudge"
---

A couple of years ago, Richard Rutter wrote [Compose to a Vertical
Rhythm](http://24ways.org/2006/compose-to-a-vertical-rhythm), which described
how web developers could use CSS to maintain proper vertical typographic rhythm
when designing pages. The technique is fairly straightforward, requiring some
basic math to ensure consistent margins and leading across all page elements.

One issue that many people face with this technique is that vertical rhythm can
easily be thrown off by non-text elements, such as images. To illustrate, here's
a [sample
page](http://static.fortes.com/projects/jquery/rhythm-example-pre.html) which
contains text, images, a code block showing a horizontal scrollbar, and even a
video. Notice how the text no longer lines up perfectly within the vertical grid
lines after the first image. This is because the image is 240 pixels tall, which
is not a multiple of 18, the line height used throughout the document.

One solution to this issue is to make sure your images always have a height that
is a multiple of the line height being used by your document. Unfortunately,
this is usually not practical for production sites.

Here's some jQuery code that you can use in order to make sure your document
keeps its rhythm:

```js
$(function() {
  var lineHeight = parseFloat($('body').css('line-height'));

  function balanceHeight(el) {
    var h = $(el).outerHeight();
    var delta = h % lineHeight;
    if (delta) {
      // For images and objects, we want to align the bottom w/ the baseline,
      // so we pad the top of the element. For other elements (text elements
      // that have a scrollbar), we pad the bottom, to keep the text within
      // on the same baseline.
      var paddingDirection = $(el).is('img') || $(el).is('object')
          ? 'padding-top' : 'padding-bottom';

      // Adjust padding, because margin can get collapsed and cause uneven spacing
      var currentPadding = parseFloat($(el).css(paddingDirection));
      $(el).css(paddingDirection, currentPadding + lineHeight - delta);
    }
  }

  // Depending on your content, you may want to modify which elements you want
  // to adjust, by modifying the selector used below. By default, we grab all img,
  // pre, and object elements.
  $('img, pre, object').each(function() {
    // Only works if we’re manipulating block objects
    if ($(this).css('display') == 'inline') {
      $(this).css('display', 'block');
    }

    /* Images need to load before you get their height */
    if ($(this).is('img')) {
      $(this).load(function() {
        balanceHeight(this);
      });
    } else {
      balanceHeight(this);
    }
  });
});
```

The code is fairly straightforward. Briefly, what it does is add padding to the
top or bottom of an element in order to ensure its total height is a multiple of
the document's overall line height.

## Usage

You can use this script as-is, but you may want to change the selector used in
the following line:

```js
$('img, pre, object').each(function() {
```

The reason is that this selector is probably too broad for your page. You most
likely do not want to adjust every single image on your page, just the ones
within your main content block. On my page, I adjust any `pre` elements, because
I've set them to overflow with scrollbars, which can alter the height of the
code block and mess up vertical spacing just like an image.

## Known Issues

I've only found two issues with the code, and they both affect Internet
Explorer:

1. `object` tags don't get adjusted. This might be a jQuery issue, but I haven't
   investigated it yet.
2. If you use [unitless line
   heights](http://meyerweb.com/eric/thoughts/2006/02/08/unitless-line-heights/),
   jQuery doesn't retrieve the correct line height in IE. You can either modify
   the code and hardcode a line height, or just don't use unitless line height
   on the `body` element.

Let me know if you run into any other issues.

