CSS: How to size text using ems

By donmc, 7 April, 2008

posted 18th May 2004

  • Text for the screen is sized with CSS in terms of pixels, ems or keywords. As most of us know, sizing with pixels is easy: get your selector and give it a font-size – no more thought required. Sizing with keywords is more complicated and requires a few workarounds, but you’re in luck as the techniques are well documented. That leaves ems. At this point people often leg it. ‘Ems are too inconsistent,’ they say, ‘they’re too hard; they never work.’ Well that may be the received wisdom, but if ever the was a case of FUD then this is it. I will now attempt to show you how ems can be as quick and easy to use as pixels.

    Why ems?

    If the world were an ideal place, we’d all use pixels. But it’s not, we have the broken browser to contend with. IE/Win will not allow readers to resize text that has been sized in pixels. Like it or not, your readers will want to resize text at some point. Perhaps they are short-sighted, doing a presentation, using a ridiculously high resolution laptop or simply have tired eyes. So unless you know (not think) your audience won’t be using IE/Win or will never wish to resize their text then pixels are not yet a viable solution.

    Keyword-based text sizing will allow all browsers to resize text so this is a possibility, but I don’t find it gives me the precision that pixels would give me. Using ems however, allows all browsers to resize text and also provides pixel-level precision and so they tend to be my unit of choice.

    Get on with it

    OK let’s dive into ems. I’ll show you, from scratch, how to size text in a document using ems. I’ll assume throughout that we are dealing with a browser set to ‘medium’ text. The default size for ‘medium’ text in all modern browsers is 16px. Our first step is to reduce this size for the entire document by setting body size to 62.5%:

    BODY {font-size:62.5%}

    This takes 16px down to 10px which I’m using purely because it’s a nice round number for example purposes – 10px text is too small for the real world. From now on it’s easy to think in pixels but still set sizes in terms of ems: 1em is 10px, 0.8em is 8px, 1.6em is 16px, etc. If you are laying out your document using CSS (which you are, right?) then you have probably used a few divs to group together elements. Apply text-size to these divs and your job is almost done. Consider a two column layout with header and footer:

    <body>
    <div id="navigation"> ... </div>
    <div id="main_content"> ... </div>
    <div id="side_bar"> ... </div>
    
    <div id="footer"> ... </div>
    </body>
    
    #navigation {font-size:1em}
    #main_content {font-size:1.2em}
    #side_bar {font-size:1em}
    #footer {font-size:0.9em}

    So this would give us a document where text in the navigation and side bar is displayed at 10px, the main content is 12px and the footer is 9px. There now remains a few anomalies to sort out (you’d have to do this even if you were sizing in pixels). In Mozilla-based browsers, all heading elements in our aforementioned #main_content div will be displayed at 12px whether they are an H1 or an H6, whereas other browsers show the headings at different sizes as expected. Applying text-sizes to all headings will give consistency across browsers, for example:

    H1 {font-size:2em}  /* displayed at 24px */
    H2 {font-size:1.5em}  /* displayed at 18px */
    H3 {font-size:1.25em}  /* displayed at 15px */
    H4 {font-size:1em}  /* displayed at 12px */

    A similar job needs to be done on forms and tables to force form controls and table cells to inherit the correct size (mainly to cater for IE/Win):

    INPUT, SELECT, TH, TD {font-size:1em}

    And so to the final tweak and the bit folks seem to find most tricky: dealing with nested elements. We’ve already touched upon it with our headers, but for now let’s look more closely at what’s going on. First of all we changed our body text to 10px; 62.5% of its default size:

    16 x 0.625 = 10

    Then we said our main content should be displayed at 12px. If we did nothing, the #main_content div would be displayed at 10px because it would inherit its size from the body element – its parent. This implies that we always size text relative to the parent element when using ems:

    child pixels / parent pixels = child ems
    12 / 10 = 1.2

    Next we wanted our h1 heading to be 24px. The parent to our h1 is the main_content div which we know to be 12px in size. To get our headings to be 24px we need to double that so our ems are:

    24 / 12 = 2

    And so it goes on. Tricky stuff occurs where rules like this are applied:

    #main_content LI {font-size:0.8333em}

    This rule implies that all main content list items should be displayed at 10px. We use the same straight forward maths to achieve this:

    10 / 12 = 0.8333

    But what happens when one list contains another? It gets smaller. Why? Because our rule actually says that any list item in the #main_content div should 0.8333 times the size of its parent. So we need another rule to prevent this ‘inherited shrinkage’:

    LI LI {font-size:1em}

    This says that any list item inside another list item should be the same size as its parent (the other list item). I normally use a whole set of child selectors to prevent confusion during development:

    LI LI, LI P, TD P, BLOCKQUOTE P {font-size:1em}

    And that’s it. When sizing text in ems there’s really one rule to remember: size text relative to its parent and use this simple calculation to do so:

    child pixels / parent pixels = child ems

    Some helpful tools

    Pixy’s list computed styles is fabulous bookmarklet which shows the cascade of calculated font sizes (or any other CSS property). Mozilla’s DOM Inspector is even more powerful as it allows you to see which CSS rules are affecting any given element in order of cascade priority so you can see why your text is or isn’t changing size when you expected it to.

    If you’re finding the maths all a bit complex, try using Riddle’s handy em calculator.

    And finally… what is an em?

    Classically, an em (pronounced emm) is a typographer’s unit of horizontal spacing and is a sliding (relative) measure. One em is a distance equal to the text size. In 10 pixel type, an em is 10 pixels; in 18 pixel type it is 18 pixels. Thus 1em of padding is proportionately the same in any text size.

    Update:

    Make sure you read Patrick H Lauke’s comment on perfecting this method for IE5/Win.

Advertisements

82 comments

  • Pete F. wrote:

    Pete F.’s Gravatar

    I like to start the page with a default font size definition in points, then use “em’s” of that point size for specific items. Works well across all browsers, including IE/Win, IE/Mac, Firefox, Opera and Safari.

    Permalink for this comment 18 May 2004, 14:12 GMT

  • Marko Samastur wrote:

    Marko Samastur’s Gravatar

    Just a small comment from a math nut. 75% of 16 is 12, not 10.

    So, if you want to go from 16px to 10px, you need to set font-size to 62.5% (unless browsers have their own math and 75% actually works).

    Permalink for this comment 18 May 2004, 14:40 GMT

  • Rich wrote:

    Rich’s Gravatar

    Marko – thanks for that. Schoolboy error duly corrected.

    Permalink for this comment 18 May 2004, 15:00 GMT

  • James Craig wrote:

    James Craig’s Gravatar

    Fantastic article, and a perfect description of how to use ems. I disagree with one small part of the method though: the arbitrary body size. I’d recommend leaving the body size at 100% and scaling the rest down in ems accordingly. Of course, the downside is that the default is no longer the easily-divided 10 pixels. The upside is that it works well with user style sheets.

    Imagine I was a more-technical-than-average user (more people will figure out user CSS eventually) that had a preference for larger fonts. Perhaps I had vision impairment, perhaps I had some standard hyperopia gaining with age, or perhaps I just liked reading large text better. Not too large, mind you. Just about 120% of normal. Seems easy enough in my user CSS, right?

    body {font-size:120% !important;}

    That is, as long as everyone uses the standard default size for the body element. Trying this one your example yields massive text. I’ve also seen other arbitrary examples like 76 percent or 80-something percent. The more variations there are, the worse it becomes.

    So that’s my two cents. Do you agree? Again, lovely article… (We just differ on one small detail.)

    Permalink for this comment 18 May 2004, 15:54 GMT

  • Gordon wrote:

    Gordon’s Gravatar

    Wonderful article, and you are right FUD was stopping. No more!

    Thanks for taking the time to capture this info, it’s people like you that help people like me look good (well.. better at least..)

    Permalink for this comment 18 May 2004, 18:30 GMT

  • Mike P. wrote:

    James, I believe that 76% came from Owen Briggs work:
    http://www.thenoodleincident.com/tutorials/box_lesson/font/

    Permalink for this comment 18 May 2004, 20:12 GMT

  • Shawn Medero wrote:

    Shawn Medero’s Gravatar

    My own testing tells me 76% is as small as you can go before things text becomes unreadable in some browsers.

    Permalink for this comment 18 May 2004, 21:05 GMT

  • ie wrote:

    An excellent article. I think I finally understand how to make ems work.

    Nonetheless, the em approach seems to me to be fairly unwieldy. I differ with you that “sizing with keywords is more complicated.” The ALA article you’re alluding to was the first of its kind. Things have moved on since.

    The technique has stood me in good stead many a time.

    body, div, table, h1, ... h6 {

    font-size: x-small; fo
    t-size: small; /* ALA uses Celik’s hack, I prefer Tan’s hack */
    }

    This gives you a “base” font size 13px in all major browsers if and when set to “medium” text size. Now you can alter text size using either ems or percentages without a usual ripple effect or tedious calculations.

    When you size text, let’s say, from smallest to largest in IE, it’s always legible. See www.stopdesign.com

    With ems you may get horrific text size. See www.micr.cz

    Permalink for this comment 18 May 2004, 22:17 GMT

  • patrick h. lauke wrote:

    patrick h. lauke’s Gravatar

    two issues that are worth mentioning as well:

    IE gets its text resizing horribly wrong when just using ems. if your text size is set to normal, it’s fine, and ems relate directly to what the equivalent percentage would be…so having 0.75em on the body would have the same effect as 75%. now, bump the text size up or down, and you’ll notice that IE scales ems far more drastically than percentages, resulting in too big / too small text. the deceptively simple solution: in addition to having something like

    body { font-size: 0.75em; }

    you need to add an extra rule (i usually do it on the html) in percentages, which helps IE get its bearing back in terms of resizing…so

    html { font-size: 100%; /* IE hack */ }
    body { font-size: 0.75em; }

    now resizing works consistently.

    – although, as per the cascade, setting the font-size on the body should really influence all text sizing in the document, IE seems to ignore it when it encounters a table. the naive approach would be to set the font size for both body and tables like so

    body, table { font-size: 0.75em; }

    this works in IE, but any other same browser will honour the cascade, resulting in table text actually being sized to 0.75 of 0.75 = 0.5625em. the solution, similar to the previous one, is to add a percentage size to the table

    body { font-size: 0.75em; }
    table { font-size: 100%; /* IE hack */ }

    i seem to remember that this same issue happens with selects and inputs as well, and can be solved in exactly the same way, by adding a 100% sizing to them in the CSS.

    i like to call these the “be patronisingly over-specific, so IE knows what you mean” rules…and the beauty is that these don’t break in other browsers, and – apart from being “obvious” – don’t really fall under the category of hacks per se, as they don’t rely on bugs in any browser’s css parsing like, say, the box model hack does…