jMetronome: Using jQuery to keep typographic rhythm
A couple of years ago, Richard Rutter wrote 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 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:
$(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:
$('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:
object
tags don’t get adjusted. This might be a jQuery issue, but I haven’t investigated it yet.- If you use unitless line
heights,
jQuery doesn’t retrieve the correct line height in IE. You can either modify
the code and hard code 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.