thheller / shadow-css

CSS-in-CLJ(S)
https://github.com/thheller/shadow-css
Eclipse Public License 1.0
114 stars 10 forks source link

[Proposal] Dev mode with pre-generated classes for better DX #27

Closed Ramblurr closed 2 weeks ago

Ramblurr commented 3 weeks ago

First I absolutely love shadow-css, it is an amazing tool and lets us a bloated node/npm build step.

shadow-css' current css generation technique (unique class names based on line and col) is really great especially for production.

However, during development, this approach presents some friction points in the developer experience:

  1. When using browser devtools to experiment with styles, we can't easily add/remove Tailwind-style classes to the class attr and preview the changes.
    • This means a dev has to think in both tailwind-style utility classes and normal-css, which is a minor, but annoying context switch.
    • It is a common practice to open the devtools and twiddle with the utility classes until you find what you like and then copy and paste the result back into your source code. This can involve using utility classes that aren't otherwise used in the codebase (so they might not have been generated)
  2. The css build cycle -> page refresh loop can become slow. especially in remote development scenarios where generated CSS files need to be synced to servers.
    • I admit this is more uncommon. But I often edit locally and rsync assets to a remote server/machine where my REPL and HTTP process is running, and I use a file watcher to sync changes automatically. It often happens I make a change -> refresh and end up with broken css because the rsync didn't complete yet.

I'd like to propose adding a "dev mode" option to shadow-css that would:

Naturally, this mode should only be used during development.

This would improve the developer experience by:

Would you be open to a PR implementing this feature?

thheller commented 2 weeks ago

I'm a very strong advocat of keeping development as close as possible to production. Changing how CSS is generated/used completely seems like a bad idea.

I see what you are trying to address, but the two main points you listed have never occurred in my typical workflow. So maybe adjusting a few things to get something halfway would be decent enough.

1) For me this has never been an issue since for the most part I use CLJS with hot-reload. So adjusting the source directly and seeing the change applied within less than a second is generally faster than switching to the browser devtools and editing things there. The same thing could be applied to server rendered HTML with very small adjustments.

2) That seems like an issue than can be addressed by better tooling. You could have the Clojure code building the CSS in the first place do the syncing. Too many file watchers in the process will always introduce latency, which can be easily removed by knowing exactly when to do what. Heck you could even have the CSS reload triggered automatically by this.

There are also limits to the things you can possibly do at the browser devtools. This isn't tailwind after all, so certain things cannot be expressed from just class strings. There is also a big difference in how the responsive and other utility qualifies work. In tailwind you have lg:px-4 lg:text-2xl whereas in shadow-css you have [:lg :px-4 :text-2xl]. Same for direct maps. These things are very common for me, so just using class strings would never work in any of my projects.

Technically there is nothing stopping you from writing your own build step function to generate all the aliases and emit those into the generated file. You could then switch out the macro you use. Given how simple it is thats not a big deal and the build tooling should still work as long as it ends up as a (css ...) form in the source files.

Ramblurr commented 2 weeks ago

Fair enough! I understand your point of view.

  1. For me this has never been an issue since for the most part I use CLJS with hot-reload. So adjusting the source directly and seeing the change applied within less than a second is generally faster than switching to the browser devtools and editing things there. The same thing could be applied to server rendered HTML with very small adjustments.

I'm always working with server rendered html, without auto full page reloads. Which is why I think I've developed this process.

2. That seems like an issue than can be addressed by better tooling. You could have the Clojure code building the CSS in the first place do the syncing

I already have the code generation (which uses a file watcher) call rsync at the end of it's build step. Even with persistent ssh sessions (ControlMaster/ControlPath) it's still slow.

Heck you could even have the CSS reload triggered automatically by this.

This sounds like a great idea! Getting the css files to hotswap wouldn't be too difficult, but if any line/col numbers changed then the html classes would be wrong. 🤔

I suppose I should investigate server side "hot reloading" and see what is feasible.

whereas in shadow-css you have [:lg :px-4 :text-2xl]. Same for direct maps. These things are very common for me, so just using class strings would never work in any of my projects.

I hadn't considered those, and of course you are correct. I also use those extensively so what I suggested wouldn't even be of use in those cases.

Thanks for the feedback!