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

CSS Performance Tooling

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

More Decks by Addy Osmani

Other Decks in Programming


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

    up the things slowing you down.
  2. 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.
  3. 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
  4. 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?
  5. 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
  6. 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
  7. 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
  8. UnCSS as a post-processor HTML, JavaScript Sass Minified HTML CSS

    UnCSS Minify Done Source Minified JavaScript Works with Sass, Less, Stylus
  9. 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

    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
  11. 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.
  12. 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
  13. 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
  14. 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
  15. Evaluate your options and choose the best tool for your

    use-case • Huxley • Wraith • Needle • CSSCritic • dpxdt Many alternatives
  16. 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!
  17. // 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. (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 :(
  25. (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.
  26. (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
  27. WebPageTest CLI $ npm install -g webpagetest Install $ webpagetest

    test <url> $ webpagetest test <url> -- wait 8000 Usage Jenkins
  28. 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
  29. PageSpeed Insights CLI $ npm install -g psi Install $

    psi <url> -- strategy=mobile Usage
  30. Phantomas CLI $ npm install -g phantomas Install $ phantomas

    --url http://jsconf.eu $ phantomas --url http://jsconf.eu --assert-requests=10 Usage
  31. 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
  32. #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
  33. 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.”