px – em – % – pt – keyword

Avatar of Chris Coyier
Chris Coyier on

When it comes to setting the size of type in CSS, you have lots of options. You can apply a “keyword”, like p { font-size: small; } or a numerical value. When using a value, you need to declare a unit of measure which itself has four options. Which is best? It depends of course. Let’s take a look:


Valid options for setting font-size in keyword are xx-small, x-small, small, medium, large, x-large, and xx-large as well as relative keywords smaller and larger.

Surprisingly enough, keyword sizing is pretty consistent across browsers and platforms. See below a test page in Opera, Firefox, IE 6, and Safari:

Note that although the they are pretty close, there are differences in where the lines break and total paragraph height.

Only when one of the relative sizing keywords are used does the “cascade” kick in and the font-size of the parent element effects the child. For example, if the parent was set to medium and the child was set to large. Only large would be used to size the child element. However, if the parent was set to medium and the child was set to larger (note, larger not just large), the child would be “sized up” to be larger than the parents font size. Change the parent, change the child, hence a “cascade”.

Keywords are a perfectly fine way to size fonts on the web. One of the more popular techniques is to set a keyword font-size on the body element, and then use relative font-sizing every where else on the page. This gives the ability to really easily size up or down the font size on the page (e.g. with JavaScript) and have the whole page move up and down in size consistently.

However, keywords don’t offer very fine-grained control of your typography, as the choices are fairly limited.


If you need fine-grained control, sizing fonts in pixel values (px) is an excellent choice (it’s my favorite). On a computer screen, it doesn’t get any more accurate than a single pixel. With sizing fonts in pixels, you are literally telling browsers to render the letters exactly that number of pixels in height:

Windows, Mac, aliased, anti-aliased, cross-browsers, doesn’t matter, a font set at 14px will be 14px tall. But that isn’t to say there won’t still be some variation. In a quick test below, the results were slightly more consistent than with keywords but not identical:

Due to the nature of pixel values, they do not cascade. If a parent element has an 18px pixel size and the child is 16px, the child will be 16px. However, font-sizing settings can be using in combination. For example, if the parent was set to 16px and the child was set to larger, the child would indeed come out larger than the parent. A quick test showed me this:

“Larger” bumped the 16px of the parent into 20px, a 25% increase.

Pixels have gotten a bad wrap in the past for accessibility and usability concerns. In IE 6 and below, font-sizes set in pixels cannot be resized by the user. That means that us hip young healthy designers can set type in 12px and read it on the screen just fine, but when folks a little longer in the tooth go to bump up the size so they can read it, they are unable to. This is really IE 6’s fault, not ours, but we gots what we gots and we have to deal with it.

Setting font-size in pixels is the most accurate (and I find the most satisfying) method, but do take into consideration the number of visitors still using IE 6 on your site and their accessibility needs. We are right on the bleeding edge of not needing to care about this anymore.


Em values are probably the most difficult values to wrap the ol’ noodle around, probably because the very concept of them is abstract and arbitrary. Here’s the scoop: 1em is equal to the current font-size of the element in question. If you haven’t set font size anywhere on the page, then it would be the browser default, which is probably 16px. So by default 1em = 16px. If you were to go and set a font-size of 20px on your body, then 1em = 20px.

Historically I think the “em” value is based on the width of the uppercase M, but don’t quote me on that.

Things start to get slightly more complicated with em’s when we start setting up more complex font sizing. Say we need a header with a larger font-size, so we set h1 { font-size: 2em; } That “2” is essentially a multiplier of the current em value. So if the current em size is 16px, that header tag is going to turn out to be 32px. That math works out cleanly, but you can imagine that it often doesn’t and rounding needs to take place.

The most popular method in working with em values is to set the font-size on the body to 62.5%. Because the default browser font-size is 16px, this makes it 10px (without hard-setting it to 10px, which wouldn’t cascade). Using 10 as a multiplier is much easier than using 16. This way, you need a font size of 18px? Use font-size: 1.8em.

So why both with all this em business when it’s just an abstraction of using pixel values anyway? Three possible reasons:

  1. The ARE resizeable in IE 6
  2. The relationship to other sizes (elastic width sites)
  3. Em’s cascade like a mo-fo

The first one is the big one. If you basically want to use pixels but don’t like the accessibility problems, em’s may be the ticket for you.

Em’s aren’t just for fonts, it’s a unit of measure that you can use for any other length (height, width, etc). Elastic width sites use em values for everything, which essentially makes the site “zoomable”, meaning that when you bump the font-size up everything bumps up all the way down to the width of the site. Em’s have a direct relationship to each other in this way. If you have a box that is 10em in height, and a font inside that is 1em in size, it will take up exactly 1/10 the height of that box. That exact proportional relationship makes em values a powerful web design technique.

There is one potential concern with em’s, with regards to #3. They do indeed cascade. Every em value is relative to its parents value. If you are using em’s as a straight substitution for pixel values, this can cause problems. For example, you might set both your “p” (paragraph) and “li” (list item) font-sizes to be 1.2em. Looks great for you today, but tomorrow some content is published to the site that has a paragraph inside a list item. Those two values will cascade (1.2 x 1.2) and that list item will be bigger in font-size than any of the others. No way around that, other than removing the tag.


Percentages are fairly obvious in how they work. That is, they work just how you think they will. If a parent has the font-size of 20px and the child has a font-size of 50%, it will come out to 10px. Just like em’s the very nature of percentage sizing is that it is relative. It also cascades in the same way, so the very problem described above of the list item and the paragraph item applies here as well.

A popular technique using percentages for font-sizing is to set a reasonable font size on the body (like “small”) and then using percentages for everything else. This allows you to swap out that keyword in a single place in order to scale the font sizing on the entire page, which makes things like a font-resizer interface feature a lot easier.


The final unit of measurement that it is possible to declare font sizes in is point values (pt). Point values are only for print CSS! A point is a unit of measurement used for real-life ink-on-paper typography. 72pts = one inch. One inch = one real-life inch like-on-a-ruler. Not an inch on a screen, which is totally arbitrary based on resolution.

Just like how pixels are dead-accurate on monitors for font-sizing, point sizes are dead-accurate on paper. For the best cross-browser and cross-platform results while printing pages, set up a print stylesheet and size all fonts with point sizes.

For good measure, the reason we don’t use point sizes for screen display (other than it being absurd), is that the cross-browser results are drastically different: