CSS and the First Meaningful Paint - CSS Conf EU, May 2017

CSS and the First Meaningful Paint - CSS Conf EU, May 2017

To render a webpage browsers needs to go through the complex dance of networking, parsing and painting before any content can be displayed to your user. Over the years, we've developed mechanisms and hacks to aid the browser at each stage of this process, but these have always come at some cost or trade-off.

How can we utilize modern web platform features to load our CSS as fast as possible? Should we still be inlining our critical content into the document or instead, how can HTTP/2 server push and Service Workers help us?

In this talk we will take a journey exploring the current, past, and future best-practices for loading CSS in the browser and how we can achieve a first meaningful paint within 1000ms. Ultimately creating a faster, more resilient experience for our users.

276c149f793de9af4e98991ed52ff874?s=128

Patrick Hamann

May 06, 2017
Tweet

Transcript

  1. CSS and the: First meaningful paint CSS Conf EU, May

    2017 Patrick Hamann @patrickhamann
  2. None
  3. Why?

  4. CSS and the first meaningful paint @patrickhamann How do you

    measure your performance?
  5. CSS and the first meaningful paint • First byte •

    Document complete • Load event • Requests / bytes • Start render • SpeedIndex • First meaningful paint • Time to Interactivity • Custom… Old New
  6. CSS and the first meaningful paint @patrickhamann WTF is TTFMP

    ?
  7. CSS and the first meaningful paint @patrickhamann First Meaningful Paint

    is the time when a page’s primary content appeared on the screen.
  8. CSS and the first meaningful paint @patrickhamann First Meaningful Paint

    is the first paint after which the biggest above-the-fold layout change has happened, and web fonts have loaded.
  9. https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI # of layout objects Timeline

  10. CSS and the first meaningful paint @patrickhamann A bold statement

  11. CSS and the first meaningful paint @patrickhamann A bold statement

  12. https://developers.google.com/web/tools/lighthouse/

  13. Optimising for TTFMP A case-study: FT.com

  14. None
  15. https://www.webpagetest.org/ Test on real devices Real network conditions

  16. CSS and the first meaningful paint @patrickhamann • Where are

    your users based? • What is their device landscape? • In what context are they using your site? • What is their network profile? • What did they come for? What is your average user profile?
  17. CSS and the first meaningful paint @patrickhamann Forming a TTFMP

    baseline: 3G EM 3G Cable 3 secs 2 secs 1 secs Time to First Meaningful Paint: 3G EM 3G Cable ? ? ? Insert your custom profiles here:
  18. Baseline

  19. CSS and the first meaningful paint @patrickhamann 1 <!DOCTYPE html>

    2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>FT.com</title> 6 7 <link rel="stylesheet" href="main.css" /> 8 9 Other head elements... 10 11 </head> 12 <body> 13 14 Content ... 15 16 </body> 17 </html>
  20. CSS and the first meaningful paint @patrickhamann

  21. Name of Presentation Baseline results TTFMP (ms) 0 1000 2000

    3000 4000 5000 6000 7000 8000 9000 10000 Baseline 3G EM 3G Cable
  22. Inline critical CSS

  23. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM Render page GET css GET js response response idle idle Render blocking Run JS Async JS
  24. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM Render page GET css GET js response response idle idle Render blocking Run JS Async JS
  25. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM Render page GET css GET js response response idle idle Render blocking Run JS Async JS GET css response
  26. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM Render page GET css response idle Async CSS Render
  27. ç https://github.com/filamentgroup/loadCSS 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head>

    4 <meta charset="UTF-8"> 5 <title>FT.com</title> 6 7 <style> 8 html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background-color:tran 9 10 Critical styles ... 11 </style> 12 13 <link rel="preload" href="main.css" as="style" onload="this.rel='stylesheet'"> 14 <noscript><link rel="stylesheet" href="main.css"></noscript> 15 <script> 16 /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */ 17 (function(){ ... }()); 18 </script> 19 20 Other head elements... 21 22 </head> 23 <body> 24
  28. Before

  29. Before

  30. After

  31. After

  32. Name of Presentation Inline critical CSS results TTFMP (ms) 0

    1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 Baseline Inline 3G EM 3G Cable % Improvement 0 10 20 30 40 50 60 70 80 90 100 TTFMP % impovement 3G EM 3259 63 3G 1462 63 Cable 1327 46
  33. CSS and the first meaningful paint • No blocking resources

    • No SPOF on CSS • Eliminates critical request • Instant painting • Causes reflow • Not cachable • Hard to maintain • Hard to automate Pros Cons
  34. Preload

  35. CSS and the first meaningful paint @patrickhamann What are your

    critical resources?
  36. Logo? Fonts? Hero image?

  37. Lighthouse https://developers.google.com/web/tools/lighthouse/

  38. Before

  39. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM First paint GET css response idle idle Render blocking Render tree
  40. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM First paint GET css response idle idle Render blocking GET font response Text paint Text blocking Render tree
  41. CSS and the first meaningful paint @patrickhamann Request page Network

    Renderer GET html Build DOM response Build CSSOM First paint GET css response idle idle Render blocking GET font response Text paint Text blocking Render tree
  42. Preload, W3C working draft https://www.w3.org/TR/preload/

  43. CSS and the first meaningful paint @patrickhamann Provides a declarative

    fetch primitive that initiates an early fetch and separates fetching from resource execution.
  44. CSS and the first meaningful paint @patrickhamann 1 <!-- preload

    stylesheet resource via declarative markup --> 2 <link rel="preload" href="/styles/other.css" as="style"> 3 4 <!-- or, preload stylesheet resource via JavaScript --> 5 <script> 6 var res = document.createElement("link"); 7 res.rel = "preload"; 8 res.as = "style"; 9 res.href = "styles/other.css"; 10 document.head.appendChild(res); 11 </script> 1 Link: </styles/other.css> rel=“preload”; as=“style” nopush Preload with markup: Preload with HTTP header:
  45. CSS and the first meaningful paint @patrickhamann < GET /index.html


    
 < HTTP/1.1 200 OK < Cache-Control: private, max-age=60, must-revalidate < Expires: Thu, 20 Apr 2017 21:39:52 GMT < Last-Modified: Thu, 20 Apr 2017 13:11:33 GMT < ETag: "966eca3815d88d8848933d33c68ab2bd" < Content-Type: text/html < Content-Encoding: gzip < Content-Length: 43177 < Date: Thu, 20 Apr 2017 21:39:52 GMT < Connection: keep-alive < Link: <main.css>; as=style; rel=preload; nopush, 
 <MetricWeb-Regular.woff>; rel=preload as=font crossorigin nopush, <MetricWeb-Semibold.woff>; rel=preload as=font crossorigin nopush, <ftlogo.svg>; as=image; rel=preload; nopush
  46. Before

  47. After

  48. Name of Presentation Preload results TTFMP (ms) 0 1000 2000

    3000 4000 5000 6000 7000 8000 9000 10000 Baseline Inline Preload 3G EM 3G Cable % Improvement 0 10 20 30 40 50 60 70 80 90 100 TTFMP % impovement 3G EM 3176 64 3G 1778 55 Cable 1042 57
  49. CSS and the first meaningful paint • Indicate hidden resources

    • Dictate priority and order • Separates fetch from exec • Easy to create contention • Requires server logic Pros Cons
  50. Server push

  51. CSS and the first meaningful paint @patrickhamann HTTP/2

  52. CSS and the first meaningful paint @patrickhamann ! " Client

    Server GET /index.html GET /main.css 200 OK /index.html #
  53. CSS and the first meaningful paint @patrickhamann ! " Client

    Server PUSH_PROMISE /main.css 200 OK /index.html GET /index.html
  54. CSS and the first meaningful paint @patrickhamann 1 <!DOCTYPE html>

    2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>FT.com</title> 6 7 <link rel="stylesheet" href="critical.css" /> 8 <link rel="preload" href="main.css" as="style" onload=“this.rel='stylesheet'" /> 9 ... 10 1 Link: <critical.css>; rel=“preload”; as=“style” Indicate push of critical styles with Link preload header: Convert inline styles into normal link rel=“stylesheet” declaration and async the main styles
  55. Before index.html main.css Start render TTFMP Idle Idle

  56. TTFMP Start render After critical.css TTFMP index.html main.css Idle Start

    render Idle
  57. CSS and the first meaningful paint @patrickhamann /hero.jpg Stream ID:

    4 Weight: 16 /data.json Stream ID: 7 Weight: 16 /app.js Stream ID: 3 Weight: 64 /icon.svg Stream ID: 9 Weight: 16 /main.css Stream ID: 2 Weight: 110 /index.html Stream ID: 1 Weight: 128
  58. Result

  59. Name of Presentation Push results TTFMP (ms) 0 1000 2000

    3000 4000 5000 6000 7000 8000 9000 10000 Baseline Inline Preload Push 1256 1042 1327 2477 2826 1778 1462 3983 5035 3176 3259 8849 3G EM 3G Cable % Improvement 0 10 20 30 40 50 60 70 80 90 100 TTFMP % impovement 3G EM 5035 43 3G 2826 29 Cable 1256 49
  60. CSS and the first meaningful paint @patrickhamann Is indicating push

    via the HTML response too late?
  61. Async push

  62. TTFMP Start render After critical.css TTFMP index.html main.css Idle Start

    render Idle
  63. CSS and the first meaningful paint @patrickhamann ! " Client

    Server PUSH_PROMISE /main.css - # 200 OK /index.html GET /index.html ! GET /index.html 200 OK /index.html App
  64. CSS and the first meaningful paint @patrickhamann 1 const http2

    = require('http2'); 2 3 function handler(request, response) { 4 if (request.url === "/index.html") { 5 const push = response.push('/critical.css'); 6 push.writeHead(200); 7 fs.createReadStream('/critical.css').pipe(push); 8 } 9 10 // Generate index response: 11 // - Fetch data from DB 12 // - Render template 13 // etc ... 14 15 response.end(data); 16 } 17 18 const server = http2.createServer(opts, handler); 19 server.listen(80);
  65. After

  66. Name of Presentation Push async results TTFMP (ms) 0 1000

    2000 3000 4000 5000 6000 7000 8000 9000 10000 Baseline Inline Preload Push Push async 3G EM 3G Cable % Improvement 0 10 20 30 40 50 60 70 80 90 100 TTFMP % impovement 3G EM 3062 65 3G 1613 59 Cable 1021 58
  67. CSS and the first meaningful paint • Uses idle time

    • Ensures delivery before preload • Easy to create contention • Limited avaliablity • Custom server logic • Hard to debug Pros Cons
  68. CSS and the first meaningful paint @patrickhamann What about the

    repeat view?
  69. CSS and the first meaningful paint @patrickhamann PRPL

  70. Result

  71. None
  72. The future

  73. 103 Early hints status code for HTTP, K. Oku https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-01

  74. Cache digests for HTTP/2, K. Oku https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest-01

  75. Conclusion

  76. CSS and the first meaningful paint @patrickhamann Resource loading is

    hard.
  77. CSS and the first meaningful paint @patrickhamann Bandwidth is often

    
 under-utilised
  78. CSS and the first meaningful paint @patrickhamann Identify your critical

    resources and request chains.
  79. CSS and the first meaningful paint @patrickhamann Use preload to

    indicate critical resources, such as fonts to the browser.
  80. CSS and the first meaningful paint @patrickhamann Push critical CSS,

    but only on first view and only within idle time.
  81. CSS and the first meaningful paint @patrickhamann Always be testing.

  82. Thanks! Patrick Hamann patrick@fastly.com @patrickhamann