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.

Prateek Rungta

September 24, 2021
Tweet

More Decks by Prateek Rungta

Other Decks in Programming

Transcript

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

    View full-size slide

  2. CMS Browser
    Server
    /home

    View full-size slide

  3. CMS Browser
    Server
    /hom
    e


    View full-size slide

  4. Bolt

    Kirby

    Ghost

    Drupal

    Joomla

    Magento

    Cra
    ft
    CMS

    WordPress

    Expression Engine

    View full-size slide

  5. Recap: Critical CSS

    View full-size slide

  6. How fast does the page load?

    View full-size slide

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

    View full-size slide

  8. Only a part of the page is visible
    🪟

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. Bundle CSS for visible portion of
    the page with HTML
    📦

    View full-size slide

  12. Async-load rest of the CSS
    🚶

    View full-size slide

  13. Improve “perceived” load speed
    🪄⏱

    View full-size slide

  14. First Contentful Paint (FCP)

    View full-size slide

  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

    View full-size slide

  16. Credit: web.dev/fcp
    Time

    View full-size slide

  17. 1.8 seconds or less
    🚀

    View full-size slide

  18. Critical CSS directly impacts FCP

    View full-size slide

  19. Largest Contentful Paint (LCP)

    View full-size slide

  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

    View full-size slide

  21. Credit: web.dev/lcp
    Time

    View full-size slide

  22. Credit: web.dev/lcp
    Time

    View full-size slide

  23. 2.5 seconds or less
    🚀

    View full-size slide

  24. Critical CSS indirectly impacts LCP

    View full-size slide

  25. Apply this to CMS-based sites

    View full-size slide

  26. 1. Content is dynamic.

    What is above the fold?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30. 1. Page Selection

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  33. Article Category Author

    View full-size slide

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

    View full-size slide

  35. Pick one average case sample per
    unique template

    View full-size slide

  36. Article Category Author

    View full-size slide

  37. Extract CSS for that page & re-use
    for all instances of that template

    View full-size slide

  38. article.critical.css,

    index.critical.css,

    category.critical.css,

    etc.

    View full-size slide

  39. 2. CSS Extraction

    View full-size slide

  40. 1. Render full page (HTML, CSS, assets etc.)


    2. Define viewport width & height


    3. Filter CSS selectors used in the viewport

    View full-size slide

  41. Web-based
    Command line
    Build tools

    View full-size slide

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

    View full-size slide

  43. Use a build tool such as Gulp.js

    View full-size slide

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

    View full-size slide

  45. 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',

    }

    View full-size slide

  46. 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 },

    ],

    })

    }

    View full-size slide

  47. 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}`)

    )

    View full-size slide

  48. 4. Watch for changes in template or CSS files, repeat
    exports.default = () => {

    gulp.watch('dist/styles.css', 'critical')

    gulp.watch('templates/**/*.{html,twig}', 'critical')

    }

    View full-size slide

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

    View full-size slide

  50. This is great for first-time visitors.

    View full-size slide

  51. What about subsequent or

    repeat visits?

    View full-size slide

  52. The browser already has the full
    CSS from the first visit.

    View full-size slide

  53. Di
    ff
    erent responses for

    first time visits & repeat visits

    View full-size slide

  54. How do we figure out if a request
    is a first or repeat visit?
    🤔

    View full-size slide

  55. Set a cookie during the first visit
    🍪

    View full-size slide

  56. Does the request contain 🍪?

    ✅ has cookie = repeat visit


    ❌ no cookie = first visit

    View full-size slide

  57. Credit: instagram.com/dudewithsign

    View full-size slide

  58. “Technical cookie”, no personally
    identifiable information,

    no cookie banner
    🧑💻🔒

    View full-size slide

  59. CMS Browser
    /home

    View full-size slide

  60. CMS Browser
    ⏳ Takes some time

    View full-size slide

  61. CMS Browser
    /home



    View full-size slide

  62. CMS Browser
    Cache
    /home

    View full-size slide

  63. CMS Browser
    Cache
    /home ...

    View full-size slide

  64. CMS Browser
    Cache
    /home ...
    /about ...

    View full-size slide

  65. CMS Browser
    Cache
    /home ...
    /about ...
    /blog ...
    /article-1 ...
    /article-2 ...
    /article-3 ...

    View full-size slide

  66. CMS Browser
    Cache
    /home

    View full-size slide

  67. CMS Browser
    Cache
    /home
    🍪 ...

    View full-size slide

  68. CMS Browser
    Cache
    /home
    🍪 ...
    - ...

    View full-size slide

  69. CMS Browser
    Cache
    /home
    🍪 ...
    - ...
    /about
    🍪 ...
    - ...
    /blog
    🍪 ...
    - ...

    View full-size slide

  70. We now cache two versions for
    each URL
    🍪

    View full-size slide

  71. • 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

    View full-size slide

  72. 🙏
    @rungta @miranj

    View full-size slide