
Show HN: DropCSS – A simple, thorough and fast unused-CSS cleaner - leeoniya
https://github.com/leeoniya/dropcss
======
tyingq
The css/js coverage tab in Chrome's developer tools is also pretty neat:
[https://developers.google.com/web/updates/images/2017/04/cov...](https://developers.google.com/web/updates/images/2017/04/coverage.png)

It drills down into the files, and there's a toggle to unminify:
[https://www.vojtechruzicka.com/static/911b02bfd910c9a689e363...](https://www.vojtechruzicka.com/static/911b02bfd910c9a689e363de64a2e8c0/17624/per-
line-coverage.png)

~~~
leeoniya
yep, that's where i got the unused bytes stats from

------
amoerie
What about dynamically changing HTML? Modal dialogs, dynamically loaded divs
(depending on access rights), etc.

Unless your css is only ever loaded when the corresponding HTML is, this is
going to flag a lot of false positives..

~~~
leeoniya
yep, you need to keep a whitelist. i made a 10-liner plugin for css-obj (see
my other comment) which i use to mark certain selectors with "!keep" which
populates my per-page whitelists during initial css blob gen.

if most of your classes are dynamically applied or most markup is missing then
this type of tool is probably not for you, especially if you have no good way
of html-ifying enough state variations to keep your whitelist managable.

FWIW, i run an SSR'd ecommerce site (with modals and a quite a bit of other
dynamic bits) and while whitelisting needs to be maintained, the resulting
list is fewer than 30 items and basically static. the items are regex and some
cover multiple/related selectors.

an improvement to this project might be to add inline ignore rules like
purgecss does. eg /* nodrop * /

------
leeoniya
Hi HN,

I put this together when looking to replace Purgecss in my build chain due to
its numerous and unresolved/unresolvable correctness deficiencies.

TL;DR:

\- Tiny API, ~60 LOC + 3 small deps

\- Does not process `<script>` or JS

\- Smaller and more thorough than Purgecss, UnCSS, PurifyCSS

\- Perf: 2.0x UnCSS, 1.1x PurifyCSS, 0.4x Purgecss

~~~
owens99
Nice work! Will give it a try.

------
Theodores
Very nice tool.

Small request. Can you use your knowledge of AST etc. to write another small
60 line script that just counts the amount of style rules in a CSS file?

I have tried this and not been able to do a good job of it, media queries, CSS
variables and other things have thrown me off and I think you are coming from
a better starting point for this 'simple task'.

Lines of code is not a useful metric with CSS and counting rules is not so
simple as parsing is really a browser thing.

~~~
leeoniya
by 'style rules' do you mean selectors or properties/values?

~~~
Theodores
Yes, just the amount of selectors.

Properties/values is another metric worth knowing.

I think that getting a number for both these metrics in the stylesheets rather
than the page currently viewed is more useful if you are working on a site.

As well as the stylesheets that are external the rules in an embedded
stylesheet are useful. My ideal tool takes a page and gets those metrics from
attached stylesheets as well as the inline stylesheets, with inline styles
totalled up too.

I have had a go at this using DomDocument PHP but took a guess at the rules
based on the total count of '{' \- terrible hack, but okay for ballpark
figures. I would prefer to be able to do it properly, but I have not got
anywhere trying to do it the node way. I didn't even know about CSSTree/AST.

~~~
leeoniya
the main loop [1] already only visits "Rule" blocks, so you can just stick a
counter in there, taking care to split combinator selectors on "," as i do,
and add them up.

[1]
[https://github.com/leeoniya/dropcss/blob/master/src/dropcss....](https://github.com/leeoniya/dropcss/blob/master/src/dropcss.js#L23-L24)

~~~
Theodores
Brilliant. Bookmarked. Thanks for that.

I am going to have a go with that, I found the main problem I was having and
that was a failure to actually obtain some stylesheets. The ones that were
without the [https://domain](https://domain) at the front were silently being
downloaded blank, parsed as blank and therefore not giving expected results.
Garbage in, garbage out rather than anything complicated. Sometimes though you
expect the harder bits to be wrong, e.g. only finding 'one match per line' due
to not having a convoluted enough regex, or something else complicated.

I have to work out how to present data too so I will finish that bit off using
my simple 'count the {' approach as that is near enough for a rule count, and
fast too.

However, I am also interested in qualitative analysis to determine if things
such as CSS grid is used or if it is all floats, whether CSS variables are
used or not and if selectors are all about classes or if there are more
advanced ones.

I think I really need to get the backend bit cludged together for now but then
pick up on your work to take things on to this next level. Sometimes a cludge
that is ballpark is useful, presenting information can only really be done
when you have something to work with. Any developer can find out the
information they need, presenting it to a non technical audience in a succinct
way in order to back up an argument is a very different proposition.

Believe it or not 'getting the colours right' is a challenge. So imagine I
want to convey to some person that shuns code and only cares about pretty
pictures that their plans to use this theme or build a Wix/Shopify/Squarespace
site is not what they want to be doing. They aren't going to listen to me
describing how these easy solutions are not performant. But, with a few pretty
graphs and succinct summaries that becomes easier.

I think you have given me the pieces of the puzzle that I need to do this. In
my own mind I can imagine the bullet points that summarise the key findings.
Best get on with it!!!

Many thanks.

------
bigbadgoose
OK, what all these tools have not exhibited so far is upstream optimization of
source generator, ie scss.

Dream scenario: 1/ run against generated output via url submission, 2/ receive
optimized output, 3/ point at source generator tree, 4/ receive suggested
source diffs

Lucid dream scenario: point at repository, receive optimized source diff
suggestions on a per-file basis

~~~
leeoniya
> 4/ receive suggested source diffs.

most of the removed css isnt necessarily useless globally, it's just useless
in context. eg a shared bootstrap css grid will be pruned differently on
different pages, but there's no single "suggestion" that can be used to modify
that shared initial grid.

for css that's leftover by accident that should actually be removed from the
source, i agree. but how will you discern the first scenario from the second?

~~~
bigbadgoose
Per-group (URLs that operate within a 10% optimization range) cached css,
generated by source tree + crawl + ongoing visit data?

Edit, also pls send me a pony. Optimzations have a few audiences, the
primaries of which are the visitor and the developer. Source tree prunes
("developer audience") not addressed in my off-the-cuff pipe dream above

------
buu700
Does (or will) this support SCSS, and Angular HTML, and
JavaScript/TypeScript/JSX? I'm on a project right now where a tool like that
will be immensely helpful pretty soon.

~~~
leeoniya
most definitely not. at least not in the core.

the aim is to stay simple and target the common baseline; there are ~1e6 css
and html supersets with existing tools to output plain html and css. surely
it's not difficult to glue the subset you need together.

as an aside, i've come to dislike scss and much prefer to maintain my
stylesheets in plain js, without awkward looping constructs, conditionals,
variables, "mixins", etc. all of this is an already-solved problem with es
modules and the language i already use: js.

check out:

[https://github.com/cssobj/cssobj](https://github.com/cssobj/cssobj)

[https://github.com/cssobj/cssobj-plugin-
gencss](https://github.com/cssobj/cssobj-plugin-gencss)

------
jhabdas
Very pithy, Leon. What made you think to write this?

P.s. When you slightly tweak MIT code you need to include the copyright and
notice in your license, or ask the copyright holder to switch to 0BSD.

~~~
leeoniya
> What made you think to write this?

[https://news.ycombinator.com/item?id=19469082](https://news.ycombinator.com/item?id=19469082)

also, i wanted to see how fast something like this would be if i pulled
together all libs that advertise/prove themselves to be "fastest" in their
respective responsibilities amongst similar competing libs.

> P.s. When you slightly tweak MIT code you need to include the copyright and
> notice in your license, or ask the copyright holder to switch to 0BSD.

thanks, good to know. on the other hand, that adapter is so trivial that i
don't think it quite warrants it. there are only so many ways to implement it
against that small api surface. anyhow, first i want to see where the whole
situation ends up:

[https://github.com/nrkn/css-select-browser-
adapter/pull/3](https://github.com/nrkn/css-select-browser-adapter/pull/3)

[https://github.com/fb55/css-select/issues/118](https://github.com/fb55/css-
select/issues/118)

i'm also glad that this [1] got merged and released quickly :)

[1] [https://github.com/taoqf/node-html-
parser/pull/5](https://github.com/taoqf/node-html-parser/pull/5)

------
JensRex
I wish there was something like this not written in JavaScript.

~~~
leeoniya
i'm sure there will be soon enough. i kind of laughed to myself when calling
it "fast" since it takes 175ms to process ~45 KB of data :D. in a compiled
language (or wasm) it would no doubt be an order of magnitude faster.

~~~
postalrat
Why is wasm faster than javascript? Can't the browser compile javascript to
something equivalent?

------
adrianhel
The fact that this is even needed is why I like CSS-in-JS.

~~~
leeoniya
you might still need it if you plan to use third party css libs, but not
wholesale.

