Link to wealthfront.com

Fork me on GitHub

Monday, November 22, 2010

Arrhythmia.js, a new take on rhythm validation

...just as regular use of time provides rhythm in music, so regular use of space provides rhythm in typography, and without rhythm the listener, or the reader, becomes disorientated and lost.
—Richard Rutter, Compose to a Vertical Rhythm
Arrhythmia.js is a tool for validating the vertical rhythm of your page. Its goal is to do away with the practice of visually checking your typography against a baseline grid by programmatically validating that the elements of your page fall within your vertical rhythm.

Designers working with vertical rhythm typically set a CSS background image that shows the vertical grid. This makes it easy to visually check that no part of a page is mis-aligned. Let's look at a page of 12px text with an 18px line-height and margin on just such a grid:



We can easily see that our paragraph elements and text they contain fall upon our 18px grid, staying true to the rhythm we've defined.

Now let's look at the same page but with paragraph margins of 11px:


In this case our margin is incorrect, it is not a multiple of our line-height so it shifts subsequent paragraph elements out of alignment. Set against the grid lines, it's obvious our text is no longer rhythmic.

But your eyes don't scale.


This process of visually checking a design's vertical rhythm is effective but limited. It may work in the initial design phase but what about the future? Things like layout changes, dynamic content and CSS refactoring could all upset your layout down the road. As a developer you can't be expected to switch on the line-height grid for a quick check every time you make a change.

The real problem is your eyes don't scale. You automate the testing of your code with JUnit, doctest or rspec — so why not your design?

Just because vertical rhythm is a means to a visual end doesn't mean it can't be programmatically verified. "Staying in rhythm" really means that your elements fall on predefined boundaries: the lines of your grid, which are positioned along the y-axis of your page at multiples of your line-height. What you're verifying with your eye isn't visual, it's mathematical.

Let's look at our baseline grid again, this time annotated with the y-position of each line:


What we're asserting visually when we check our grid is simply that the y-positions of all our block elements are multiples of 18px or, in other words, that "myElement.yPosition % lineHeight == 0." If the top and bottom y-position of every element on our page is a multiple of our line-height we know our design is in rhythm.


So what's Arrhythmia?

Arrhythmia iterates through the children of a supplied element ("body" by default) and verifies that their start and end y-positions are multiples of your line-height. If it finds an element out of rhythm it will examine all of that element's mis-aligned descendants in a depth-first search until it arrives at the deepest mis-aligned node, this is the "problem element" that has skewed the rest of the elements on the page.

Of course, it's not enough for Arrhythmia to tell you that something's out of rhythm on our page, we want to know why.


If you're familiar with the CSS Box Model you know that the full height of an element is really composed of the element's content, padding, border and margin. The sum of these is the final height the element will take on the rendered page in your browser. In jQuery this is the method outerHeight() which, when supplied with true as an argument, gives you the height of an element plus its padding, border and margin.

jQuery actually has several methods to measure element height, we can use all of them to determine which part of the box model is most likely causing the misalignment.


outerHeight(true) is the height of the entire rendered element, including margin.





outerHeight() is the height of the element without margin.





innerHeight() gives us the height of the element content plus its padding.





Finally height() will give us the height of just the element content.






Arrhythmia's approach is a simple one. It looks at each height measurement starting with the outermost (outerHeight(true)) and working inwards. At each stage it checks if the height measurement (element+padding, element+padding+border, etc) is a multiple of our line-height. If it isn't, that particular box model component (padding, border, margin, etc) receives a "mark." Next it checks the height of just the component (i.e. the margin height). If the component's height is not a multiple of the line-height, this too receives a mark.

At the end Arrhythmia look for the highest scoring component, favoring the outermost "errors" first in a tie, and reports back.

Sometimes margin or padding might not be a multiple of the line-height intentionally — they can act as adjustment values so that an element with content not a multiple of the line-height (i.e. an image) "rounds out" to a multiple after padding or margin is applied. This is where the simple scoring system comes into play. A "corrective" padding value, for example, would only get one mark. The padding itself would not be a multiple of the line-height but the element+padding height measurement would.

The Report

When Arrhythmia is done it reports back via the console with an explanation of what it thinks went wrong based on the diagnostic process. It also highlights the problem element purple on your page and provides a "DOM trace" of all the parent elements it drilled through to find the arrhythmic element.

In the future it would be great to add functionality to allow Arrhythmia to validate silently and issue a POST request with diagnostic info. This would allow you to maintain a log of browser/OS combinations on which your design breaks. It could serve both as a reporting mechanism and as a distributed testing framework, every user that visits your site will be effectively testing your layout against their particular configuration.

Get It

You can grab Arrhythmia on GitHub and check it out for yourself. Feel free to contribute via pull-request, just include tests for your features or fixes. At the moment Arrhythmia uses a small home-made testing framework that loads up full HTML documents as test fixtures. In the future I hope some or all of the tests can move to something like JS Test Driver.

Usage

Arrhythmia requires jQuery and takes a standard jQuery selector expression. It will validate the rhythm of all child elements within the selector you specify. At its most basic usage, Arrhythmia can take the "body" element and validate that every block element on the page is in rhythm.

Arrhythmia("body").validateRhythm();

Note that you can supply validateRhythm() with your line-height, or let it guess by injecting a
tag into the DOM and measuring the element's height. You can also use this to assert that your line-height on the page matches what you expected.

Arrhythmia("body").assertLineHeight(18);

A few caveats

  • Arrhythmia is little more than a proof-of-concept at the moment, you can check out its handful of unit tests in src-test but there are *a lot* of edge cases out there in CSS land.
  • Arrhythmia only operates on block elements, since only block elements have height.
  • Arrhythmia expects elements it examines to be position: relative.
  • IE's Console support is kind of lame and doesn't support the "%o" format token, but validateRhythm() will still return a true or false value and give you diagnostic info.

Finding rhythm

If you're interested in establishing vertical rhythm in your design I suggest you check out Richard Rutter's article. Steve Losh also talks about establishing rhythm in this post. You might also consider pulling the reset.css and typography.css files from the Blueprint CSS Framework as a starting point in establishing your vertical grid.



Before anyone mentions it: yes, I know we're not rocking vertical rhythm on the Wealthfront blog. We *are* looking for a talented designer to redesign it however, so get in touch!