Josh (the blog)

I’ve delivered simple, clear and easy-to-use services for 20 years, for startups, scaleups and government. I write about the nerdy bits here.


@joahua

In defense of Data URIs

A friend sent this Mobify article my way with a one-liner comment: “Sprites over Data URIs?”

True to Bettridge’s law of headlines, here’s my reaction.

No. :P

Their test image is a 17.6KB file. If you’re serving up any files that big you’ve already lost (see also, our jumbo sprites on mastheads). See also the recommendation at the end of the post to “use data URIs in exactly the right way” – which, I hope, is broadly how we’ve been using them.

This isn’t to completely disregard these results, but I would like to see it run with better test data (images in the 500 byte - 4KB range, say) to reflect actual best practice usage. Again, if you’re using images as large as they are, there are probably ample opportunities to make easier micro-optimisations, which is what this whole debate is about.

That said, there are plenty of other problems with this article.

The penalty of data URIs for older phones is severe - on Android 2.2 materialization of five 17.8kB images took almost 2 seconds in the data URI condition!

Note how Android 2.2 is a complete outlier in the original chart and subsequent observations – it’s not supported by us, and comprises >0.5% of m-site traffic (source: GA, last 30 days). It’s a red herring that would be better off excluded for honest charting – especially given the author’s own data reflects ours – see his own RUM chart distribution for evidence.

There’s also a methodology problem in that this is dealing with inlined src attributes on elements, which introduces additional DOM performance and possibly reflow considerations assuming these are visibly attached that you wouldn’t experience with background images in CSS. Completely agree that it’s harder to measure the nonexistent ‘load’ event of CSS background images, but that isn’t a defense for the disingenuous finding that this equally impacts all inlined images. (Though the high CPU assumption seems sound enough.)

Here are a couple of assumptions of my own:

  1. CSS parsing is necessarily more efficient than popping elements onto an unstable DOM, unless you attach the CSS late in the load. Note how in the histogram charts image materialization starts dependably late (i.e. reliably >5ms in) – CSS parsing has happened long before this assuming it was in the head.

  2. CSS uses ‘classes’, and is therefore more efficient than creating separate DOM elements with separate attributes that need to be individually unpacked. Therefore, measuring the materialization of five images is a poor test, unless four of those were deep cloned from an original element and browsers somehow cache the unpacked binary data (poking around source code and bug reports might help ascertain this). Suspect the test didn’t do this, and if not, strongly doubt that browsers would have stored a hash of the unpacked encoded data to quickly reference – memory is too tight on mobile to bother with this.

  3. Because of 2, multiple instances of elements (commenting icons, media type indicators, etc.?) will not incur any measurable performance hit over loading just a single element, though obviously more elements on the DOM costs something.

  4. Even disregarding 1-3, we are splitting hairs here. It’s a micro-optimisation whereby we’re hoping to move events in the 10-20ms range (if you accept that a 17KB image is even a reasonable test) to the 1-5ms range. That sounds big, but consider the number of elements we are doing this with, and consider user perception of this on pages that can already take over a second to load.

Data inlining, especially with preprocessors, is about maintainable code (because sprites are the Dementors of the front-end world), repeating/large-but-small-filesize backgrounds, and the ability to pick whatever format you like should you so choose (i.e. for some very small images, GIF outperforms PNG, and for some textures, JPG outperforms PNG, and with inlining you can CHOOSE to do this!)

If this is really keeping you up at night, look into using Appcache as well, because that’ll hopefully save on some network pain (assuming we’re worried about that given how bullish the original post is on pipelining). I apologise in advance for the caching complexity and curly support requests it creates!

Still firmly of the view that there’s a big place for data-inlining on the mobile web.

SSI on OS X Mountain Lion Server 10.8.3

We currently have a strong dependency on SSI at work and were recently offered a nicely-specc’d Xserve that was being retired for use as an in-house preview server.

I’ve got my doubts about SSI, but we have literally thousands of templates that require it for includes. It also functions in subtly different ways to PHP, and we’re not about to embark on any bulk conversions of our front-end templates into haml, jade, or whatever else is in vogue – the legacy cost is too great.

Apologia out of the way, we are using Mountain Lion’s in-built Apache 2.2 server after a trigger-happy rm -rf on my part killed a painstakingly crafted homebrew version implemented by a colleague.

Config locations

If you are using the in-built server, you are using virtual hosts.

This simple truth is not well documented online, so if you are spending time in /etc/apache2/httpd.conf you will not succeed. The default port 80 (non-SSL) vhost file is at /Library/Server/Web/Config/apache2/sites/0000_any_80_.conf.

Enabling SSI parsing

There’s ample info online about enabling mod_include, but what isn’t mentioned is that you need a handler specified in the virtualhost config.

AddHandler server-parsed .html is all it takes, but this won’t necessarily have effect within your directive unless you tell it to.

An age old tale of computers doing exactly what you tell them. And a wasted Friday afternoon!

IE7 expected a string, identifier or number

JSON is nice to read. It’s also nice when parsers are forgiving, but here we are.

I just spent ages chasing [what I thought was] a bug using $.getJSON in IE7 that turned out to be an issue with something else entirely.

If you’re using $(el).css or .animate with JSON, make sure you’ve terminated all items correctly and don’t have any hanging commas left over:

Hanging comma at end of JSON params

This case sent me particularly crazy as I was also dealing with an actual JSON request in the same general vicinity that I’d validated a bunch of times and seemed perfect.

Note to self: Before you go down the whitespace and text encoding rabbithole next time, make sure you’re actually checking the right JSON!

Some tips for mobile CSS

On “retina”/high DPI:

  • SVG sprites are a beautiful thing. Make the canvas size work at a device ratio of 1, and capable user agents (read: mobile Safari) will do the hard work for you. Don’t create a double-resolution sprite, because you’ll need to manually re-declare all your sizing and positioning, which is no fun.
  • Opera supports SVG but doesn’t bother to scale it. In most cases, this means a well-quantised PNG is going to be smaller.
  • Opera’s device-pixel-ratios are actually expressed as ratios (x/y), as opposed to floats (1.5). I actually think this makes a lot of sense, though I’m pretty sure they’ve already lost this battle.
  • Mozilla/Fennec doesn’t do ratios in a standard way, either – their syntax is “min–moz-device-pixel-ratio”. They obviously missed the part where browser prefixes are supposed to be, well, prefixed. This was done back in late 2010 and they looked at the -webkit implementation first, before implementing it in a very different fashion without much debate. It’s a mind-bogglingly undiscussed issue on Bugzilla and the wider Internet.
  • Android DPI values for DevicesYouKnowAbout(tm) hover around the 1.5 mark. Anything lower than this and the device capabilities probably don’t make it worthwhile, and anything higher is probably an iPhone. Higher resolution Android devices just don’t seem to exist, or, for the most part, report themselves as existing (March 2012 – yeah, the Nexus and Galaxy S2 and stuff are big, but don’t seem to be documented as reporting high DPIs).
  • Okay, so DPI values aren’t quite that simple. To quote A List Apart, “we don’t know what each device considers a pixel and this is a problem.” Read about it, test lots, buy Android devices if you want to be sure.

Miscellaneous gotchas:

  • Certain Android variants – I don’t have enough devices to test conclusively – drop gradient backgrounds, opacity etc. (presumably for performance reasons) despite having “A grade” capabilities in a lot of other senses. Make sure you do simple things like having backgrounds and borders on elements that would have previously been divided by gradient contrast. Your featurephone visitors will also thank you. In an ideal world you’d probably progressively enhance these kind of things, rather than patching it up afterwards, but it’s easy to get caught up in the excitement of only needing to support “A Grade” browsers (accepting that IE is suitably banished from Mobile land) and just forget. Don’t forget.
  • Beware HTML5 manifests – they raise storage prompts that could scare some users in some browsers (“why is this site storing info about me?). They’re also actually not that effective unless you pin an app to the home screen, so reconsider your need to use one.
  • Fennec uses left/right swiping for accessing browser UI. This is a pretty dumb design decision, because it breaks left/right swiping gestures (think photo galleries). Expose an alternative UI (Next/Prev buttons perhaps?) for browsers that don’t do touch events in this way.
  • Windows Phone 7. You’ve heard good things, right? Kinda. It’s definitely up there, but early versions of this device (2011) had a browser that was more or less equivalent to IE7 with a few IE8 features thrown in for good measure. I’m not bothering to support it. However, even the latest versions (March 2012) don’t support touch events (!) so expect it to be even worse than Fennec for conventional swipe-driven mobile UI in the browser. It does have Canvas support, but apparently lacks SVG in the browser.
  • SVG gradient support is patchy (probably for similar reasons Android devices choose not to render CSS gradients)

Tips:

  • Use text-rendering: optimizeLegibility; on headings as it does great things on the iPhone 4. (via Menacing Cloud)
  • Flash ads work. This is staggering, but true. Expect that Android devices will successfully display Flash ads, and pay attention to different markup that gets adserved for these devices as it will possibly impact your CSS.
  • Image based ads scale down quite well. It’s tricky getting mobile inventory smaller than 300px wide, but if you’re expecting 240px devices then just use max-width:100% for this. It’s better than breaking the layout or not serving ads to these screens, not that there are many of them left.

You[r inner copywriter[/editor]] will love this.

[Visit] Telescopic Text.

Click [[any of] the [first [three]]] words.

(My formatting will make sense when you start using it)