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

Critical CSS for CMS-based Server-rendered Websites

Critical CSS for CMS-based Server-rendered Websites

A super fast website often involves inlining your critical CSS while asynchronously loading the non-critical CSS. You understand what that means, but you’ve got a CMS-based website with many templates and hundreds or more (server rendered) pages but no simple switch or plugin to “Turn on Critical CSS”. This talk will show you how to marry Critical CSS with a site like yours to produce webpages that load quickly and get your green scores in your performance monitors.

We look at an end-to-end solution that we have evolved and battle tested over the years at Miranj. I cover identifying target templates, leveraging the Critical library to extract critical CSS, automating the extraction process using Gulp, reducing response size for repeat visitors, and getting this entire system to work with the caching layer(s) that you may already be using.

5d201d801f5a6e5a54c1178a20eb913d?s=128

Prateek Rungta

September 24, 2021
Tweet

Transcript

  1. Critical CSS for CMS-based, Server-rendered Websites

  2. CMS Browser Server /home

  3. CMS Browser Server /hom e <html>
 <head > <body >

    <header > .. . <section > <section > .. . <footer>
 </html>
  4. Bolt 
 Kirby 
 Ghost 
 Drupal 
 Joomla 


    Magento 
 Cra ft CMS 
 WordPress 
 Expression Engine
  5. Recap: Critical CSS

  6. How fast does the page load? ⚡

  7. 1 large CSS file with rules for entire page/site ⏳

  8. Only a part of the page is visible 🪟

  9. Fetch CSS for visible portion of the page only 🧐

  10. Fetch CSS for visible portion of the page only 🧐

    a.k.a Critical CSS
  11. Bundle CSS for visible portion of the page with HTML

    📦
  12. Async-load rest of the CSS 🚶

  13. Improve “perceived” load speed 🪄⏱

  14. First Contentful Paint (FCP) ⏱

  15. “The time from when the page starts loading to when

    any part of the page’s content is rendered on the screen.” web.dev/fcp
  16. Credit: web.dev/fcp Time

  17. 1.8 seconds or less 🚀

  18. Critical CSS directly impacts FCP

  19. Largest Contentful Paint (LCP) ⏱

  20. “The render time of the largest image or text block

    visible within the viewport, relative to when the page first started loading.” web.dev/lcp
  21. Credit: web.dev/lcp Time

  22. Credit: web.dev/lcp Time

  23. 2.5 seconds or less 🚀

  24. Critical CSS indirectly impacts LCP

  25. Apply this to CMS-based sites

  26. 1. Content is dynamic. 
 What is above the fold?

  27. 2. Extracted CSS will go out of sync as website

    styles change
  28. 3. Can subsequent pages avoid bundling extra CSS with HTML?

  29. 4. How does this work with server side page caching?

  30. 1. Page Selection

  31. We might have 1000s of active pages on any site

  32. None
  33. But these 1000s of pages are powered by handful of

    templates
  34. Article Category Author

  35. Extract Critical CSS per template, not for every single page

  36. Pick one average case sample per unique template

  37. Article Category Author

  38. Extract CSS for that page & re-use for all instances

    of that template
  39. article.critical.css, 
 index.critical.css, 
 category.critical.css, 
 etc.

  40. 2. CSS Extraction

  41. 1. Render full page (HTML, CSS, assets etc.) 2. Define

    viewport width & height 3. Filter CSS selectors used in the viewport
  42. Web-based Command line Build tools

  43. We have to re-extract every time any HTML or CSS

    changes
  44. Automate!

  45. Use a build tool such as Gulp.js

  46. 1. Get a fully rendered sample page (per template) 2.

    Feed this HTML & CSS to the tool 3. Save generated Critical CSS (per template) 4. Watch for changes in template or CSS files, repeat steps 1-3.
  47. 1. One sample page per template const samples = {


    article: 'http://site.local/article-2',
 page: 'http://site.local/about',
 index: 'http://site.local/page/2',
 category: 'http://site.local/categories/perf',
 }
  48. 2. Extract Critical CSS using `npm i critical` function extract(src,

    destination) {
 return critical.generate({
 src,
 css: ['dist/styles.css'],
 target: `dist/${destination}.critical.css`,
 minify: true,
 dimensions: [
 { width: 400, height: 900 },
 { width: 1300, height: 900 },
 ],
 })
 }
  49. 3. Run the extraction for all templates Object.entries(samples)
 .forEach(([template, sample])

    => {
 exports[`critical:${template}`] = () => extract(sample, template)
 })
 
 exports.critical = gulp.parallel(
 ...Object.keys(samples)
 .map(template => `critical:${template}`)
 )
  50. 4. Watch for changes in template or CSS files, repeat

    exports.default = () => {
 gulp.watch('dist/styles.css', 'critical')
 gulp.watch('templates/**/*.{html,twig}', 'critical')
 }
  51. 3. Delivery

  52. CSS is typically included via one (or more) external files

  53. <html > <head > <link rel=stylesheet href=styles.css> .. . 🚫

    Blocks rendering
  54. <html > <head > <link rel=stylesheet href=styles.css> .. .

  55. <html > <head > <style>
 /* Critical CSS */
 </style>


    <link rel=stylesheet href=styles.css > ... 📦 Bundle with HTML
  56. <html > <head > <style > /* Critical CSS *

    / </style > 
 <link rel=stylesheet href=styles.css
 media=print
 onload="this.media='all'; this.onload=null;">
 ... 🚶 Async load full CSS
  57. This is great for first-time visitors. ⚡

  58. What about subsequent or 
 repeat visits?

  59. The browser already has the full CSS from the first

    visit.
  60. <html> <!-- Repeat Visit -- > <head > <link rel=stylesheet

    href=styles.css> .. . ✅ Cached locally
  61. <html> <!-- Repeat Visit -- > <head > <style >

    /* Critical CSS * / </style > <link rel=stylesheet href=styles.css
 media=print
 onload="this.media='all'; this.onload=null;">
 ... 📦 Avoid extra baggage
  62. Di ff erent responses for 
 first time visits &

    repeat visits
  63. <!-- First Visit -->
 
 <html > <head > <style>


    /* Critical CSS */
 </style>
 
 <link rel=styleshee t href=styles.css media=print onload="this.media='all'"> ... <!-- Repeat Visit -->
 
 <html > <head > 
 <link rel=styleshee t href=styles.css>
 
 ...
  64. How do we figure out if a request is a

    first or repeat visit? 🤔
  65. Set a cookie during the first visit 🍪

  66. Does the request contain 🍪? 
 ✅ has cookie =

    repeat visit ❌ no cookie = first visit
  67. <!-- First Visit -->
 
 <html > <head > <style>


    /* Critical CSS */
 </style>
 
 <link rel=styleshee t href=styles.css media=print onload="this.media='all'"> ... <!-- Repeat Visit -->
 
 <html > <head > 
 <link rel=styleshee t href=styles.css>
 
 ...
  68. <!-- ❌ No Cookie -->
 
 <html > <head >

    <style>
 /* Critical CSS */
 </style>
 
 <link rel=styleshee t href=styles.css media=print onload="this.media='all'"> ... <!-- ✅ Has Cookie 🍪 -->
 
 <html > <head > 
 <link rel=styleshee t href=styles.css>
 
 ...
  69. <!-- ❌ No Cookie -->
 
 <html > <head >

    <style>
 /* Critical CSS */
 </style>
 
 <link rel=styleshee t href=styles.css media=print onload="this.media='all'"> ... <!-- ✅ Has Cookie 🍪 -->
 
 <html > <head > 
 <link rel=styleshee t href=styles.css>
 
 ... Set-Cookie:
 repeat_visit=1
  70. <!-- ❌ No Cookie -->
 
 <html > <head >

    <style>
 /* Critical CSS */
 </style>
 
 <link rel=styleshee t href=styles.css media=print onload="this.media='all'"> ... <!-- ✅ Has Cookie 🍪 -->
 
 <html > <head > 
 <link rel=styleshee t href=styles.<hash>.css>
 
 ... Set-Cookie:
 repeat_visit=<hash>
  71. Credit: instagram.com/dudewithsign

  72. “Technical cookie”, no personally identifiable information, 
 no cookie banner

    🧑💻🔒
  73. 4. Caching

  74. CMS Browser /home

  75. CMS Browser ⏳ Takes some time

  76. CMS Browser /home <html>

  77. CMS Browser Cache /home

  78. CMS Browser Cache /home <html>...</html>

  79. CMS Browser Cache /home <html>...</html> /about <html>...</html>

  80. CMS Browser Cache /home <html>...</html> /about <html>...</html> /blog <html>...</html> /article-1

    <html>...</html> /article-2 <html>...</html> /article-3 <html>...</html>
  81. CMS Browser Cache /home

  82. CMS Browser Cache /home 🍪 <html>...</html>

  83. CMS Browser Cache /home 🍪 <html>...</html> - <html>...</html>

  84. CMS Browser Cache /home 🍪 <html>...</html> - <html>...</html> /about 🍪

    <html>...</html> - <html>...</html> /blog 🍪 <html>...</html> - <html>...</html>
  85. We now cache two versions for each URL 🍪

  86. Recap

  87. • Pick one representative sample per template • Automate Critical

    CSS generation via a build tool • Inline critical CSS (only) for first time visitors • Set a cookie to di ff erentiate first vs repeat visits • Cache two versions of each URL
  88. </talk>

  89. 🙏 @rungta @miranj