25 February 2013

Twitter Bootstrap, Rems, Ems, and px Fallback. Oh my!

Discontent is the Mother of Improvement

For some time now, I've been working on a series of novels--YA-ish, Japanese light novelish, stuff that doesn't have a real "market" as far as publishers are concerned. Recently, I started working on a web site to publish these novels and offer ebook versions. It's a work in progress at this point. I'm building it on WordPress using a theme called The Bootstrap, which is based on Twitter Bootstrap.

After a lot of reading about responsive web sites, typography, and the like, I decided I wanted my site to use font-size in rems with line-height and perhaps a few other critical values in ems. Bootstrap 2.2.2 isn't designed that way. It uses fixed px-based sizes. I experimented a bit and found the base Bootstrap adjusts pretty well given a baseFontSize in rems and a baseLineHeight in ems, but there are some places where that just doesn't work (places where values are hard coded or expect a px value based on the math). I also wanted to implement a typographic scale. Again, Bootstrap 2.2.2 doesn't support that well out of the box.

So I set out to do that. This post explains the process for anyone else who wants to implement it.

Defining the Scope

I started by finding places where font-size and line-height are set in Bootstrap 2.2.2 code. Then I eliminated cases where the value is okay as is (font-size:0, for example). As I worked through the changes, I found a handful of other values that needed to be adjusted. The result is the LESS Analysis sheet in this workbook (Google Docs spreadsheet), which represents my change log to get Bootstrap 2.2.2 to use rems and ems.

The LESS Analysis sheet lists the Bootstrap file, line number, and element being whose property is being set in columns A-C. The property is identified in column D. If more than one element is being set in a single statement, the same line number will repeat (example, forms line 85). Column E identifies the original Bootstrap LESS value, and column F the original Bootstrap CSS value that resulted from that LESS value. Column G contains notes.

Column H identifies changes to the Bootstrap LESS files that will produce values that closely approximates the original Bootstrap values, only in rem instead of px. Column I identifies changes to column H for my typographic scale, which is based on the square root of the golden ratio. This gives a sense of how much (little) work is involved in changing scales once the core Bootstrap changes are in place.

Once I was convinced this was mostly working, I packaged it up and sent it to Mark (mdo) as possibly helpful for Bootstrap 3, for which they're considering going to rem with px fallbacks.

The Dreaded px Fallback

Sadly, older versions of several browsers (notably Internet Explorer before IE9) don't support rem-based font-sizes. While they render the page, from a graphic design and usability perspective, the result is a nightmare. So we need font-size to be set in rem with px fallbacks (meaning, we set the px size first, then the rem size. If the browser recognizes rems, we get rems, which will be based on the user's preferred font size set in the web browser. For browsers that don't recognize rems, we get the px value, where 1 rem is 16px by default. Because our line-height values are set in ems, they work either way.

So I did a little more analysis. The result is the pxFallback sheet in the same workbook linked above. Same basic idea as LESS Analysis, but tighter. While this looks like a lot, it's actually less than 20 additional lines of code to change. Note that the list here is in addition to changes in the LESS Analysis sheet and that, to implement px fallbacks, you need to call the .font-size mixin with the value identitied in the LESS analysis sheet, not just set font-size.

LESS Mixin for rem with px Fallback

I Googled up some LESS mixins to generate px fallbacks, but they all seemed inordinately complex and were based on the 62.5% font-size premise, which is designed to make 1 rem = 10px so math is easier for humans. But since Bootstrap uses LESS to generate all that stuff, and good responsive design encourages 100% font-size in the html, let's remove that dependency. The mixin, appears in row 52 of the pxFallback sheet.


// font-size rem with px fallback, @size should be rem size
// -------------------------
.font-size(@size){
font-size:unit(round(@size * 16, 3), px);
font-size:unit(@size, rem);
}


It's possible this mixin is horribly flawed. It's nowhere near as complex as what I found when I searched, but is inspired by what I saw. It's possible it's horribly flawed, but I doubt it. I've looked at the CSS it generates and the CSS looks like what I expected. I've also dropped the resulting CSS into my WordPress testbed and it works as expected. I haven't tested it on IE7 yet, but if the px fallback concept works as advertised, IE pre-9 should be happy.

The mixin takes a size which is assumed to be the rem size. This is based on the fact that my other changes to Bootstrap necessarily start from a rem font-size. The unit() function forces the units of the value to the specified unit type. So whether the value you pass the mixin has units or not, it will always get px and rem in the order shown and will always assume the parameter size is intended to be rems.

I've assumed that 3 decimal places is adequate for px values. I know HTML pixels aren't necessarily the same as monitor pixels, but I suspect that they aren't so difference that values less than 1/1000 of a pixel make a significant difference in appearance. I haven't rounded the rem units.

I've assumed that, if rem is not available, 1 rem = 16 px. This is a standard conversion, but may not match the user's default browser font settings. Unfortunately, I don't think there's a way to interrogate the browser to get that value in CSS--and even if there was, it wouldn't help the LESS code because at the time LESS compiles, there is no browser to interrogate. Statistics suggest IE pre-9 represents no more than 25% of the browser user base. If you're really worried about appearance perfection for that group using their custom font sizing, put up a warning on your site explaining the situation and recommend they get Chrome or Firefox, both of which support rem sizing.

Typographic Scale

The changes documented in the spreadsheets are based on the assumption that you want to implement a typographic scale. You should. Just accept that. The Bootstrap-emulating scale is a bit weird because the default Bootstrap scale isn't truly modular. You can use it as-is, or you can drop out some of the unused steps to get a more compact scale and make adjustments to the LESS changes accordingly. Or you can use my scale based on the square root of the golden ratio.

If you don't like either of those options, build your own scale. The Typographic Scale sheet in the workbook shows the two scales I tested and can serve as a guide for making your own. Make your own copy or download it to your spreadsheet tool and you can adjust the values in columns F and I of row 2 and generate your own scale based on the square root of that value, or change the formula in H2 if you don't want the square root of the value in F2.

Conclusion

I'm hoping the Bootstrap team decides to incorporate rem/em based sizing in Bootstrap 3. If not, I'll repeat this analysis for BS3 and post the results here. Meanwhile, if you're willing to compile your own, you can make Bootstrap 2.2.2 use rem/em sizing with px fallback by implementing the changes outlined. (I'm using WinLess on Windows and the latest LESS.js package.)