iOS 8 has finally fixed a mobile web annoyance that’s been kicking around for years. If you want to skip the history lesson, go here for the details.
When is a pixel not a pixel? Almost always, if you’re on mobile.
Here’s the problem: in the beginning, smartphone web development was based entirely around the iPhone. While it defaulted to rendering pages as if they were on a desktop (and, indeed, still does) we could change that with a quick
<meta name="viewport" width="device-width">
As the tag might imply, it rendered the page at a native resolution for the iPhone – 320 pixels wide. Unfortunately, this was too easy. Everyone hard-coded the concept of 320px wide pages everywhere, so when Android phones with 480px wide screens arrived, they had to lie about their
– they said it was 320 pixels and scaled up accordingly. With the market (or, more accurately, mind share) still dominated by the iPhone, this went mostly unnoticed.
(This is actually a great example of why the mobile web is so broken compared to native development. Market considerations will always, always win over standards compliance – if you ever expect anything different you will be disappointed. Native environments only have one implementation, so they avoid this.)
Then the iPhone 4 came stomping in and ruined everything.
Retina! It’s great. Looks fantastic. Ruined pixel measurements forever. The iPhone 4 screen was 640 pixels wide, but Apple quickly discovered what the Android team had – if you make
equal 640 pixels, a lot of web sites are going to look awful. So Apple lied, too. They also introduced a new property,
window.innerWidth * window.devicePixelRatio
Gross? Gross. But it worked. If you didn’t care about the actual physical size (and mostly, you don’t) you can carry on using these make-believe jumbo pixels. A much better alternative was to abandon pixels altogether and start thinking about things in terms of percentages, or, even better, viewport widths (vw) and viewport heights (vh). There was one problem still left over, though – borders.
Although you measure borders the exact same way you do anything else in CSS, you don’t usually want to. For instance, when I say I want a
tag to be
wide, I mean that I want it to take up exactly 8/10ths of the screen width. When I specify a
, what I really mean is “as thin a border as I can get” – it’s just that one pixel is the smallest measurement available.
Except, with device pixel ratios, it isn’t. On a retina iPhone a one pixel border is actually
1 * window.devicePixelRatio
, i.e. two pixels, i.e. your borders don’t look really clean and sharp on a retina display. The logical answer is to specify a border-width of 0.5px – but no browser was capable of interpreting that. Until now.
iOS8 saves, but it’s still kind of gross.
iOS8 does understand 0.5px border widths, and it works exactly as you’d imagine. Except it still doesn’t work anywhere else. Even more frustratingly, it’s in CSS limbo. If 0.5 pixels was considered an invalid CSS declaration, you could just do:
The cascading nature of CSS would mean that browsers would ignore the 0.5px value and revert to the 1px one. But browsers do consider it valid – they just round it to zero and you get no border.
Alex Dieulot has come up with a quick hack that tests for 0.5 pixel width support, and applies a CSS class accordingly. If you’re using a CSS preprocessor you can make a pretty simple function that will tidy up your source code, but it still feels ugly, doesn’t it? Here’s hoping we see wider support soon – Chromium has a ticket open for it, at least.
Unfortunately, the saga isn’t over – the iPhone 6 Plus reports a device pixel ratio of 3 (even though that isn’t accurate) and the Android side of the world now has a dizzying array of device pixel ratios – some all the way up to 4. Is it too much to ask for a