02 March 2013

Bootstrap 2.3 rem/em


Previously, I wrote about making changes to make Twitter Bootstrap 2.2.2 use rem/em text sizing. It wasn't perfect, but it worked pretty well. Since then, I've done similar analysis and changes to Bootstrap 2.3 (and 2.3.1 since 2.3.1 is JavaScript-only changes).  I've also made some improvements and better addressed some Firefox-specific issues noted in the earlier post.

Changes needed to make Bootstrap 2.3 support rem/em are documented in a spreadsheet as before. Good practice recommends working on a copy of the original Bootstrap LESS so you have the original as a reference (or fallback). A few notes about some differences from the earlier version.

More Comprehensive


This version of the spreadsheet is more comprehensive, listing everywhere that font-size or line-height are set and (I think) everywhere that @baseFontSize and @baseLineHeight are used. Columns A-E identify which file and line are being changed, what element/class/etc. it applies to, the specific property being set, and the original value in Bootstrap 2.3's LESS code. Note that the same line may appear multiple times if it sets values for multiple elements/classes/etc. For example, rows 11-13 all represent the same line.

Column F identifies the value to use with my own typographic scale (based on the golden ratio). Column G identifies changes to column F to get something closer to the original Bootstrap font sizing and line spacing. Note that I haven't tested the Bootstrap scale, but am basing it on the work for Bootstrap 2.2.2. If you have issues, let me know, but it's probably just a matter of choosing a different value on the Bootstrap scale.

Better Handling of Firefox Append/Prepend Issues


Rows 329-349 of the spreadsheet (highlighted yellow) identify a set of changes to forms that add lines to address component sizing for prepend/append components. I have tested on IE9 and the latest versions of Chrome and Firefox (as of this writing) and these changes seem to address the sizing issues in csstests.html (in the tests folder).

Row 349 is a large chunk of CSS specifically to address text positioning for certain prepend/append inputs Firefox and uses @-moz-document url_prefix() to target only Firefox. Try it with and without (in Firefox, of course) to see the difference and you'll understand. This is FF-targeted because it causes issues in Chrome and IE. The padding set seems to work with different base rem multipliers and gets the text on append/prepend submit inputs (among others) to be properly centered. If it doesn't for your specific settings, I recommend fiddling with the values in Firebug. That's how I figured out what worked.

 Mixin for REM with PX Fallbacks


As before, I'm using a mixin called .font-size() to set font sizes. The mixin takes a REM size and generates px fallbacks for certain lame browsers. I use the LESS unit() function to force units to px and rem, regardless of what you pass, but assumes you've passed rem (multiply whatever you pass by 16 to get px). This approach seems much better than what I found in all the px-fallback example LESS on the web and it seems to work. The code for this mixin appears in row 350 of the spreadsheet and below.

// REM/EM FONT-SIZE WITH PX FALLBACK
// assumes 1rem = 16px for fallback
// -------------------------
.font-size(@size){
  font-size:unit(round(@size * 16, 3), px);
  font-size:unit(@size, rem);
}


Other Notes


I'm not 100% sure about the .navbar .brand padding (spreadsheet row 183). It may be slightly off. It's possible padding: 0.45em @baseLineHeight would work better that 0.5em, which is what you'll get when @navbarHeightMultiplier is 2 (value I'm using). Your mileage may vary. Do what you think works best, and let me know what you think is appropriate here. I'm open to suggestions.

I plan to do something similar for Bootstrap 3 and am currently looking at the WIP branch to get a sense of what will be involved. (Maybe we'll be lucky and mdo and fat will decide to make it rem/em by default.)

If there's enough interest, I may set up a pull of BS2.3.1 to incorporate these changes. My git-foo is rather weak, so if you're a github guru and would like to help me figure out a few questions I have about proper management of branches, let me know. Likewise, if BS3 doesn't include rem/em, given enough interest, I may do a pull to apply these changes.

UPDATE: (2 hours later) I noticed I missed .navbar-search .search-form, the one place #font is called with a fixed (px) font-size. I have updated the spreadsheet accordingly. (This will be obviously wrong because the search form in the navbar will use a 13rem font. None of the default Bootstrap tests includes a navbar search form.)

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.)