Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CSS Performance Tooling

96270e4c3e5e9806cf7245475c00b275?s=47 Addy Osmani
September 13, 2014

CSS Performance Tooling

Presented at CSSConf.eu, September 2014.

Video: https://www.youtube.com/watch?v=FEs2jgZBaQA

You've learned the concepts, now meet the tools. High-performance sites need to feel instant and deliver the goods in < 1000ms. In this talk, learn about the bleeding edge tools that can automate keeping your CSS on the fast path. Automate generating critical-path CSS, removing unused CSS, discovering duplicate selectors, colors and more.

Along the way, we might even optimize a well known conference site and send them a pull request to help keep the web fast ;) The tools we'll discuss can be used with Grunt, Gulp, Make or horse so strap yourself in and get ready for a ride into the crazier side of CSS performance optimization.


Addy Osmani

September 13, 2014


  1. @addyosmani +AddyOsmani CSS PERFORMANCE TOOLING

  2. When you want to be fast, you have to give

    up the things slowing you down.
  3. Optimising Performance.

  4. 3 Tiers Of Optimization Tooling 1. Baseline ◦ Minification, Concatenation,

    Image optimization, Compression (GZip, Zopfli), Async scripts, Leverage caching, WOFF2 for fonts, Spriting. Avoid redirects. 2. Get fast, stay fast. ◦ Inlining critical CSS, deferring non-critical assets to avoid render blocking, removing unused CSS, visual regression testing to verify changes. Performance benchmarking. 3. Nice to haves ◦ Reduce duplicate colors, selectors, font-families, sizes.
  5. Waiting for my page to render. Why care about optimization

  6. 300-1000ms 1 second + 10 seconds + Later. 0-100ms Human

    Perception in 2014
  7. Case Study Assume cable speeds used for testing unless otherwise

  8. Optimizing JSConf.eu

  9. JSConf.eu Friday 3G

  10. JSConf.eu Friday Cable

  11. JSConf.eu Monday Cable

  12. Make JSConf.eu an instant experience.

  13. Make your site an instant experience.

  14. Step 1: Measure

  15. https://sites.google.com/a/webpagetest.org/docs/using- webpagetest/metrics/speed-index The average time at which visible parts of

    the page are displayed. Expressed in milliseconds and dependent on size of the viewport. Speed Index
  16. Lower score is always better SI Visualized

  17. ~1s page load Max 200ms server response time Speed Index

    under 1000 Performance Budget
  18. None
  19. Monday Mobile 3G (Fast) Cable

  20. None
  21. Friday Ideally 85 Mobile 90 Desktop

  22. Monday Regression

  23. https://www.flickr.com/photos/tamakisono/668326457

  24. None
  25. None
  26. https://www.flickr.com/photos/mipsyretro/5593568015 #ILoveRoamingCharges

  27. None
  28. Profiling Tools Are Now Everywhere!

  29. Accidental slowness is a problem many of us face.

  30. soup.io The web

  31. HTTPArchive - August 2014 Average page size 1.8MB ~1.2MB images

    ~0.3MB scripts & styles
  32. http://perf.fail/ Perf.fail

  33. Step 2: Optimise

  34. It’s basically Tinder for Node modules

  35. None
  36. Party like it’s 1977.

  37. Build Tools Make Had no logo so it gets a

  38. Minify CSS, HTML, JS & Images

  39. None
  40. None
  41. None
  42. Minify duplicates, colors, keyframes, pseudos, filters, font-weight

  43. None
  44. None
  45. CSS, JS, HTML, Image minification New Speed Index 1321 Old

    Speed Index 2093
  46. Remove unused CSS

  47. None
  48. HTML CSS DOM CSSOM Render Tree Layout Paint Network Deliver

    only the goods that will be used Hello • Wrong way to think about unused CSS ◦ “I just want to ship the CSS for current page” ◦ “I’m going to ignore styles used in other pages” • Right way ◦ What site-wide CSS am I shipping that is not used by any pages?
  49. Isn’t using 56% of the site wide styles being shipped

    Page A
  50. Isn’t using ~56% of them Page B

  51. Isn’t using ~59% Page C

  52. Pattern? Maybe. Optimize, then check mileage.

  53. None
  54. Removing unused CSS Load files and execute JavaScript Extract Stylesheets

    from HTML Filter out selectors not found in HTML PhantomJS css-parse Stylesheets concatenated & parsed Remaining rules converted back to CSS
  55. None
  56. uncss: { dist: { options: { ignore : ['#added_at_runtime', /test\-[0-9]+/],

    media : ['(min-width: 700px) handheld and (orientation: landscape)'], csspath : '../public/css/', raw : 'h1 { color: green }', stylesheets : ['lib/bootstrap/dist/css/bootstrap.css', 'src/public/css/main.css'], ignoreSheets : [/fonts.googleapis/], urls : ['http://localhost:3000/mypage', '...'], // Deprecated timeout : 1000, htmlroot : 'public', report : 'min' }, files: { 'dist/css/tidy.css': ['app/index.html', 'app/about.html'] } } } Gruntfile.js
  57. uncss: { dist: { files: { '_site/stylesheets/screen.css': [ '_site/index.html', '_site/sponsors.html',

    'site/_news.html', '_site/schedule.html', '_site/speakers/index.html', ... JSConf Gruntfile.js Include ALL site URLs
  58. UnCSS as a post-processor HTML, JavaScript Sass Minified HTML CSS

    UnCSS Minify Done Source Minified JavaScript Works with Sass, Less, Stylus
  59. Sizing up the average Bootstrap page MINIFIED ORIGINAL STYLES UNCSS

    + MINIFICATION 120KB 110KB 11KB (1) ~ 90% improvement (2) Based on average improvements reported by UnCSS users
  60. None

    20KB 11KB 7KB (1) > 50% improvement on minification. Worth it? Questionable. (2) UnCSS is most useful when using a CSS library or have a large site
  62. Unused CSS removed Speed Index 1316 Old Speed Index 2093

  63. Warnings We cannot identify with 100% certainty all the CSS

    that you still need for your site, since it's likely to be missing some CSS that you will actually need in some cases. Dynamically injected styles are supported, but have many edge-cases where they may lead to inaccurate removals of CSS. When removing unused CSS, don’t guess it. Test it.
  64. CSS Regression Testing Useful for more than just catching imperfect

  65. Build-time styles & UI verification Before After Diff

  66. None
  67. Why care about it? It’s useful Regressions in your UI

    can be hard to identify Cost of manual testing gets higher with responsive layouts Can drive tests with fake data if needed Solution Visual regression tooling + Resemble.js
  68. npm install phantomcss Install grunt.initConfig({ 'phantomcss-gitdiff': { options: { baseUrl:

    'http://localhost:3000/', serverRoot: 'test/files/' }, mobile: { options: { screenshots: 'screens/mobile/', results: 'failures/mobile/', viewportSize: [320, 480] }, src: ['test/files/{,**/}*.html'] ... Gruntfile.js PhantomCSS PhantomJS - scriptable headless browser CasperJS - navigation scripting & caching PhantomCSS - screenshot comparison library npm install grunt-phantomcss-gitdiff Grunt task
  69. Verifies visual optimizations didn’t mess up.

  70. 0.06% difference from the original

  71. Warnings Imperfect solution Not as useful on pages with mutable

    content that changes regularly More difficult to use on complex applications Your mileage may vary
  72. Evaluate your options and choose the best tool for your

    use-case • Huxley • Wraith • Needle • CSSCritic • dpxdt Many alternatives
  73. Optimize Critical Path CSS

  74. Our goal is to show content asap

  75. Just render visible content! 1. One RTT render for above

    the fold 2. Fast server response. No redirects 3. Must optimize critical rendering path a. Inline critical CSS b. Remove any blocking JavaScript Don’t render the whole page!
  76. // for each selector in my site.css var elements =

    document.querySelectorAll('.someClass'); // for each element on the page matched for (var i=0; i < elements.length; i++) { // if "above the fold" if (elements[i].getBoundingClientRect().top > window.innerHeight) { // Keep the CSS rules that use this selector keep = true; } } Scripting alone is not good enough https://gist.github.com/PaulKinlan/6284142 Harder to handle.. Special characters - content: '\2192' etc. @-Rules - @font-face, @media, @keyframe pseudo-selectors - :before, :hover
  77. Multi-part problem to automate. What do we really need to

    do? 1. Extract stylesheets from HTML ◦ Choice of extraction or defining *.css files upfront 2. Generate the above the fold CSS ◦ Decide on target viewports. Multiple? One sweet spot? ◦ Keep it small and lightweight: under 14 KB 3. Inline critical-path CSS in <head> 4. Asynchronously load the rest of your styles ◦ Remove them from the critical path
  78. New Critical-path CSS tools which builds on Penthouse <333 $

    npm install --save-dev penthouse CriticalCSS by FilamentGroup is also solid $ npm install --save-dev criticalcss Critical $ npm install --save-dev critical
  79. Extract stylesheets (Oust) Generate Critical path CSS for a viewport

    (Penthouse) Inline styles in <head> (inline-critical) Output Source Critical’s workflow Inject loadCSS & async load site. css Wrap link tags in <noscript> for users with JS disabled
  80. function loadCSS( href, before, media ){ var ss = window.document.createElement(

    "link" ); var ref = before || window.document.getElementsByTagName( "script" )[ 0 ]; ss.rel = "stylesheet"; ss.href = href; ss.media = "only x"; // inject link ref.parentNode.insertBefore( ss, ref ); setTimeout( function(){ ss.media = media || "all"; } ); return ss; } Set media back to all so the stylesheet applies once it loads Async load CSS with loadCSS.js Temporarily set media to something non- matching. Fetches without blocking render. loadCSS(‘site.css’) http://filamentgroup.com/lab/performance-rwd.html
  81. None
  82. critical: { dist: { options: { base: './', css: [

    'app/styles/main.css', 'app/styles/bootstrap.css' ], width: 320, height: 70 }, src: 'app/index.html', dest: 'app/styles/critical.css' } } Specify stylesheets or use Oust to extract them grunt-critical Specify viewport
  83. Critical-path CSS optimized version New Speed Index 1225 Old Speed

    Index 2093
  84. Challenges Background images or Fonts missing Relative paths may need

    updating to absolute Unstyled content showing The most common problem is with clearing floats. Use the clear-fix-pattern. Special glyphs not showing/showing incorrectly When using hexadecimal format in CSS it needs to be prepended with a backslash, like so: \2192
  85. None
  86. Bottleneck: that 5MB GIF...

  87. None
  88. (1) Optimizations made with best tools available ▪ PNGOUT, Zopfli,

    Pngcrush, AdvPNG, extended OptiPNG, ▪ JpegOptim, jpegrescan, jpegtran, and Gifsicle. (2) Animated GIFs are HARD to automate optimizations to ImageOptim / Imagemin :(
  89. GIFBrewery ~1MB half-res, 256 colors ~4MB full-res, 256 colors

  90. (1) Run OSX screencapture every 0.1 seconds ▪ Get a

    folder of images (PNGs) (2) Run `python anim_encoder.py folderName` over the output (3) Single packed PNG with diffs between frames. JS for turning PNG + JSON into animation Packed Image Diffs https://github.com/benvanik/anim_encoder with thanks to @umaar for introducing me to the above workflow (1) Works best for UI where only small areas change between frames (2) ~600KB for our animation. Introduces more render-blocking JS.
  91. <video> ~450KB

  92. None
  93. Minify + UnCSS + <video> New Speed Index 1327 Old

    Speed Index 2093
  94. static?

  95. Minify + UnCSS + static header Speed Index 923 Old

    Speed Index 2093
  96. grunt-critical + optimisations + static header Speed Index 917 Old

    Speed Index 2093
  97. What’s important to you?

  98. Automating Tier 1 + Tier 2

  99. A magic bullet? No, Timmy

  100. (1) Client renders page, custom JS beacons back ATF CSS

    styles ▪ PageSpeed gathers critical CSS beacons from visitors (2) Critical CSS is inlined... ▪ Remaining CSS loaded after first paint + https://developers.google.com/speed/pagespeed/module/filter-prioritize-critical-css ModPagespeedEnableFilters prioritize_critical_css # Apache pagespeed EnableFilters prioritize_critical_css # Nginx • modpagespeed.com • ngxpagespeed.com
  101. Tuesday: JSConf.eu New images Old Speed Index 2149

  102. Optimized: JSConf.eu + mod_pagespeed Old Speed Index 2149 New Speed

    Index 1310
  103. Mileage does vary (a few changes later..) Old Speed Index

    1310 New Speed Index 1542
  104. Automate performance measurement Detect when you’ve fallen off the fast-path

  105. WebPageTest CLI $ npm install -g webpagetest Install $ webpagetest

    test <url> $ webpagetest test <url> -- wait 8000 Usage Jenkins
  106. Grunt-PerfBudget http://cognition.happycog.com/article/grunt-plugins-reviewed $ npm install grunt-perfbudget Install 1 2 3

    Budget in ms for render, KB for page weight Over budget? Task fails and reports an error. GitHub PRs will show the build failed
  107. PageSpeed Insights CLI $ npm install -g psi Install $

    psi <url> -- strategy=mobile Usage
  108. Grunt-PageSpeed $ npm install grunt-pagespeed $ grunt pagespeed Usage Install

    Set performance thresholds for mobile/desktop!
  109. Phantomas CLI $ npm install -g phantomas Install $ phantomas

    --url http://jsconf.eu $ phantomas --url http://jsconf.eu --assert-requests=10 Usage
  110. Grunt-Phantomas $ npm install grunt-phantomas Install $ grunt phantomas:default $

    grunt phantomas:screenshot Usage
  111. Tier 3: Stylesheet Complexity Analysis

  112. The easiest way to keep CSS maintainable is to minimize

  113. None
  114. npm install -g colorguard Install $ colorguard --file styles.css Collision:

    #020202, #000000 - #020202 [line: 2] is too close (0.3146196209793196) to #000000 [line: 2, 3, 7, 12, 13, 16, 17] Collision: #020202, #010101 - #020202 [line: 2] is too close (0.1574963682909058) to #010101 [line: 20] Collision: #000000, #010101 - #000000 [line: 2, 3, 7, 12, 13, 16, 17] is too close (0.15712369811016996) to #010101 [line: 20] Example CSS Colorguard
  115. None
  116. http://www.stylestats.org

  117. http://www.wallarc.com/wallpaper/view/370816 Step 3: Rinse & Repeat

  118. about:blank is the baseline to beat

  119. None
  120. Stay light, stay fast.

  121. 1.1 seconds 12.1 seconds ~ 2100 1321 1316 1225 1327

    923 917 Speed Index Scores
  122. #perfmatters 1. Don’t guess it, test it. ◦ Performance advice

    is addictive. Always measure. 2. Measure, optimize & repeat ◦ Validate your optimizations. Make sure output is ~1:1 3. Set a performance budget ◦ Set goals. Automate performance measurement. 4. Focus on what matters to your users
  123. https://www.flickr.com/photos/keijoknutas/8850877937

  124. We have one last thing to do. A pull request.

  125. CSSConf LGTM’d!

  126. Let’s Perf The Web Forward Together <3

  127. High-Performance Browser Networking Udacity Perf Optimization https://www.udacity.com/course/ud884

  128. Improving SmashingMag performance http://www.smashingmagazine.com/2014/09/08/improving-smashing-magazine-performance-case-study/ “Strategically speaking, the following could be

    your performance optimization roadmap: • Remove blocking scripts from the header of the page. • Identify and defer non-critical CSS and JavaScript. • Identify critical CSS and load it inline in the head, and then load the full CSS after rendering. • Keep all critical HTML and CSS to under 14 KB, and aim for a Speed Index of under 1000. • Consider using WOFF2 to further reduce latency and file size of the web fonts. • Replace JavaScript libraries with leaner JavaScript modules.”
  129. Getting Your PageSpeed Score Up https://www.youtube.com/watch?v=pNKnhBIVj4w

  130. #perfmatters <thank-you> +AddyOsmani @addyosmani