$30 off During Our Annual Pro Sale. View Details »

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
Tweet

More Decks by Addy Osmani

Other Decks in Programming

Transcript

  1. @addyosmani
    +AddyOsmani
    CSS
    PERFORMANCE
    TOOLING

    View Slide

  2. When you want to be fast, you have to
    give up the things slowing you down.

    View Slide

  3. Optimising
    Performance.

    View Slide

  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.

    View Slide

  5. Waiting for my page to render.
    Why care about optimization tooling?

    View Slide

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

    View Slide

  7. Case Study
    Assume cable speeds used for testing unless otherwise noted.

    View Slide

  8. Optimizing JSConf.eu

    View Slide

  9. JSConf.eu
    Friday
    3G

    View Slide

  10. JSConf.eu
    Friday
    Cable

    View Slide

  11. JSConf.eu
    Monday
    Cable

    View Slide

  12. Make JSConf.eu an instant experience.

    View Slide

  13. Make your site an instant experience.

    View Slide

  14. Step 1: Measure

    View Slide

  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

    View Slide

  16. Lower score is always better
    SI Visualized

    View Slide

  17. ~1s page load
    Max 200ms
    server
    response time
    Speed Index
    under 1000
    Performance
    Budget

    View Slide

  18. View Slide

  19. Monday
    Mobile 3G (Fast)
    Cable

    View Slide

  20. View Slide

  21. Friday
    Ideally
    85 Mobile
    90 Desktop

    View Slide

  22. Monday
    Regression

    View Slide

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

    View Slide

  24. View Slide

  25. View Slide

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

    View Slide

  27. View Slide

  28. Profiling Tools Are Now Everywhere!

    View Slide

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

    View Slide

  30. soup.io
    The web

    View Slide

  31. HTTPArchive - August 2014
    Average page size
    1.8MB
    ~1.2MB images
    ~0.3MB scripts &
    styles

    View Slide

  32. http://perf.fail/
    Perf.fail

    View Slide

  33. Step 2: Optimise

    View Slide

  34. It’s basically Tinder for Node modules

    View Slide

  35. View Slide

  36. Party like it’s 1977.

    View Slide

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

    View Slide

  38. Minify CSS, HTML, JS & Images

    View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. Minify duplicates, colors, keyframes, pseudos, filters, font-weight

    View Slide

  43. View Slide

  44. View Slide

  45. CSS, JS, HTML, Image minification New Speed Index
    1321
    Old Speed Index
    2093

    View Slide

  46. Remove unused CSS

    View Slide

  47. View Slide

  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?

    View Slide

  49. Isn’t using 56% of the site wide
    styles being shipped
    Page A

    View Slide

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

    View Slide

  51. Isn’t using ~59%
    Page C

    View Slide

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

    View Slide

  53. View Slide

  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

    View Slide

  55. View Slide

  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

    View Slide

  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

    View Slide

  58. UnCSS as a post-processor
    HTML,
    JavaScript
    Sass
    Minified HTML
    CSS
    UnCSS Minify Done
    Source Minified JavaScript
    Works with Sass, Less, Stylus

    View Slide

  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

    View Slide

  60. View Slide

  61. Sizing up JSConf.eu styles
    MINIFIED
    ORIGINAL STYLES UNCSS + MINIFICATION
    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

    View Slide

  62. Unused CSS removed Speed Index
    1316
    Old Speed Index
    2093

    View Slide

  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.

    View Slide

  64. CSS Regression Testing
    Useful for more than just catching imperfect updates

    View Slide

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

    View Slide

  66. View Slide

  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

    View Slide

  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

    View Slide

  69. Verifies visual optimizations
    didn’t mess up.

    View Slide

  70. 0.06% difference from the original

    View Slide

  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

    View Slide

  72. Evaluate your options and choose the best tool for your use-case
    ● Huxley
    ● Wraith
    ● Needle
    ● CSSCritic
    ● dpxdt
    Many alternatives

    View Slide

  73. Optimize Critical Path CSS

    View Slide

  74. Our goal is to show content asap

    View Slide

  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!

    View Slide

  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

    View Slide

  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
    4. Asynchronously load the rest of your styles
    ○ Remove them from the critical path

    View Slide

  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

    View Slide

  79. Extract
    stylesheets
    (Oust)
    Generate Critical
    path CSS for a
    viewport
    (Penthouse)
    Inline styles in

    (inline-critical) Output
    Source
    Critical’s workflow
    Inject loadCSS &
    async load site.
    css
    Wrap link tags in
    for
    users with JS
    disabled

    View Slide

  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

    View Slide

  81. View Slide

  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

    View Slide

  83. Critical-path CSS optimized version New Speed Index
    1225
    Old Speed Index
    2093

    View Slide

  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

    View Slide

  85. View Slide

  86. Bottleneck: that 5MB GIF...

    View Slide

  87. View Slide

  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
    :(

    View Slide

  89. GIFBrewery
    ~1MB half-res, 256 colors
    ~4MB full-res, 256 colors

    View Slide

  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.

    View Slide


  91. ~450KB

    View Slide

  92. View Slide

  93. Minify + UnCSS + New Speed Index
    1327
    Old Speed Index
    2093

    View Slide

  94. static?

    View Slide

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

    View Slide

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

    View Slide

  97. What’s important to you?

    View Slide

  98. Automating Tier 1 + Tier 2

    View Slide

  99. A magic bullet?
    No,
    Timmy

    View Slide

  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

    View Slide

  101. Tuesday: JSConf.eu
    New images
    Old Speed Index
    2149

    View Slide

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

    View Slide

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

    View Slide

  104. Automate performance measurement
    Detect when you’ve fallen off the fast-path

    View Slide

  105. WebPageTest CLI
    $ npm install -g webpagetest
    Install
    $ webpagetest test
    $ webpagetest test -- wait
    8000
    Usage Jenkins

    View Slide

  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

    View Slide

  107. PageSpeed
    Insights CLI
    $ npm install -g psi
    Install
    $ psi --
    strategy=mobile
    Usage

    View Slide

  108. Grunt-PageSpeed
    $ npm install grunt-pagespeed
    $ grunt pagespeed
    Usage
    Install
    Set performance thresholds for mobile/desktop!

    View Slide

  109. Phantomas CLI
    $ npm install -g phantomas
    Install
    $ phantomas --url http://jsconf.eu
    $ phantomas --url http://jsconf.eu --assert-requests=10
    Usage

    View Slide

  110. Grunt-Phantomas
    $ npm install grunt-phantomas
    Install
    $ grunt phantomas:default
    $ grunt phantomas:screenshot
    Usage

    View Slide

  111. Tier 3: Stylesheet Complexity
    Analysis

    View Slide

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

    View Slide

  113. View Slide

  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

    View Slide

  115. View Slide

  116. http://www.stylestats.org

    View Slide

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

    View Slide

  118. about:blank is the baseline to beat

    View Slide

  119. View Slide

  120. Stay light, stay fast.

    View Slide

  121. 1.1 seconds
    12.1 seconds
    ~ 2100
    1321
    1316
    1225
    1327
    923
    917
    Speed Index Scores

    View Slide

  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

    View Slide

  123. https://www.flickr.com/photos/keijoknutas/8850877937

    View Slide

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

    View Slide

  125. CSSConf LGTM’d!

    View Slide

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

    View Slide

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

    View Slide

  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.”

    View Slide

  129. Getting Your PageSpeed Score Up
    https://www.youtube.com/watch?v=pNKnhBIVj4w

    View Slide

  130. #perfmatters

    +AddyOsmani
    @addyosmani

    View Slide