Skip to content

Latest commit

 

History

History
175 lines (140 loc) · 6.52 KB

README.md

File metadata and controls

175 lines (140 loc) · 6.52 KB

🗑 DropCSS

A simple, thorough and fast unused-CSS cleaner (MIT Licensed)


Introduction

DropCSS is an unused CSS cleaner; it takes your HTML and CSS as input and returns only the used CSS as output. The core is simply some minimal glue between these awesome low-level tools:

The entire logic for DropCSS is this ~60 line file.

It is recommended to also run your CSS through an optimizer like clean-css to group selectors, merge and remove redundant rules, purge unused keyframes, etc. Whether this is done before or after DropCSS is up to you, but since clean-css also minifies, it probably makes sense to run DropCSS first to avoid bouncing [and re-parsing] the output back and forth (optimize & minify -> drop) vs (optimize -> drop -> minify), though this will likely depend on your actual input; profiling is your friend.

A bit more on this project's backstory & discussions in /r/javascript and on Hacker News.


Installation

npm install -D dropcss

Usage & API

const dropcss = require('dropcss');

let html = `
    <html>
        <head></head>
        <body>
            <p>Hello World!</p>
        </body>
    </html>
`;

let css = `
    .card {
      padding: 8px;
    }

    p:hover a:first-child {
      color: red;
    }
`;

const whitelist = /#foo|\.bar/;

let dropped = new Set();

// returns { css }
let cleaned = dropcss({
    html,
    css,
    keepText: false,
    shouldDrop: (sel) => {
        if (whitelist.test(sel))
            return false;
        else {
            dropped.add(sel);
            return true;
        }
    },
});

console.log(cleaned.css);

console.log(dropped);
  • shouldDrop is called for every CSS selector that could not be matched in the html. Return false to retain it or true to drop it. Additionally, this hook can be used to log all removed selectors.
  • keepText - By default, DropCSS will remove all text nodes from the HTML before processing further since very few CSS selectors can actually target text. Not having to process text nodes is a significant performance boost. However, a few uncommon pseudo-classes like :blank and :empty do assert on text nodes. If combined as e.g. :not(:empty), this could result wrongful removal, or wrongful retention of selectors. Setting keepText to true will leave all text nodes in place to allow for this to work properly at the expense of performance.

Features

DropCSS stands on the shoulders of giants.

  • Removal of practically all conceivable selectors is achieved thanks to the exhaustive selector support of css-select: https://github.com/fb55/css-select#supported-selectors.
  • CSSTree enables media queries to be transparently processed and removed like all other blocks without special handling.
  • Retention of all transient pseudo-class and pseudo-element selectors which cannot be deterministically checked from the parsed HTML.

Performance

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 2.16 MB
251 Files, 51 Folders
6.60 KB 76.15% 138ms 575 / 8.5%
UnCSS 13.7 MB
2,831 Files, 301 Folders
6.72 KB 75.71% 429ms 638 / 9.3%
Purgecss 2.53 MB
513 Files, 110 Folders
8.01 KB 71.05% 78ms 1,806 / 22.0%
PurifyCSS 3.45 MB
791 Files, 207 Folders
15.4 KB 44.34% 186ms 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.

Caveats

  • Not tested against malformed HTML (the underlying Fast HTML Parser claims to support common cases but not all)
  • Not tested against malformed CSS (the underlying CSSTree parser claims to be "Tolerant to errors by design")
  • There is no processing or execution of <script> tags; your HTML must be fully formed (or SSR'd). You should generate and append any additional HTML that you'd want to be considered by DropCSS. If you need JS execution, consider using the larger, slower but still good output, UnCSS. Alternatively, Puppeteer can now output coverage reports, and there might be tools that utilize this coverage data to clean your CSS, too. DropCSS aims to be minimal, simple and effective.