An exceptionally fast, thorough and tiny unused-CSS cleaner
npm install dropcssAn exceptionally fast, thorough and tiny (~10 KB min) unused-CSS cleaner _(MIT Licensed)_
---
DropCSS takes your HTML and CSS as input and returns only the used CSS as output. Its custom HTML and CSS parsers are highly optimized for the 99% use case and thus avoid the overhead of handling malformed markup or stylesheets, so well-formed input is required. There is minimal handling for complex escaping rules, so there will always exist cases of valid input that cannot be processed by DropCSS; for these infrequent cases, please start a discussion. While the HTML spec allows html, head, body and tbody to be implied/omitted, DropCSS makes no such assumptions; selectors will only be retained for tags that can be parsed from provided markup.
It's also a good idea to run your CSS through a structural optimizer like clean-css, csso, cssnano or crass to re-group selectors, merge redundant rules, etc. It probably makes sense to do this after DropCSS, which can leave redundant blocks, e.g. .foo, .bar { color: red; } .bar { width: 50%; } -> .bar { color: red; } .bar { width: 50%; } if .foo is absent from your markup.
More on this project's backstory & discussions: v0.1.0 alpha: /r/javascript, Hacker News and v1.0.0 release: /r/javascript.
---
---
```
npm install -D dropcss
---
`js
const dropcss = require('dropcss');
let html =
Hello World!
;let css =
p:hover a:first-child {
color: red;
};
const whitelist = /#foo|\.bar/;
let dropped = new Set();
let cleaned = dropcss({
html,
css,
shouldDrop: (sel) => {
if (whitelist.test(sel))
return false;
else {
dropped.add(sel);
return true;
}
},
});
console.log(cleaned.css);
console.log(dropped);
`
The shouldDrop hook is called for every CSS selector that could not be matched in the html. Return false to retain the selector or true to drop it.
---
- Supported selectors
| Common | Attribute | Positional | Positional (of-type) | Other |
|---------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|----------|
| - universal - tag# - id. - class> - descendant - child+ - adjacent sibling~ - general sibling | [attr][attr=val][attr[attr^=val][attr$=val][attr~=val] | :first-child:last-child:only-child:nth-child():nth-last-child() | :first-of-type:last-of-type:only-of-type:nth-of-type():nth-last-of-type() | :not() |
- Retention of all transient pseudo-class and pseudo-element selectors which cannot be deterministically checked from the parsed HTML.
- Removal of unused @font-face and @keyframes blocks.
- Removal of unused CSS variables.
- Deep resolution of composite CSS variables, e.g:
`css
:root {
--font-style: italic;
--font-weight: bold;
--line-height: var(--height)em;
--font-family: 'Open Sans';
--font: var(--font-style) var(--font-weight) 1em/var(--line-height) var(--font-family);
--height: 1.6;
}
@font-face {
font-family: var(--font-family);
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
}
body {
font: var(--font);
}
`
---
#### Input
test.html
- 18.8 KB minified
- 502 dom nodes via document.querySelectorAll("*").length
styles.min.css
- 27.67 KB combined, optimized and minified via clean-css
- contents: Bootstrap's reboot.css, an in-house flexbox grid, global layout, navbars, colors & page-specific styles. (the grid accounts for ~85% of this starting weight, lots of media queries & repetition)
#### Output
| lib size w/deps | output size | reduction | time elapsed | unused bytes (test.html coverage) | |
|---|---|---|---|---|---|
| DropCSS | 58.4 KB 6 Files, 2 Folders | 6.58 KB | 76.15% | 21 ms | 575 / 8.5% |
| UnCSS | 13.5 MB 2,829 Files, 301 Folders | 6.72 KB | 75.71% | 385 ms | 638 / 9.3% |
| Purgecss | 2.69 MB 560 Files, 119 Folders | 8.01 KB | 71.05% | 88 ms | 1,806 / 22.0% |
| PurifyCSS | 3.46 MB 792 Files, 207 Folders | 15.46 KB | 44.34% | 173 ms | 9,440 / 59.6% |
Notes
- About 400 "unused bytes" are due to an explicit/shared whitelist, not an inability of the tools to detect/remove that CSS.
- About 175 "unused bytes" are due to vendor-prefixed (-moz, -ms) properties & selectors that are inactive in Chrome, which is used for testing coverage.
- Purgecss does not support attribute or complex selectors: Issue #110.
A full Stress Test is also available.
---
DropCSS does not load external resources or execute