Extract the minimal CSS used in a set of URLs with puppeteer
npm install minimalcss!Build status



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 withdocument.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.) and uses the DOM anddocument.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
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:
``bash`
yarn add minimalcss --dev
You can install it globally if you like:
`bash`
yarn global add minimalcss
`bash`
npm install [--save-dev|--global] minimalcss
Now you can run it:
`bash`
./node_modules/.bin/minimalcss https://example.com/ https://example.com/aboutus > minimal.min.css
minimalcss isn't the first library to perform this task. What's unique andminimalcss
special about is that it uses the Chrome Headless browser.
* penthouse -
uses puppeteer (since version 1.0) and CSSTree.
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 GET of the URL as if done by a server). I.e.PhantomJS
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 use to 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.
* You can specify a viewport,
which might cause the page to render slightly different. It does not
create the minimal CSS _only_ on DOM that is visible though.
* If the CSS contains @font-face { ... } rules whose name is neverfont-face
used in any remaining CSS selector, the whole block is removed.
Let's make this a thriving community project!
Help needed with features, tooling, and much testing in real web performance
optimization work.
`javascript`
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:
`javascriptFailed the minimize CSS: ${error}
minimalcss
.minimize({ url: 'https://example.com/' })
.then(result => {
console.log('OUTPUT', result.finalCss.length, result.finalCss);
})
.catch(error => {
console.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.
Optionally, you can supply a list of URLs like this:
`javascript`
minimalcss
.minimize({ urls: ['https://example.com/page1', 'https://example.com/page2'] })
...
and minimalcss will try to merge the minimal critical CSS across all pages.
But we aware that this can be "dangerous" because of the inherit order of CSS.
Calling minimalcss.run(options) takes an object whose only mandatoryurls
key is or url. Other optional options are:
* debug - all console logging during page rendering are included in thedocument.querySelector
stdout. Also, any malformed selector that cause errors in skippable
will be raised as new errors.
* - function which takesloadimages
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.
* - If set to true, images will actually load.withoutjavascript
* - If set to false it will _skip_ loading the page firstminimalcss
without JavaScript. By default will evaluate the DOM as plain asdisableJavaScript
can be, and then with JavaScript enabled _and_ waiting for network activity
to be idle.
* - By default JavaScript is enabled. If set to true it will ignore withoutjavascript option and loading the page only one time without JavaScript.browser
* - Instance of a Browser, which will be used instead of launching another one.userAgent
* - specific user agent to use (string)viewport
* - viewport object as specified in page.setViewportpuppeteerArgs
* - Args sent to puppeteer when launching. ListcssoOptions
of strings for headless Chrome.
* - CSSO compress function optionstimeout
* - Maximum navigation time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout.ignoreCSSErrors
* - By default, any CSS parsing error throws an error in minimalcss. If you know it's safe to ignore (for example, third-party CSS resources), set this to true.ignoreJSErrors
* - By default, any JavaScript error encountered by puppeteerignoreRequestErrors
* - When CSS files return 404 or another request error minimalcss will ignore this instead of throwing an error.minimalcss
will be thrown by . If you know it's safe to ignore errors (for example, ontrue
third-party webpages), set this to .styletags
* - If set to true, on-page
`
In this case the @font-face { font-family: MyName; } would be removed even
though it's mentioned from somewhere else.
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.
First thing to get started, once you've cloned the repo is to install all
the dependencies:
`sh`
yarn
Testing is done with jest. At the
beginning of every test, a static file server is started on localhostpuppeteer
and a browser instance is created for every test.
To run the tests:
`sh`
yarn jest
Best way to get into writing tests is to look at existing tests and copy.
All 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:
`sh`
yarn lintcheck
This blog post
demonstrates technique to use minimalcss when you don't yet have a server.http-server` package you can start a server right before you run
Using the
and shut down as soon as you're done.
Copyright (c) 2017-2020 Peter Bengtsson.
See the LICENSE file for license rights and limitations (MIT).