How to specify dynamic font sizes consistently with CSS

This artice is also available in a French translation.

When developing web sites, we naturally want to offer our end users the option to freely resize the text size in their web browser of choice to their liking. What I would like to do here is present what I’ve found to be most consistent way to achieve this with CSS.

The problem

The problem, as almost always, comes down to web browsers. In this situation, it is Internet Explorer that doesn’t allow the user to resize any font set with the px (pixel) unit, but instead only those with other units, such as percentage, em etc. This problem still applies to IE 7 as well, although that version at least offers page zooming (which is altogether different from text resizing, though).

This is a problem, since most designs and wanted end results are based in pixels, and there are different ways to accomplish this end goal in a coherent manner with other units.

The solution I use

First, it’s about choosing a unit for font sizes. I’ve decided to go with the em unit here, since it seems to be the most stable and accordingly implemented one. One em used for font size equals the font size of the parent element.

So, let’s start from the top:

The default text size in most (I’d like to say all, but who knows…) modern web browsers is 16 pixels, and that will be the base for our code. To make sure all our font sizes derive from that, we start by setting the font size to 100% on the html element:


html{
    font-size: 100%;    
}

The next step is to decide the default text size for the web site, i.e. the font size that will be the most commonly used one for most of the text, and also a good ground to stand on for calculating the text size for all other elements. Let’s say we want a web page where the general font size is 10 pixels. We then take the desired font size, 10, and divide it with the default web browser size, 16: 10 / 16 = 0.625. This is the value that we use for the em unit on the body element:


body{
    font: 0.625em/1em Verdana, Geneva, Arial, Helvetica, sans-serif;
}

The result of this is that the font size will be 10 pixels for all text in the web page, unless you explicitly specify something else. If, for example, you then (which seems very likely) want to have a different font size for different heading types, the calculation is then based on the value you have set for the body element.

This means that 1em set as font size for any child element of the body element will equal 10 pixels. Let’s say that you want your h1 element to have a font-size of 17 pixels, you need to calculate the em value like this: 17 / 10 = 1.7. The resulting code for h1 elements would then look like this:


h1{
    font: 1.7em/1em Arial, Helvetica, sans-serif;
}

From that on, it’s fairly simple. Just make sure to start your calculations with the default font size set (in this case, 10 pixels), and also, use a calculator to assure that your values are rounded correctly (otherwise, different web browsers will do different rounding for you, resulting in inconsistency).

Note: form elements

To reach a more consistent result with form elements as well, I recommend that you specify the font sizes form them as 100%:


input, select, textarea{
    font-size: 100%;
}    

End user experience

With the code displayed above, the font size in web browsers will be 10 pixels, when the web browser font size setting is set to normal (i.e., the web browser’s default font size). Then, if the user resizes the text in any direction, it will automatically adapt itself in well-balanced steps and enhance the end user experience.

An example font style sheet

In the projects I work on, I usually have all the font size settings in a separate CSS file, to easily get an overview of it, and also to separate font settings from CSS used solely for layout purposes. Here’s what an example font CSS might look like for me:


/* Tag-specific */
html{
    font-size: 100%;    
}

body{
    /* The default font size will be 10 pixels with the default web browser font size setting*/
    font: 0.625em/1em Verdana, Geneva, Arial, Helvetica, sans-serif;
}

input, select, textarea{
    font-size: 100%;
}

h1{
    /* This font will be 20 pixels with the default web browser font size setting */
    font: 2em/1em Verdana, Geneva, Arial, Helvetica, sans-serif;
}

h2{
    /* This font will be 15 pixels with the default web browser font size setting */
    font: 1.5em/1em Arial, Helvetica, sans-serif;
}

h3{
    /* This font will be 12 pixels with the default web browser font size setting */
    font: bold 1.2em/1em Verdana, Geneva, Arial, Helvetica, sans-serif;
}    

For discussion

Since all web browsers but IE offer resizing of fonts set in pixels directly, and IE 7 totally relies on page zoom and has hidden away text resizing deep down, is it about time to start specifying font sizes with pixels instead? Or is a more dynamic font size value still they way to go, even though it might require some calculations and less readable/understandable code.

 

Related reading

Posted in CSS,Developing,Technology |

39 Comments

  • Thanks for this, Robert! I don't have the time right now to read the article thoroughly, but this is definitely a bookmark.

    I've tried out some similar methods in the past, but this looks a little more robust.

    In answer to your question; I think it's a good thing to keep on specifying font sizes in a more dynamic way. I mean, with techniques like this one, why not? :)

  • My method is more or less the same. The biggest difference is that I use percentages over EM's. Why? I don't know. I've been doing it for as long as I can remember and I have never experienced problems with it (except for Opera on occassion — however, I think I know a fix for that now).

    The other difference is that I use unit-less line-height values, so they basically function as multipliers. I really like the way they work like that.

    With the latest browsers implementing more advanced zoom-functionality, you could start using fixed pixel font-sizes. But using relative font-sizes is extremely easy, as you demonstrated. So, why not?

  • trovster says:

    I do a similar thing, although I set the following

    <code>body {

    font: 62.5%/1.45 Verdana, Helvetica, Arial, sans-serif;

    }</code>

    Which is pretty much the same, as it gives you a base of 10px (by majority default) to start with. I also set up a unitless line-height, which cascades nicer than em in this case.

  • Dave Child says:

    I usually use:

    body {

    font: 0.8em etc;

    }

    body * {

    font-size: 100%;

    }

    That sets the base font size to a decent value (0.8em). The second bit solves that annoying bug where IE inherits the specified font size rather than the calculated font size (thus making text progressively smaller for child elements). I usually set the line-height separately (because I often use it to center something vertically).

  • David Aurelio says:

    I think your method is bad practice. Here's why:

    More advanced users may want to change the default font size in their browser to a value comfortable for them.

    These settings would make the calculating of em sizes useless, leading to inconsistently rounded sizes across browsers or even elements.

    Furthermore, with a default font size lower than 16px a website may become unreadable.

    I tried it out myself: I find a default font size of 14px very comfortable on my screen. I changed it to that value and all websites that used your method became unusable. And I really don't want to increase the font size every time I visit my favorite sites.

    If you want to deduce the font size on your website from the user's preferences, please stick to these preferences and use a font size of 1em (or 100%).

    If you want a fixed size across browsers, use px instead, this is what it's for.

    For enabling increasing the font size on IE too, simply use a css hack.

    <code>

    html {

    font-size: 10px;

    *font-size: 0.625em; /* for Internet Explorer */

    }

    </code>

    That's a compromise. I think users considering changing the default font size are mostly using other browsers.

  • David Aurelio says:

    Hey Robert,

    the asterisks in my post (in the css example) have been converted to em-tags. Your preview doesn't tell the truth :(. Could you correct that? thanks.

  • Robert Nyman says:

    Harmen,

    You're welcome!

    Jeroen,

    Whatever works for you is fine by me!

    trovster,

    It should be about the same, yes, although I find it harder to set (rather: read) other elements font size then.

    Dave,

    Hmm, interesting. Thanks for sharing.

    David Aurelio,

    It's an interesting point you bring up. First and foremost, this solution is about getting a consistent font size amongst elements and setting up that relation. This is achieved with this solution; the only difference, in combined scenarios, is that the base to begin from will be rendered smaller, but comparing a base of 16 and 14 is pretty negligible, at least to me.

    Even though I think it's very rare that people specify their own default font size, I'd say not more than about 1%, the main reason for doing that must be to cater for web sites that don't specify a dynamic font based on your font-size setting.

    Also, from what I've seen in web browsers, is that if you specify your own font size (and, optionally font family, font weight etc) you can decide whether to combine it with the CSS in the web pages you visit or completely override it. Hence: I don't really regard it as a problem. Trying to combine custom settings and/or a custom CSS with what web sites offer will most likely not end happy, and that goes for far more things than just font settings, and therefore I think that the complete overriding behavior that web browser vendors have implemented is definitely the way to go.

    About serving em-based fonts to IE and pixel-based fonts to other web browsers: it might initially seem interesting, but I don't really see a point for not giving <code>em</code> to all web browsers. Additionally, for maintenance reasons, code forking is something I'd recommend avoiding as long as possible.

  • Philippe says:

    On paper, I dislike that method of font-sizing, because, as already mentioned, it fails to take into account the user chosen font-size.

    In the real world where designers are often obsessed with microscopically small font-sizes, I kind of like it: My browser' minimum font-size setting allows to completely override that super small font-size, and I end up with text that is nicely readable for me, in the average range of 14~16px. This article has some pointers on this.

    It does beautifully break some sites though, esp when the designer hasn't bothered to check how his or here construction holds up at different font-sizes.

  • Jules says:

    I don't have the book in front of me but Dan Cederholm (Bullet Proof CSS) uses <code>html {font-size: small;}</code> as the initial point. Any benefits of one over the other?

  • David Aurelio says:

    <blockquote cite="Robert Nyman">First and foremost, this solution is about getting a consistent font size amongst elements and setting up that relation.I didn't moan about that – I use this this technique, too. It is extremely useful and makes it easy to maintain a stylesheet. But you stated this article is about <blockquote cite="Robert Nyman">offer[ing] our end users the option to freely resize the text size in their web browser of choice to their liking.and that the problem to solve is that <blockquote cite="Robert Nyman">Internet Explorer [...] doesn’t allow the user to resize any font set with the px (pixel) unit [...]. The problem I see is not mainly your implementation, which can easily overriden in my (or the user's) user stylesheet. But other webdesigners use k3wl em-based font settings in a chaotic way, e.g. the german news site spiegel.de: for the <abbr title="Hypertext Markup Language">html</abbr> element, a font size of 100.01% is defined. The articles' font size of 0.75em is defined for a div with an id rather than for the body element – no chance to override that in an user stylesheet easily.

    I think it is necessary to make web designers even more sensitive that not everybody uses the same screen resolution. Not to break the users preferred font size is part of this. If you need a fixed pixel layout, use pixel values! I don't state that would not be tolerable.

    But if we use a user-specified value we shouldn't manipulate it. In my opinion, the reason why only a few people are using customized font size settings is the fact you break a big part of websites doing it.<blockquote cite="Robert Nyman">About serving em-based fonts to IE and pixel-based fonts to other web browsers: it might initially seem interesting, but I don’t really see a point for not giving em to all web browsers. Additionally, for maintenance reasons, code forking is something I’d recommend avoiding as long as possible.The problem description in your article is about enabling font resizing in <abbr title="Internet Explorer">IE</abbr>. Why should you solve one browser's problem for all? And it isn't causing that many maintenance efforts having one hack in the very first block of your <abbr title="Cascading Style Sheets">CSS</abbr> file.

  • Robert Nyman says:

    Philippe,

    Thanks for the link.

    Jules,

    My experience is that it hasn't been as consistent, but I can't point to any concrete examples. Most web developers have their preferred method that they use and have for a long time.

    David Aurelio,

    and that the problem to solve is that

    My point is that it still does that, by the resizing tools offered in all web browsers.

    Not to break the users preferred font size is part of this.

    That's what I'm arguing. If you have a specified font size, most likely you would want that to override that completely, not try to find some golden solution where it works in conjunction with every web site out there with each and everyone's practices.

    If you need a fixed pixel layout, use pixel values!

    I'm not saying that I need a fixed font size, but basically every web site is out there where a desired font size is wanted for the best end user experience and design communication. Then the end user is free to resize that.

    In my opinion, the reason why only a few people are using customized font size settings is the fact you break a big part of websites doing it.

    In my opinion, I don't think I break it. And the reason definitely isn't that, but rather that most people don't know it can't be done and/or don't see the need for it, except for resizing the text in certain web sites where they don't like the size.

    Why should you solve one browser’s problem for all?

    So, if you have a code solution that would work for all web browsers vs. if you have a slightly leaner version for Firefox, a special one for Opera and a Safari-enhanced one, which would you go for? Naturally, the same solution that works for all. It's common web developer sense.

  • Johan says:

    Your ideas?

    - why not add seperate font styles in pixels for IE WIN??

    - more specific: zoom feature IE7 and fontsize?

    - px vs percentage?

    - line-height in px or relative fontsizes for body? (to accomodate min-fontsize Moz FF)

  • [...] m, since most designs and wanted end results are based in pixels, … Original post by Robert Nyman and powered by Img Fly [...]

  • [...] m, since most designs and wanted end results are based in pixels, … Original post by Robert Nyman and powered by Img Fly [...]

  • Ash Searle says:

    I've been meaning to blog about this…

    <code>font-size: medium</code>

    That's it. That's your default font-size. Apply that to any element at any time and you magically adopt the user's chosen default font-size.

    The browser default font-size also applies to the root element, whatever that may be (i.e. <code>html { font-size: 100% }</code> is redundant.) Think about it: the browser default can't apply to HTML or BODY as they are not guaranteed to exist (browsers can render any old XML document, and the default font-size applies to them.)

    Finally, there are usually TWO default font-sizes. One for general text, and one for monospace text. The monospace font-size (normally 12px vs 16px for standard text) applies ONLY to elements with a font-family of <code>monospace</code>. It doesn't apply to monospaced fonts.

    In other words: <code>font: normal medium/1 'Courier New';</code> would render at 16px even though 'Courier New' is a monospaced font. But <code>font: normal medium/1 monospace</code> will render at 12px…

    Oh, and in case you're wondering: <code>font-family: 'Courier New', monospace;</code> will render at 16px (default) rather than the smaller 12px (default) used for <code>monospace</code>.

  • In my opinion, I don’t think I break it.

    I thik they do. Think of one who wants his "normal" font size not to be 16 but 20 pixel on every page. All other font sizes should scale with it. So his 1em is 20px big and that's what em is all about, to ensecure that all sizes scale with the users own personal 1em (eg. 20px).

    If all sites would ues em the "right" way, he would never have to change the font-size on a specific page becourse the "normal" font-size would always be his own 20px.

  • Probably my only problem with this article is that size 10 is too small. I have average eyes and I find it too small.

    btw just for the record I usually use the same method as you, thanks for the article, always useful as a reference tool :)

  • Siegfried says:

    Indeed a very good article. I personally use font sizes in percent and base the various container sizes then on em.

    Now, the IE has no bug here. The IE behaviour here is simply close to the specification. So if a designer setz a font size to 12px, then this simply means 12px. What most designers do not know is that the physical dimensions of one pixel may vary largely across different media. But still, a size in pixels is a size in pixels. So if a web page designer wishes to fix a font size to some pixels this should not be ignored by any browser. I had a long discussion about that in the mozilla zine some time ago. What i learned there was, that this special Firefox feature to let the user ignore the page design by rezizing fonts regardless of their pixel size was put in because the vast majority of real world pages where designed so poorly. So to be precise, this "feature" is indeed a "bug" in Firefox, altough a bug which unfortunately is necessary to make poorly designed pages usable.

    The correct place to set the users preferences (f.ex. the preferred font size) is the preferences dialogue. Firefox has this and IE has this. This has exactly the efects Jeena Paradis already pointed out. And a well designed page always referes to these sizes. Font sizes in pixels are an exception and should, if at all, be used very very sparse. F.ex. i had for some time navigation buttons on my page. These had a grafical button as background images. So then the size of the button element was set exactly to the size of that grafik. The text on the button then had to fit into that box and that grafik. So for these navigation texts i fixed the font size to the appropriate number of pixels. The "font size bug" in Firefox made it possible to crash this layout. Lately i threw out those grafical buttons. Besides not adapting to very small screens (PDAs and the like) i wanted to avoid the layout beeing crashed by font resizing.

    Font size fixing in pixels may be necessary, but exceptional and rare. But since under very rare and special conditions this may be necessary resizing fonts may make those pages unusable. Tis Firefox "feature" is a quite dangerous one, altough i admit that it is necessary because of those many poorly designed web pages. But it is no bug that the IE does not have this "feature".

  • While I understand the problem and the reasoning this method might break (being the result not the same as the setting) the advantage of setting a new default font size in your browser, I ask you: what is the solution?

    The solution is not fixed font-sizes, because that does not solve the source of the problem mentioned above. In fact, I doubt it can be solved. Simply fact is that for most people the default font size is too large, meaning designers need to accommodate for that by setting the root's font size element to a fraction of it.

    After all, even now when people set their default font size to the actual default (usually 16px), we still don't get that. So, is the correct conclusion that the default font size is bound to break anything that tries to take the user's setting into account, while still providing decent font sizes?

    I probably don't understand the problem, but if I do, I would encourage the people that change their default font size to stop pretending they are important, because all it takes is to increase your non-standard default font size by 1.6 times.

  • Robert Nyman says:

    Johan,

    why not add seperate font styles in pixels for IE WIN??

    Answered twice above.

    more specific: zoom feature IE7 and fontsize?

    Doesn't affect font-size at all, but only the display of the entire web page.

    px vs percentage?

    That's a long discussion. My take is that percentage would be the way to go then to make it scalable.

    line-height in px or relative fontsizes for body? (to accomodate min-fontsize Moz FF)

    Definitely a discussion for another day… :-)

    Ash,

    Absolutely. But while that's true, most designs out there are based on a preferred pixel size that will work best with the design. My take is that one should try to make the default font match that font size, but still be scalable (through em/percentage etc).

    Jeena,

    Well, I think that's the general problem: to try and combine a custom font size with the font size set in a web page. In my opinion, it shouldn't be done.

    But if someone has chosen a larger font size, it will be smaller than the one set through preferences, but still larger than the normal font size. So, in that respect, it's still dynamic, but it isn't an exact match for the setting. But if you want that, my take is that you should use the overriding option.

    Jermayn,

    I understand that. 10 pixels is just a simple example for demo and calculating purposes. Go with what suits you best. :-)

    And thanks, I'm glad it was useful to you! :-)

    Siegfrid,

    Thank you!

    Good input about IE.

  • Johan says:

    There is a correlation between page lay-outs and font styling:

    - fluid lay-outs use ems and percentage for font-size depending on the way the fluid container boxes are styled to prevent breakage.

    - zoom lay-outs are ems all the way, both font-sizing and container boxes

    - fixed lay-outs use px, ems and percentage for font-size but have px styled container boxes

    The body is the preferred way to set a default font-size but does vary a lot, as I have seen with many a design.

    body {font-size:100.01%; /* Opera fix*/}

    body {font-size: 100%;}}

    body{font-size: 76%; /* Owen Briggs idea?? */}

    body{font-size: 62.5%; /* very small */}

    body{font-size:0.8em; /* any quirks possible??*/}

    body{font-size: small; /* IE5 WIN and font sizing}

    All of the above are related to the px range 12-16px for default font-size.

    Other questions:

    - What is the default font-size to aim for: 12px or 16px

    - Do we need JS to control eg max font-sizing?

    - line-height has many settings from 1.2 to 1.5 which equals about 19px for average normal text treatments, what is a default line-height value here?

  • NICCAI says:

    I prefer to set body font-size equal to x-small which is approximately 10px (but not in IE 5.5 which is xx-small). From there I simply size up using percentages – 110% approx = to 11px, 160% approx = to 16px, etc…

  • Robert Nyman says:

    Johan,

    I’ll leave it to other readers to answer those questions, to see what the general opinion is.

    NICCAI,

    Thanks for sharing!

  • Ash Searle says:

    To answer some of Johan’s questions with my own opinion:
    The usual default font-size is 16px (tested across different browsers and platforms.) 12px is the default only for monospaced text (e.g. text in a <pre> block)

    No, we don’t need JS to control a max font-size setting (why would you? If a user wants to zoom to 160pt size, why stop them? They can always *always* break your layout if they want to…)

    Default line-height:
    alert(getComputedStyle(document.documentElement, null).lineHeight)
    This turns out to be normal… The CSS 2.1 spec says that browsers should implement a ‘reasonable’ value for normal somewhere between 1.0 and 1.2.

    I think the line-height depends on the application: if you’re going to use sub-scripts and super-scripts (footnotes?) then you’ll want a bigger line-height. Also, academic texts generally use a line-height of either 1.5 or 2 (allowing people to write notes between lines on a printed copy.)

    As for setting <body> font-size: most pages contain a few div elements used purely for layout, so why not set the font-size on each containing element (to ‘small’, ‘medium’ (16px) or ‘large’) then use context-relative em values?

  • Robert Nyman says:

    I agree with Ash here, that someone can always break the design anyway. And hell, if they want a gargantuan font size, just let them.

    What might be interesting in relation to that, though, is the question if we should prevent users from having a too small font-size. If the end user, accidentally (trust me, this has been up for a lot of web sites), have set their font size to smaller, the script could bump the default level up to a reasonable standard.

  • Stefan Van Reeth says:

    Great article, good discussion. Except for agreeing mostly with Robert, there's not much to add to it anymore, so I leave it like this.

    Robert, about your last question: nope. Same answer as the big font sizes. If the user wants microscopic fonts, just let them ;).

  • Jens Wedin says:

    Here's a great resource when it comes to font sizes.

    I´ve used his idea on a few big sites.

    http://www.thenoodleincident.com/tutorials/typogr

  • Yeah, the noodle method is the only one I dare use on larger projects.

  • Robert Nyman says:

    Stefan, Jens, Anton,

    Thanks for your comments and input!

  • kimblim says:

    Just a quick question regarding this:

    h1{

    font: 1.7em/1em Arial, Helvetica, sans-serif;

    }

    Why the "1.7em/1em" ? Whats the 1em for?

  • kimblim says:

    But ofcourse! Thanx!

  • Robert Nyman says:

    kimblim,

    The value following the slash is line-height.

  • Kenneth Sundqvist says:

    One rule that should be used when deciding on the size for the body copy is that at the largest user setting in IE6 it should hit the bolder state (which is also +2 in FX, and that is less irritable to scale to than +3). If you don't know what that means try to scale the font on a page and watch it get bolder at some point.

    This is a usability thing. Because there are people with poor vision who use IE6 who like to scale their text and hitting that bolder spot makes reading a lot easier to read. Even I scale text to bolder when I'm sitting back from the monitor to read longer texts, such as these comments.

    Needless to say, the cool, crisp, small designer size fails at this, especially with the horrible medium grey colour.

    The Noodle Incident guide, posted above, is very good indeed. And I use it myself all the time.

  • [...] How to specify dynamic font sizes consistently with CSS Let’s say we want a web page where the general font size is 10 pixels. We then take the desired font size, 10, and divide it with the default web browser size, 16: 10 / 16 = 0.625. This is the value that we use for the em unit on the body element: .625e (tags: em) [...]

  • Vedran says:

    I can’t see the implementation of this on you blog. Am i just not able to find it? In that case: preach what you do. Otherwise my bad – or not (it should be easily findable)!

    Vedran

  • Robert Nyman says:

    Vedran,

    In line 12 of the page code, there’s a link to the external stylesheet, which contains the code. Not that difficult, eh? :-)

    The stylesheet in turn is gzipped for better performance, so no, you can’t read it. But if you use Firebug or any other de facto development tool, you can easily check the font and how it is set.

  • zev says:

    Does anyone know how to adjust font size based on text length? in other words, I want to have a CSS class defined that I can use for dynamic text, but force the text to fit in an area that is confined to a specific pixel width.

  • Robert Nyman says:

    zev,

    There's not really a perfect solution for that. You could play around with <code>text-overflow</code> in CSS, but it's seldom optimal.

  • [...] of people use (including Dan Cederholm in his book Bulletproof CSS, which I recommend), is to make the base font size equivalent to 10px, instead of dealing with the typical default base font size of 16px. I don’t know how much it [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>