CSS3 width and height media queries work best with relative units
In CSS the “@media” rule defines a block style rules which only apply to a given medium like print or on screen.
/* These styles apply when the web page is printed */
@media print
{
body {background: none;}
}
CSS3 adds additional features to the @media rule – media queries. Media queries allow adding conditions to media rules. Both the media and the conditions must match for the rules to apply. This technique is widely used to provide different layouts for different devices; for example one layout for phones, one for tablets and desktops. The following media rule applies to screens (computer monitors, phones, etc) that are less than 600 pixels wide in portrait orientation.
@media screen and (max-width: 600px) and (orientation: portrait) {
/* skinny screen styles here */
}
This is a commonly used rule to provide style rules form smaller screens, like on phones, instead of medium and large screens like tablet and desktop computers. This rule measures the screen width in pixels – which seems logical. Screens are measured in pixels, right?
The problem
When the iPhone 4 was introduced, the screen doubled (quadrupled?) in resolution from 320px x 480px to 640px x 960px. This didn’t present a problem for website authors though, because the iPhone 4’s web browser used “logical” pixels internally for screen measurement instead of “device” pixels[1]. Media queries which worked for the standard resolution iPhone 3GS would work for the high resolution iPhone 4 (and 4S and 5).
The term “device pixels” refer to the actual, physical grid of picture elements making up a screen. “Logical pixels” are a unit of measure used in web authoring equal to 1/96 of an inch. The term “logical” pixels never came up much in web development circles before the high-resolution iPhone 4, because most ‘standard’ resolution screens equated logical pixels with physical pixels. Pixels were pixels. This made things easy. HTML, CSS, and JavaScript all work with logical pixels, and logical pixels matched the physical pixels, until…
The Samsung Galaxy S3[2] has a high resolution display, similar to the iPhone. The physical resolution is 720px by 1280px. Generally, the S3 uses logical pixels, e.g. JavaScript will calculate the screen dimensions as 360px by 640px[3]. However, the S3 treats media queries for screened media differently — it measures physical pixels instead of logical pixels. So our small screen media query above would fail; the S3 could end up displaying a large format layout.
@media screen and (max-width: 600px) and (orientation: portrait) {
/* Samsung Galaxy S3 will not apply these rules, it sees the screen as 720px wide */
}
Where iOS assumes that the web author wants logical pixels, the Samsung Galaxy S3 assumes the web author wants physical or device pixels. You might think that you could just test for the S3’s resolution like so:
@media screen and (max-width: 720px) and (orientation: portrait) {
/* Poor kindle fire gets a phone layout */
}
But this leaves out small tablets like the Kindle Fire (at 600px x 1024px). Using pixels in media queries can end up sending tablets a phone layout and phones a tablet layout.
Problematic.
EMs to the rescue
An EM is a unit of measure which comes from typography. It’s related to the width of the capital letter “M” in a given typeface, at a given size. If you’ve set your font size at 12pt then 1em = 12pt, 2em = 24pt, 1.5em = 18pt, and so on.
EMs are most often used in CSS to define font sizes in such a way that they are consistently sized relative to one another, but still allow the user to adjust the overall size of the type. Some web designers prefer using EMs for all length measurements because doing so automatically relates all visual elements back to the base font size.
If no other font size is defined, EMs will be based upon the system’s default font size – which is 16 logical pixels[4] unless the user has changed their browser settings or zoomed in on the page. So to determine a breakpoint between phone and tablet in portrait orientation, we just divide the logical pixels dimensions by 16.
Using the portrait dimensions of the Kindle Fire we get the following.
600px / 16xpx = 37.5em
This gives us a reasonable division between small-sized phone-type displays and tablet-to-desktop displays. Media queries can use any valid CSS length units, so we can define a media query like so:
@media screen and (max-width: 37.5em) and (orientation: portrait) {
/* style all the things, works everywhere */
}
Trading pixels for EMs works consistently on all mobile devices I’ve tested, including:
- iPhone 4 through 5
- iPad (with Retina display)
- iPad mini
- Droid Razr
- Samsung Galaxy S2
- Samsung Galaxy S3
- Kindle Fire (second edition)
This also worked well in various Android emulators I tried.
Why this works
There is no such thing as a ‘device EM’ – all EMs are logical units. Since browser makers have settled on a defacto-standard font size, EMs give us a reliable, logical measurement unit which avoids the problems of inconsistent pixel definitions.
Since media queries measure the viewport (the screen, the printed page, etc) web authors don’t have to worry about a media query being fouled up by their choice of font size. The following will work just fine:
@media screen (max-width: 37.5em) {
/* i like big wordz */
html, body {font-size: 36px;}
}
If you think about how CSS works, it shouldn’t be surprising that this is not a problem. From the perspective of the Document Object Model, the HTML element is a child of the viewport; CSS doesn’t allow you to change the font size of the viewport. No problem.
Byproduct of enhanced usability
If a user has changed their default browser font size (larger or smaller), then a media query relying on EMs changes accordingly. Suppose I have poor eyesight and I bump my browser’s default font size up to 200% — the 37.5em break point defined above will take effect at 1200px instead of 600px. This means I’ll get a phone sized layout instead of a tablet or desktop layout. This is a good thing. The layout is now optimized to show me the most content possible given my browser zoom settings (and what my eyesight can handle). The layout is responsive to screen size and user preference.
It’s worth pointing out that both Firefox and Internet Explorer 9 apply these changes to media queries on the fly — that is if you change the font size, these browsers will reflow web content based upon the new default font size (you can change your desktop layout to a tablet layout). Webkit browsers only apply the zoom settings on page load, requiring a reload for the changes to take effect.
Demo
I like the following set of media queries to target small, medium, and large screens. This only tests for width and not orientation, assuming users can scroll in any orientation.
@media screen and (max-width: 37.5em)
{
/* small / phone */
}
@media screen and (min-width: 37.5em) and (max-width: 64em)
{
/* medium / tablet */
}
@media screen and (min-width: 64em)
{
/* large / desktop */
}
Resize the browser window to see the effect of the media query.
-
It’s possible to test for device pixels instead of logical pixels in a media query, but various platforms are so inconsistent with this feature that it’s not very useful. ↩
-
This is likely to be a problem on other platforms too – this is just where I discovered the problem. ↩
-
Assuming, of course, that you’re using the ‘viewport’ meta tag and scaling the view to the device width. ↩
-
I haven’t found any documentation that makes it official, but 16px seems to be the default font size for browsers. ↩
This is a great article Michael, thanks!
I’ve been using REMs lately because they’re always relative to the base and you can avoid EM stacking problems. But I use a pixel fallback for older browsers.
I’ve also switched to a base font size of 62.5% which allows me to specify my fonts in a relatively intuitive way as 1.6rems will always equal 16pxs. Well, mostly. Handier than guessing at least.
Gonna try switching all my queries tomorrow!
I’ve been using REMs for type too – they’re the bee’s knees.
Unfortunately REMs don’t work in media queries in Webkit browsers – they never evaluate to true, so the styles contained in the rule are never seen. Internet Explorer 9 and Firefox work just fine with REMs in media queries, and according to the specs, REMs should work fine in Webkit too. But they don’t.
So yeah. EMs for media queries and REMs for type.