Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can it remove unused css? Just like uncss. #329

Open
lmrwork opened this issue Aug 13, 2019 · 5 comments
Open

Can it remove unused css? Just like uncss. #329

lmrwork opened this issue Aug 13, 2019 · 5 comments

Comments

@lmrwork
Copy link

lmrwork commented Aug 13, 2019

Can it remove unused css? Just like uncss.

@peterbe
Copy link
Owner

peterbe commented Aug 14, 2019

No it can not.

If you have A.css and B.css it leaves those alone and produces a C.css.
The idea is that you stop using <link href=A.css rel=stylesheet> <link href=B.css rel=stylesheet> and just use <style>{{ content of C.css }}</style>.

What I do on my sites is that I have C.css inlined with a <style> tag and then some JavaScript code that lazily injects A.css and B.css as <link> elements into the DOM. That way, if there are CSS selectors needed, after initial load, that's only in say B.css it will be styled.

One feature I'm interested in but haven't had time to implement is...

  • Input:
    • A.css
    • B.css
  • Output:
    • C.css (combined minimal CSS from A.css and B.css)
    • a.css (what's left of A.css minus C.css)
    • b.css (what's left of B.css minus C.css)

I'd love for someone to step up and write that. It's probably just a matter of using csstree correctly. After all, A.css is parsed into a csstree AST and available in memory.

I can help with the stuff around it if someone can find a csstree pattern to subtract selectors from one AST based on another AST.

Something you'd be interested in attempting @lmrwork ?
Perhaps @lahmatiy can give us a hint to get started 😉

@lahmatiy
Copy link
Contributor

I’m aware of rules removal from original styles, because of side effects. For instance, suppose we have a.css:

.foo { ... }  /* not used in critical path, so it will not aded to c.css */
.bar { ... }  /* used in critical path, goes to c.css  */

Note that .bar overlaps .foo, because of rule’s order (specificity of rules is the same).

If you inject c.css and then a.css, you’ll get:

/* c.css */
.bar { ... }
/* a.css */
.foo { ... }
.bar { ... } /* yeah, that’s duplicate */

All work as intended (.bar still overlaps .foo).

However, if you subtract rules used in c.css from a.css, you’ll get:

/* c.css */
.bar { ... }
/* a.css */
.foo { ... }

And now .foo overlaps .bar...

@peterbe
Copy link
Owner

peterbe commented Aug 14, 2019

@lahmatiy But what if you inject the new a.css BEFORE c.css?
Pseudo code...

window.onload = function() {
  var criticalStyleElement = document.querySelector('style')
  ['/static/a.css', '/static/b.css'].forEach(function(url) {
    var link = document.createElement('link'); 
    link.rel = 'stylesheet';
    link.href = url;
    link.insertBefore(criticalStyleElement);
  })
};

You'd end up with a DOM that looks something like this:

<head>
  <title>My Page</title>

  <link rel=stylsheet href=a.css>
  <link rel=stylesheet href=b.css>
  <style>{{ content of c.css }}</style>
  ...

That way, you'd ultimately end up with:

/* a.css */
.foo { ... }
/* c.css */
.bar { ... }

@lahmatiy
Copy link
Contributor

@peterbe Just change the order of .foo & .bar in a.css and you'll get the same problem. Real CSS is much complicated, so some style can be broken when you put c.css before a.css and some when after.
Btw, the right place for c.css is before original styles.

@peterbe
Copy link
Owner

peterbe commented Aug 15, 2019

Ok. I can see that if I think about it. Darn it!

Somewhat related I came to the same conclusion if attempting to find a combined minimal CSS from two different URLs that have either different DOMs or different CSSs or both different.

Still, let's get back on track. I can definitely see some useful use cases of finding out which selectors were not in the minimal CSS. For example, if you might help you find blocks of CSS in your originals that are never needed. Kinda like Chrome's coverage report.

You could do something like this:

  • A.css is the original fat bloated CSS
  • C.css is the minimal CSS
  • BLOAT.css is A.css minus C.css (<-- this is where we need csstree AST magic)

Now, if you pretty-print that BLOAT.css you can open it and see what's not used and you can start inspecting it manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants