├── .gitignore ├── Gruntfile.js ├── README.md ├── article.php ├── category.php ├── css └── styles.css ├── functions └── criticalcss.php ├── index.php ├── package.json └── tasks ├── contrib-cssmin.js └── criticalcss.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | css/critical 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON( "package.json" ), 7 | // The following line sets a variable baseURL for production/local development environments: 8 | baseurl: process.env.baseurl || 'http://localhost:8080', 9 | // Capture the path to the CSS directory for the sake of both tasks: 10 | csspath: require( "path" ).resolve( "css/" ) 11 | }); 12 | 13 | grunt.loadTasks( "tasks" ); 14 | 15 | grunt.registerTask( "critical", [ 16 | "criticalcss", 17 | "cssmin:critical" 18 | ]); 19 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Critical CSS Boilerplate 2 | There’s a lot of information about the CriticalCSS technique out there—[how it works](https://www.smashingmagazine.com/2015/08/understanding-critical-css/) and [why it is so rad](https://www.filamentgroup.com/lab/performance-rwd.html)—and disparate [tools to help with the process](https://github.com/addyosmani/critical). 3 | 4 | What we haven’t seen much of are demos of CriticalCSS in action—minimal, functional examples of a seamless CriticalCSS setup, presented in a way that provides a clear path to _fully automated_ CriticalCSS generation and inclusion. 5 | 6 | That’s our goal for this repo. 7 | 8 | Currently, this project assumes very little about development environment and nothing about deployment strategy—it is using Grunt for the CSS generation and minification, a relatively minimal PHP function for inclusion, and _nothing_ in the way of a path to deployment. 9 | 10 | Ultimately, this repository will contain multiple examples of the CriticalCSS approach, with clear documentation and example code for various languages and deployment strategies. 11 | 12 | ## Getting Started 13 | 14 | To run this project locally, spin up a development server using the following command: 15 | 16 | ```shell 17 | $ php -S localhost:8080 18 | ``` 19 | 20 | ### Generating CriticalCSS 21 | 22 | `grunt critical` will generate CriticalCSS files for each of the pages listed in `/tasks/criticalcss.js`—currently the “home” and the “category” pages. It will then minify the results in place. 23 | 24 | #### Dissecting `criticalcss.js` 25 | 26 | The CriticalCSS Grunt task itself is relatively uncomplicated. 27 | 28 | ```javascript 29 | grunt.config( "criticalcss", { 30 | PAGENAME: { 31 | options: { 32 | outputfile: "<%=csspath%>/critical/PAGENAME.css", 33 | filename: "<%=csspath%>/styles.css", 34 | url: "<%=baseurl%>/PATH/TO/PAGE?nocritical" 35 | } 36 | } 37 | } 38 | ``` 39 | 40 | Each page is a configurable object, containing the following: 41 | 42 | *`outputfile`* 43 | The desired path for the output; here, in a “critical” subdirectory inside the CSS directory. Like any generated CSS, this should be .`gitignore`’d. 44 | 45 | *`filename`* 46 | The local path to the full stylesheet. 47 | 48 | *`url`* 49 | The URL for the page. `baseurl`, seen here, is provided by `Gruntfile.js` and should be set to either a local development URL or the remote URL, depending on the task’s current context. The `?nocritical` query string is required so that the page used to generate the CriticalCSS doesn’t _contain_ CriticalCSS—otherwise the results of the generation would be the same every time the task is run, rather than relying on the full stylesheet for necessary styles. 50 | 51 | Additional configuration options are documented at [https://www.npmjs.com/package/grunt-criticalcss](https://www.npmjs.com/package/grunt-criticalcss). 52 | -------------------------------------------------------------------------------- /article.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Hello there

10 | 11 |

This page doesn’t have a CriticalCSS file associated with it, but uses the same loading pattern as all the other pages. In the event that a CriticalCSS file isn’t available for a page with a given slug, the full stylesheet is loaded as usual.

12 | 13 |

This page can be added to the CriticalCSS pattern by adding a new entry in /tasks/criticalcss.js.

14 | 15 | 16 | -------------------------------------------------------------------------------- /category.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

A Category Page

11 | 12 |

This page has a unique style for its heading, so that style will be included in the page’s CriticalCSS.

13 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

14 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

15 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

16 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

17 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

18 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 100%; 3 | font-family: Helvetica, Arial, sans-serif; 4 | line-height: 1.4; 5 | } 6 | 7 | .wrapper { 8 | max-width: 800px; 9 | margin: 0 auto; 10 | padding: 2em 0; 11 | } 12 | 13 | .wrapper-category { 14 | max-width: 1000px; 15 | margin: 0 auto; 16 | padding: 1em 0; 17 | } 18 | 19 | .hed { 20 | color: red; 21 | font-size: 2em; 22 | margin: 0 0 1.2em 0; 23 | padding: 0; 24 | } 25 | 26 | .category-hed { 27 | color: blue; 28 | font-size: 1.5em; 29 | margin: 0 0 1em 0; 30 | padding: 0; 31 | } 32 | 33 | .category-subhed { 34 | color: orange; 35 | font-size: 1.3em; 36 | margin: 0 0 1em 0; 37 | padding: 0; 38 | } 39 | 40 | .image-right { 41 | float: right; 42 | margin: 20px 0 20px 20px; 43 | max-width: 250px; 44 | } 45 | 46 | .image-left { 47 | float: left; 48 | margin: 20px 20px 20px 0; 49 | max-width: 250px; 50 | } 51 | 52 | .lead-text { 53 | font-size: 1.3em; 54 | line-height: 1.3; 55 | } 56 | 57 | .body-text { 58 | font-size: 1em; 59 | } 60 | 61 | .post-script { 62 | font-size: .75em; 63 | margin: 20px 0; 64 | } 65 | -------------------------------------------------------------------------------- /functions/criticalcss.php: -------------------------------------------------------------------------------- 1 | '; 14 | return false; 15 | } 16 | 17 | // Get the CriticalCSS file for a given page slug, to match the names assigned in /tasks/criticalcss.js: 18 | $criticalCssPath = 'css/critical/' . $slug . '.css'; 19 | 20 | // Get the contents of the CriticalCSS file: 21 | $critical = @file_get_contents($criticalCssPath); 22 | 23 | // Make sure the generated file isn’t empty: 24 | $criticalCssNotEmpty = $critical !== false && strlen($critical) !== 0; 25 | 26 | // If the file isn’t empty and we’re not opting out of CriticalCSS behavior—either because the cache is primed or because we’re explicitly avoiding it—we’ll be using the contents of the CriticalCSS file. 27 | $useCriticalCss = $criticalCssNotEmpty && ( 28 | !isset($_COOKIE['stylesCached']) || isset($_GET['nocritical']) 29 | ); 30 | 31 | if ($useCriticalCss) { 32 | // Serve the critical styles inline. 33 | $output = ''; 36 | 37 | // Load a small snippet of JavaScript that allows us to asynchronously load our full stylesheet: 38 | $output .= ''; 48 | } else { 49 | $output = ''; 50 | } 51 | print $output; 52 | } 53 | ?> -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Hello there

11 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis at commodo lectus. Curabitur finibus, sapien in luctus sodales, eros justo imperdiet felis, eget rutrum augue est vel lacus. Nulla pellentesque orci semper urna efficitur laoreet. In hac habitasse platea dictumst. Donec blandit commodo vestibulum. Ut tristique nec mi ac cursus. Aliquam vitae sem vel lectus consequat volutpat. Donec fringilla turpis a dui consequat consectetur. Donec dignissim lacus eget sem ornare, aliquet volutpat lacus ultricies. Donec a congue lorem. Fusce vitae ultricies diam, ac posuere nisl. Morbi pulvinar vestibulum felis, vel molestie orci posuere in. Aliquam eu lacus commodo, congue neque nec, tristique metus. Proin vulputate odio vel leo vestibulum, vitae semper lectus vehicula.

12 | hell, yes, this is dog 13 |

Pellentesque eu laoreet odio, sed fringilla leo. Donec sit amet imperdiet massa. Donec eu urna nunc. Curabitur et suscipit nulla, non tempor tortor. Suspendisse sapien nulla, auctor eget hendrerit placerat, bibendum non urna. Etiam placerat mollis sodales. Pellentesque tincidunt luctus velit sed placerat. Nullam aliquam neque ac nisi interdum, ac dapibus ex posuere. Aliquam vel tortor id nisl scelerisque facilisis. Vivamus euismod dolor tristique aliquet tempor.

14 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

15 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

16 | Smiling Monkey Selfie 17 |

Wait, there's more

18 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

19 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

20 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

21 |

Finally...

22 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

23 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

24 |

Integer non risus ante. Nullam in dolor enim. Ut sed ligula convallis, ullamcorper metus id, varius mi. Mauris mattis erat sit amet purus lobortis, eget sollicitudin odio placerat. Nulla fringilla accumsan erat et ullamcorper. Duis at consectetur metus. Curabitur vel fermentum purus. Donec id felis viverra justo ullamcorper consectetur. Etiam porta auctor est rhoncus tristique. Integer odio dui, aliquet sit amet mi id, placerat porta enim. Quisque tincidunt lorem sit amet lobortis porttitor. Suspendisse at dolor euismod, laoreet tellus ut, porttitor diam. Aliquam sed odio eget tortor interdum dictum.

25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "criticalcss-boiler", 3 | "description": "CriticalCSS Boilerplate", 4 | "version": "0.0.1", 5 | "author": { 6 | "name": "Bocoup", 7 | "url": "https://github.com/bocoup" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/bocoup/critical-css-boilerplate.git" 12 | }, 13 | "devDependencies": { 14 | "grunt": "^1.0.1", 15 | "grunt-cli": "^1.2.0", 16 | "grunt-contrib-cssmin": "^1.0.1", 17 | "grunt-criticalcss": "^1.0.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tasks/contrib-cssmin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.loadNpmTasks( "grunt-contrib-cssmin" ); 3 | 4 | grunt.config( "cssmin", { 5 | options: { 6 | banner: "/*! Built on: <%= grunt.template.today('yyyy-mm-dd') %> */" 7 | }, 8 | critical: { 9 | expand: true, 10 | cwd: "<%=csspath%>/critical/", 11 | dest: "<%=csspath%>/critical/", 12 | src: '*' 13 | } 14 | }); 15 | }; -------------------------------------------------------------------------------- /tasks/criticalcss.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.loadNpmTasks( "grunt-criticalcss" ); 3 | 4 | grunt.config( "criticalcss", { 5 | home: { 6 | options: { 7 | outputfile: "<%=csspath%>/critical/home.css", 8 | filename: "<%=csspath%>/styles.css", 9 | url: "<%=baseurl%>?nocritical" 10 | } 11 | }, 12 | category: { 13 | options: { 14 | outputfile: "<%=csspath%>/critical/category.css", 15 | filename: "<%=csspath%>/styles.css", 16 | url: "<%=baseurl%>/category.php?nocritical" 17 | } 18 | } 19 | }); 20 | }; 21 | --------------------------------------------------------------------------------