A Node library to extract the minimal CSS used in a set of URLs with puppeteer.
Used to find what minimal CSS is needed to render on first load, even with
document.onload executed.
This minimal CSS is also known as critical path CSS and ultimately a web performance technique to make web pages load faster at initial load.
You supply a list of URLs that it opens (one at a time) and for each page
it downloads all external CSS files (e.g.
<link rel="stylesheet" href="bootstrap.min.css">) and uses the DOM and
document.querySelector to investigate which selectors, in the CSS, are
actually in the DOM. That minimal payload of CSS is all you need to load
the URLs styled without having to make it block on CSS.
Under the hood it relies on the excellent puppeteer library which uses the Headless Chome Node API. This means it runs (at the time of writing) Chrome 62 and this library is maintained by the Google Chrome team.
The CSS to analyze (and hopefully minimize) is downloaded automatically just
like a browser opens and downloads CSS as mentioned in the DOM as <link>
tags.
The CSS is parsed by CSSTree and the minification and compression is done with CSSO. An AST of each CSS payload is sent into the Headless Chrome page evaluation together with a callback that compares with the DOM and then each minimal CSS payload is concatenated into one big string which then CSSO compresses into one "merged" and minified CSS payload.
Install:
yarn add minimalcss --dev
You can install it globally if you like:
yarn global add minimalcss
npm install [--save-dev|--global] minimalcss
Now you can run it:
$ ./node_modules/./bin/minimalcss https://example.com/ https://example.com/aboutus > minimal.min.cssminimalcss isn't the first library to perform this task. What's unique and
special about minimalcss is that it uses the Chrome Headless browser.
-
penthouse - uses PhantomJS which is a headless WebKit browser.
PhantomJSis no longer maintained. Supports only 1 URL at a time and can't you have to first save the CSS files it should process. -
critical - uses
penthouse(see above) with its "flaws" meaning you can only do 1 URL (or HTML string) and you have to prepare the CSS files too. -
UnCSS - uses jsdom to render and execute JavaScript. Supports supplying multiple URLs but still requires to manually supply the CSS files to process.
-
mincss - Python project that uses lxml.html to analyze the HTML statically (by doing a
GETof the URL as if done by a server). I.e. it can't load the HTML as a real browser would and thus does not support a DOM with possible JavaScript mutations on load. It can optionally usePhantomJSto extract the HTML.
-
You don't need to specify where the CSS is. It gets downloaded and parsed automatically.
-
It uses puppeteer and CSSTree which are both high quality projects that are solid and well tested.
-
The CSS selectors downloaded is compared to the DOM before and after JavaScript code has changed the DOM. That means you can extract the critical CSS needed to display properly before the JavaScript has kicked in.
-
Ability to analyze the remaining CSS selectors to see which keyframe animations that they use and use this to delete keyframe definitions that are no longer needed.
This is highly experimental.
The goal is to expand the tooling with all the bells and whistles that
penthouse and critical etc. has.
Let's make this a thriving community project!
Help needed with features, tooling, and much testing in real web performance optimization work.
const minimalcss = require('minimalcss')Just prints out the current version.
Returns a promise. The promise returns an object containing, amongst other things, the minified minimal CSS as a string. For example:
minimalcss
.minimize({ urls: ['http://peterbecom.dev/css-blocking/ultra-basic.html'] })
.then(result => {
console.log('OUTPUT', result.finalCss.length, result.finalCss)
}).catch(error => {
console.error(`Failed the minimize CSS: ${error}`)
})That result object that is returned by the minimize function contains:
finalCss- the minified minimal CSS as a string.stylesheetContents- an object of stylesheet URLs as keys and their content as text.stylesheetAstObjects- an object of stylesheet URLs as keys and their AST as a plain object.
Calling minimalcss.run(options) takes an object whose only mandatory
key is urls. Other optional options are:
debug- all console logging during page rendering are included in the stdout. Also, any malformed selector that cause errors indocument.querySelectorwill be raised as new errors.skippable- function wich takes request as an argument and returns boolean. If it returns true then given request will be aborted (skipped). Can be used to block requests to Google Analytics etc.loadimages- If set totrue, images will actually load.withoutjavascript- If set totrueit will skip loading the page first without JavaScript. By defaultminimalcsswill evaluate the DOM as plain as can be, and then with JavaScript enabled and waiting for network activity to be idle.browser- Instance of a Browser, which will be used instead of launching another one.userAgent- specific user agent to use (string)
When minimalcss evaluate each CSS selector to decide whether to keep it
or not, some selectors might not be parseable. Possibly, the CSS employs
hacks for specific browsers that
cheerio doesn't support. Or
there might be CSS selectors that no browser or tool can understand
(e.g a typo by the CSS author). If there's a problem parsing a CSS selector,
the default is to swallow the exception and let the CSS selector stay.
Also by default, all these warnings are hidden. To see them use the --debug
flag (or debug API option). Then the CSS selector syntax errors are
printed on stderr.
We use ES6+ with jsdoc comments and TypeScript to do static type checking, like puppeeteer does.
Run tsc to check types.
yarn tscAll code is expected to conform with Prettier according
to the the .prettierrc file in the root of the project.
To check that all your code conforms, run:
yarn lintcheck
If your document uses Blob to create injectable stylesheets into the DOM, minimalcss will not be able to optimize that. It will be not be included in the final CSS.
Copyright (c) 2017-2018 Peter Bengtsson. See the LICENSE file for license rights and limitations (MIT).