Slide 1

Slide 1 text

Critical CSS for CMS-based, Server-rendered Websites

Slide 2

Slide 2 text

CMS Browser Server /home

Slide 3

Slide 3 text

CMS Browser Server /hom e 
 .. . .. . 


Slide 4

Slide 4 text

Bolt 
 Kirby 
 Ghost 
 Drupal 
 Joomla 
 Magento 
 Cra ft CMS 
 WordPress 
 Expression Engine

Slide 5

Slide 5 text

Recap: Critical CSS

Slide 6

Slide 6 text

How fast does the page load? ⚡

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Only a part of the page is visible 🪟

Slide 9

Slide 9 text

Fetch CSS for visible portion of the page only 🧐

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Bundle CSS for visible portion of the page with HTML 📦

Slide 12

Slide 12 text

Async-load rest of the CSS 🚶

Slide 13

Slide 13 text

Improve “perceived” load speed 🪄⏱

Slide 14

Slide 14 text

First Contentful Paint (FCP) ⏱

Slide 15

Slide 15 text

“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

Slide 16

Slide 16 text

Credit: web.dev/fcp Time

Slide 17

Slide 17 text

1.8 seconds or less 🚀

Slide 18

Slide 18 text

Critical CSS directly impacts FCP

Slide 19

Slide 19 text

Largest Contentful Paint (LCP) ⏱

Slide 20

Slide 20 text

“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

Slide 21

Slide 21 text

Credit: web.dev/lcp Time

Slide 22

Slide 22 text

Credit: web.dev/lcp Time

Slide 23

Slide 23 text

2.5 seconds or less 🚀

Slide 24

Slide 24 text

Critical CSS indirectly impacts LCP

Slide 25

Slide 25 text

Apply this to CMS-based sites

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

1. Page Selection

Slide 31

Slide 31 text

We might have 1000s of active pages on any site

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Article Category Author

Slide 35

Slide 35 text

Extract Critical CSS per template, not for every single page

Slide 36

Slide 36 text

Pick one average case sample per unique template

Slide 37

Slide 37 text

Article Category Author

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

article.critical.css, 
 index.critical.css, 
 category.critical.css, 
 etc.

Slide 40

Slide 40 text

2. CSS Extraction

Slide 41

Slide 41 text

1. Render full page (HTML, CSS, assets etc.) 2. Define viewport width & height 3. Filter CSS selectors used in the viewport

Slide 42

Slide 42 text

Web-based Command line Build tools

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Automate!

Slide 45

Slide 45 text

Use a build tool such as Gulp.js

Slide 46

Slide 46 text

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.

Slide 47

Slide 47 text

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',
 }

Slide 48

Slide 48 text

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 },
 ],
 })
 }

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

4. Watch for changes in template or CSS files, repeat exports.default = () => {
 gulp.watch('dist/styles.css', 'critical')
 gulp.watch('templates/**/*.{html,twig}', 'critical')
 }

Slide 51

Slide 51 text

3. Delivery

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

.. . 🚫 Blocks rendering

Slide 54

Slide 54 text

.. .

Slide 55

Slide 55 text


 /* Critical CSS */
 
 ... 📦 Bundle with HTML

Slide 56

Slide 56 text

/* Critical CSS * / 
 
 ... 🚶 Async load full CSS

Slide 57

Slide 57 text

This is great for first-time visitors. ⚡

Slide 58

Slide 58 text

What about subsequent or 
 repeat visits?

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

Slide 61

Slide 61 text

Slide 62

Slide 62 text

Di ff erent responses for 
 first time visits & repeat visits

Slide 63

Slide 63 text


 
 
 /* Critical CSS */
 
 
 ... 
 
 
 
 
 ...

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Set a cookie during the first visit 🍪

Slide 66

Slide 66 text

Does the request contain 🍪? 
 ✅ has cookie = repeat visit ❌ no cookie = first visit

Slide 67

Slide 67 text


 
 
 /* Critical CSS */
 
 
 ... 
 
 
 
 
 ...

Slide 68

Slide 68 text


 
 
 /* Critical CSS */
 
 
 ... 
 
 
 
 
 ...

Slide 69

Slide 69 text


 
 
 /* Critical CSS */
 
 
 ... 
 
 
 
 
 ... Set-Cookie:
 repeat_visit=1

Slide 70

Slide 70 text


 
 
 /* Critical CSS */
 
 
 ... 
 
 
 .css>
 
 ... Set-Cookie:
 repeat_visit=

Slide 71

Slide 71 text

Credit: instagram.com/dudewithsign

Slide 72

Slide 72 text

“Technical cookie”, no personally identifiable information, 
 no cookie banner 🧑💻🔒

Slide 73

Slide 73 text

4. Caching

Slide 74

Slide 74 text

CMS Browser /home

Slide 75

Slide 75 text

CMS Browser ⏳ Takes some time

Slide 76

Slide 76 text

CMS Browser /home

Slide 77

Slide 77 text

CMS Browser Cache /home

Slide 78

Slide 78 text

CMS Browser Cache /home ...

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

CMS Browser Cache /home

Slide 82

Slide 82 text

CMS Browser Cache /home 🍪 ...

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

We now cache two versions for each URL 🍪

Slide 86

Slide 86 text

Recap

Slide 87

Slide 87 text

• 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

Slide 88

Slide 88 text

Slide 89

Slide 89 text

🙏 @rungta @miranj