One of Responsive Web Design’s biggest critiques is that it is responsible for slow load times and extreme page bloat. The best response to this criticism is Tim Kadlec’s Great Divorce:
“Blame the implementation, not the technique”. – Tim Kadlec
tl;dr — If a page clocks in at 28MB and 399 HTTP Requests, that’s not the fault of responsive design, that’s the fault an organization that doesn’t care about web performance.
I agree with Tim, but “blame the implementation” doesn’t fully answer the question for me. Surely RWD has a detectable footprint. If a client with high-performance needs asks “How does RWD affect performance?”, my answer cannot be “None cuz it’s not my problem.”
So I’ll put my own site under the microscope. I’m a seasoned RWD developer, I’ve worked on large projects, I co-host an award-winning front-end sound effects podcast, and I care about web performance; so my own blog should be a perfect use case.
The first part of any optimization work is to look into the mirror and find all your flabby parts.
This data was taken awhile ago and a little bit has changed, but the data more-or-less holds up. 174.975kb total. Not too shabby.
Let’s keep the focus on how much the fluid grid, flexible media, and media-queries of RWD are costing my peformance baseline.
Third-Party Performance Notes
For the purpose of this experiment, I’m going to conveniently ignore the third-party scripts like analytics, advertisements, and comments; focusing on the parts that I authored and can fix. Third-party performance is pretty mixed bag.
Stylesheets: 24.2kb, (13.8%)
||Size|% of Total| |Total CSS|24.2kb|100%| |Vendor prefixes|1.4kb|5.8%| |Media queries|2.8kb |11.5%|
Media queries are responsible for ~12% (2.8kb) of my CSS weight. For every 8 lines of CSS I write, one more is needed to make it responsive. This might go up on a larger more complex site, but this seems very reasonable for a cross-device approach. I also included the weight of vendor prefixed rules to give perspective on how much desktop browser compatibility is costing me.
CSS Performance Notes
On "hard refresh" I can see the webfonts blocking the type rendering. I'll use Filament Group’s loadCSS to bring my webfonts in and maybe re-evaluate my font-stack entirely (see below).
From there I'm definitely going to look at optimizing for the Critical Path.
Layout thrashing and repaints is at ~4 cycles. Maybe worth investigating if I can lower that.
Images: 31.92kb (18.2%)
I followed the advice from my 2012 A List Apart article “Mo’ Pixels, Mo’ Problems”, it seems to be paying off. Considering the average site has over 1.1MB of images, I feel good about this. I went with inline SVG (32kb) and icon-fonts (26kb) across the board as my responsive image solution. I probably need a fallback IE8+Android 2.3 solution, but that will be ongoing work.
Image Performance Notes
A quick run through SVGO tells me I could save ~8KB on each page load. I am planning on switching to SVG sprites, so that will save a
@font-face request. (see below)
On pages with raster images, I'll pre-emptively add the
lazyload attribute to medium importance images and go ahead and start using
<picture> for large-format in-post images.
||Size|% of Total| |jQuery|33.1kb|78%| |Prism.js, Lettering.js, other rando-scripts|7kb|17.3%| |FitText.js, FitVids.js|2kb|4.7%|
Webfonts: 42.6kb, (24.3%)
||Size|% of Total| |Open Sans|31.7kb|54.1%| |SymbolSet|26.8kb|45.8%|
None of this bloat is directly impacted by RWD, unless you count Symbolset, an icon font, as an RWD image solution. That ding would be negated by a PNG sprite which would be nearly the same size and 4x the maintenence costs.
Webfont Performance Notes
I like Open Sans, but I’m converting to the idea that webfonts need lazy-loaded, and possibly conditional based on viewport size. Mobile devices have decent enough super family sans-serifs pre-installed, so it might make sense to leverage them entirely instead of taking the hit for
I'm only using a small handful SymbolSet icons. Symbolset is also responsible for ~57% of my CSS selectors (391 of 697) . I'll likely abandon it and use an SVG icon set loaded explicitly-as-needed in an SVG sprite.
In Conclusion: Expect RWD to add ~10%
It appears that RWD has added 4.8kb (~2%) to my total page weight. That’s not much to make my site work across the multi-device web. I say “Expect RWD to add ~10%” so that you (and your boss) can plan and set a budget for a few more styling rules, some polyfilling, and some oversized images along the way. It might be more than 10% for you, but for most people it’s probably less than you imagined.
Now that the “RWD Causes Bloat” myth can hopefully be debunked, let’s take a look at what’s really causing my performance problems. Using PageSpeed Insights and WebPageTest, we start to get a better picture.
||Home|Article| |PageSpeed (Mobile)|79|78| |PageSpeed (Desktop)|93|91| |WPT Total Bytes|247 KB|483kb| |WPT Speed Index (First View)|1446|1749| |WPT Start Render|1.293s|1.491s| |WPT DOMContentLoaded|1.773s|1.830s| |WPT Fully Loaded|3.522s|11.378s|
Uf. 11 seconds to render an article page, which is probably my most visited type of template. For the Article template I used a post with code examples as my baseline. A Mobile PageSpeed of 78 is embarrassing, I’m definitely not “Breaking the Glass in 1000ms”. And ideally, I’d like to get my Speed Index under 1000.
If you dig in, you can see that most of my rendering and latency problems are being caused by classic modern day web design performance woes:
- Webfonts blocking site and type rendering (3 fonts)
- Time to first-byte is slow, has no critical CSS in it (~500ms)
- Sub-optimal image spriting (172 icon font)
- Unnecessary CSS rules (679 selectors)
- Static content caching (mostly third-party issues)
Now we have a list of performance improvements to be made. Nothing to do with RWD, I simply need to re-evaluate a few design and code decisions I’ve made, find workarounds, or nix features. I’ll be documenting that process going forward and hopefully will be back with another blog post and a faster website in a couple days.