An Event Apart: “Performance as User Experience”

Aaron Gustafson speaking at An Event Apart Seattle 2018 on April 3, 2018.

Design is problem solving. Each and every day, we are tasked with finding ways to reduce the friction our users experience on the Web. That means streamlining flows, reducing cognitive load, and writing more appropriate copy, but user experience goes far beyond the interface. Our users’ experiences begin with their first request to our servers. In this intensely practical session, Aaron will explore the ins and outs of page load performance by showing how he made the web site of the 10K Apart meet its own contest rules, by having a site that was functional and attractive even without JavaScript, and was less than ten kilobytes at initial load. You’ll walk away with a better understanding of the page load process as well as numerous ways you can improve the projects you are working on right now.


  • “I’ve been amazed at how often those outside the discipline of design assume that what designers do is decoration — likely because so much bad design simply is decoration. Good design isn’t. Good design is problem solving.” — Jeff Veen
  • Hallmarks of good UX:
    • Streamlined flow
    • Clear, concise copy
    • Low cognitive load
    • Fast performance
  • Poor performance is friction
    • A 1s delay in page load can reduce conversions by 7%
    • For an online shop earning $100k/day, that’s about $2.5m in lost sales — For Amazon, 1s is worth about $1.6b in sales
    • 53% abandon websites that take more than 3s to load
  • Performance matters
  • 10k Apart example — What we were looking for:
    • Usable pages are delivered in 10kB or less
    • Pages are accessible via screen readers, the keyboard, etc.
    • Entries follow the philosophy of progressive enhancement
    • Users can do what they need to without JavaScript
  • CSS and JavaScript can delay rendering or cause steps in page loading to run again
  • CSS and JavaScript in the <head> causes the browser to wait while the files are fetched and parsed
    • Same for images and JavaScript in the <body> as the browser encounters them
  • Steps for better performance
    1. Use native features whenever possible
    2. Only include assets you actually need
    3. Optimize everything
    4. Think about when you load assets
    5. Consider how you load assets
    6. Only load assets when they add value
  • Step 1: Use native features whenever possible
    • They’re effectively free
    • <header>
      • Shorter than <div id="header">, less HTML — semantic too
      • Depending on location, could also provide landmark for navigation
    • <input>
      • required — works in modern browsers to require fields
      • aria-required="true" — informs assistive tech
      • autocorrect="off" — don’t let the browser try to autocorrect names
      • autocapitalize="words", autocapitalize="off"
      • placeholder="Sir Tim Berners Lee" — auto-disappearing suggestion without JS
      • autocomplete="name" — if the browser knows the user’s name, let it fill it in
      • autocomplete="email" — let the browser suggest an e-mail address if it has one
      • type="email" — modern browsers validate e-mail addresses and may supply custom keyboard
    • display: flex; — so much better than floats for layout
    • var $radios = document.querySelectorAll('input[type=radio]'); — CSS selector-based DOM traversal without a JS library
    • Use system font stacks
      • If you do need a custom font, subset it as much as possible — but proceed with caution, make sure you have all the characters you will need
  • Step 2: Only include assets you actually need
    • Every tool has a cost (to the user)
    • Chances are, your library of choice is on a CDN — load from a CDN when possible
    • Find the server early
      • <link rel="dns-prefetch" href=""> — drop this in the <head>
    • Go for the handshake
      • <link rel="preconnect" href="">
    • Grab that resource
      • <link rel="preload" href="" as="script"> — helps optimize the process
    • Download isn’t everything — Vanilla JavaScript can do many more operations/second than the same functionality in popular JS libraries
  • Step 3: Optimize everything
    • Our approach to CSS (Gulp)
      1. Write modular CSS in Sass (+ Breakup for MQ management)
      2. Compile CSS with a precision of 4 decimal places (gulp-sass)
      3. Fallbacks for the last 2 browser versions (gulp-autoprefixer)
      4. CSS shorthand declarations if possible (gulp-shorthand)
      5. Remove any unused declarations/rule sets (gulp-uncss)
      6. Optimize the files (gulp-csso)
      7. Minify (gulp-clean-css)
      8. Gzip (gulp-zopfli)
      9. Brotli (gulp-brotli)
    • Our approach to JS (Gulp)
      1. Write modular JavaScript, grouped as appropriate
      2. Combine files based on folder structure (gulp-folders, gulp-concat)
      3. Create an wrapping closure to isolate from other JS (gulp-insert)
      4. Minify (gulp-uglify)
      5. Gzip (gulp-zopfli)
      6. Brotli (gulp-brotli)
    • We also minified & pre-compressed our HTML
  • Step 4: Think about when you load assets
    • <script src="/j/home.min.js" defer></script> — run after the DOM is loaded
    • <script src="/j/home.min.js" async></script> — run whenever it becomes available, but don’t delay page load
    • Consider dependencies
    • Avoid race conditions
    • HTTP/2 creates a single connection and contents stream in — much better for multiple files
  • Step 5: Consider how you load assets
    • Start simple
      • <link rel="stylesheet" href="/c/d.min.css"> — default styles for all browsers
      • <link rel="stylesheet" href="/c/a.min.css" media="only screen"> — advanced styles for modern browsers — browsers that don’t grok media queries ignore this file
      • Conditional scripting
        • <!--[if lt IE 9]><script src="/j/html5shiv.min.js"></script><![endif]--> delivers script to <= IE8
        • <!--[if gt IE 8]><!--><script src="/j/main.min.js"></script><script src="/j/home.min.js" async></script><!--<![endif]--> — hides scripts from <= IE8
      • Conditional images
        • @media (min-width: 36.375em) { .presented-by [href*=microsoft]::before { background-image: url(/i/c/edge.png); background-image: url(/i/c/edge.svg), none; }} — browsers that support multiple backgrounds also support SVG
      • Conditional animation
  • Step 6: Only load assets when they add value
    • Evaluate images case-by-case
      • Does the image reiterate information found in the surrounding text?
      • Is the image necessary to understand the surrounding content?
      • Does the image contain text?
      • Is the image a graph, chart, or table?
      • Could the content of the image be presented in a different format that would not require an image?
      • Is the image purely presentational?
    • Images make up 53% of the average web page size
    • Images don’t always fit
    • Not every article needs a picture
    • If you can avoid using an image, do it
    • If you need an image, choose the best format
    • Quick image format recap
      • GIF
        • For images with large swaths of solid colors
        • Binary transparency
      • JPG
        • For photographs and images with gradations of color
        • Can be compressed (introduces artifacts)
      • PNG (8-Bit)
        • Alternative to GIF
        • Can support alpha transparency (with the right creation software)
      • PNG (24-bit)
        • Alternative to JPG
        • Usually larger than JPGs Supports alpha transparency
      • WebP
        • Newer format, not universally supported
        • Smaller than JPGs and 24-bit PNGs
        • Support alpha transparency
        • and so much more…
    • Sometimes images are “nice to have”
    • Not every browser supports WebP
      • <picture><source type="image/svg+xml" srcset="my.svg"><source type="image/webp" srcset="my.webp"><img src="my.jpg" alt="Alt text"></picture>
  • Every choice we make affects our users’ experiences
  • Let’s spend our time to save it for our users
  • Speedy performance is a great user experience

Speaker Links and Resources

Related Links