GoogleChromeLabs / tooling.report

tooling.report a quick way to determine the best build tool for your next web project, or if tooling migration is worth it, or how to adopt a tool's best practice into your existing configuration and code base.
https://tooling.report
Apache License 2.0
848 stars 49 forks source link

CSS strategy #40

Closed jakearchibald closed 4 years ago

jakearchibald commented 4 years ago

Options:

Manually arrange CSS

Manually control the number of CSS files output in production, and manually control what they contain.

You could still have one CSS file per component, but another CSS file would 'import' them, and postcss (or whatever) would concatenate them. Eg:

component1.css

.component1 {
  background: green;
}

component2.css

.component2 {
  background: blue;
}

page-type-1.css

@import url('./component1.css');
@import url('./component2.css');

The output would be page-type-1.css, which contained the contents of component1.css and component2.css.

This should be compatible with CSS modules if you still want to use them.

Use a CSS framework

Something like tailwind. We can then use a plugin to eliminate unused styles. CSS modules aren't needed here.

Per-component styles

Author per-component styles using CSS modules, and use 'smarts' to combine and split them, similar to Next.js.

The only downside here is I'm not sure how to implement this, so it might be a big job, and things might end up falling back to "Manually arrange CSS" if I don't figure it out.

@argyleink @una over to you! Feel free to add anything I've missed. I just wanted to move the discussion from chat to here.

argyleink commented 4 years ago

those are essentially what we've discussed as options as well. I think we're headed towards a combo of your options here, "manually arranged CSS" using your css modules pr/rollup config, and yep, more manual but then we can put things where we want them.

the hashed names kinda taint utility style css architecture, am i wrong here? it, doesnt necessarily make it easier? anyway, it's not blocking it.

i believe our current plan is along these lines

import {inline as NavStyles} from '../../components/Footer/style.css';
import {inline as HeaderStyles} from '../../components/Header/style.css';
import {inline as TypeStyles} from '../../components/Type/style.css';

...

const criticalStyles = NavStyles + HeadStyles + TypeStyles;
    <html>
      <head>
        <style>{criticalStyles}</style>
        <link rel="stylesheet" href={appStyleCSSPath} />
      </head>
      <body>
        <Header /> 
        <h1>Tests</h1>
        <section>{renderTests(tests)}</section>
        <Footer />
      </body>
    </html>

where appStyleCSSPath is rollup/postcss making a bundle out of all our component single files and global styles.

we can gather and split what we want, make component styles, and share components styles, all with what your setup. i think we're fine. the less opinionated build system will let us iterate and be flexible i think. we arent managing that many pages, so i'm not worried about this manual work scaling beyond our abilities.

thoughts tho? @una, does this match what you were thinking? It's basically on us here to:

@jakearchibald, just tried @import from a css-module and looks like that logic needs added? or we use postcss and a plugin to do it? did i hold it wrong?

my personal work uses a system very much like next, and it's fine, but does have it's tradeoffs.

jakearchibald commented 4 years ago

@jakearchibald, just tried @import from a css-module and looks like that logic needs added? or we use postcss and a plugin to do it? did i hold it wrong?

I haven't implemented any of the options yet, and I won't until we decide which to use. Feel free to play around though.

una commented 4 years ago

See #46 -- adds @import logic

argyleink commented 4 years ago

with #46 adding @import we can make css bundles with just css, then with jakes work we can get a cssPath and import it. sounds like we could just make an entry css file per layout/view (i count 3 atm), have that be "page styles," aka the components used on the page. then we can use jakes logic to inline critical styles in the head. and lastly, create a shared index file that we let @import bundle and jake's cssPath load a shared file on each page before the page styles.

flows like this?

  1. critical inline (manually js controlled)(do we need?)
  2. shared css file (manually css controlled)
  3. page css file (manually css controlled)

and our elements on each page leverage shared hashed classes, for no naming collisions. seems capable to me, we could even cut the critical inline out and stick it into shared. we've got options 👍

only gotcha feels like duplicating stuff in shared and page, otherwise it's a g2g strat?

una commented 4 years ago

Yeah @jakearchibald & @argyleink -- that pretty much sums up my thoughts on this ☝️Here's my take

I agree the biggest thing we need to decide is if we want to go with a utility framework like Tailwind or not. The way we're moving now is leaning towards not doing this and instead using codesplitting/class naming with a shared base (current trajectory).

Utility CSS

I like utility CSS, but I'm not convinced that it would be more performant in our case.

Tailwind inherently does not control for unused CSS and seems like it would result in a bulkier output. Adding the colocated and name-spaced styling (Jakes PR with the codesplitting) to utility CSS also wouldn't help with this as we'd end up with code like:

.header34230,
.link3923,
.header20034,
tooltip-summary2039 {
  color: blue
}

Utility CSS generally shares one file for the entire product, which helps reduce weight in many cases but in a site with so few varied components that share visual styles, I don't think it will be a huge boost. It also removes the benefits of code splitting as we're not separating code needed per page.

Code Split / Colocated Styles / Managed Stylesheets

This is what your PR (Jake) moves us toward, and what #46 demonstrates. Colocating styles with the components so we can codesplit them and retain unique namespaces. The issue is the <style>{inline}</style>

What I would like to see is this:

Ideally, when we import Footer into a page view, this would also import the style.css associated with Footer. Say a page imports Footer and SummaryCard. SummaryCard imports Tooltip. I would expect the page to build a CSS file composed of Footer/style.css,SummaryCard/style.css, andTooltip/style.css` and inject that one time into the page template.

This would provide the cleanest CSS per-page and also would keep that nice colocation DX + eliminate the namespacing issue. Ideally this would be done automatically, as the import <component> stage within index.tsx.

We also wouldn't need to import global styles into every component if we're building the page CSS together with the components at the time the page builds. We'd just import it once into there.

Anyway, those are my thoughts -- let me know if that makes sense or if that's confusing. TLDR: the page builds the CSS based on the components being imported and their dependancies + the global CSS

una commented 4 years ago

Lightweight critical CSS solution from @developit for future reference: https://gist.github.com/developit/0ccc1927079c52d8c618b1e93bcfb644