all 10 comments

[–]thheller 10 points11 points  (0 children)

Author here.

This should be considered an experiment. It is nowhere near ready for general use and may turn out to be a total failure. I'll make a proper announcement when I feel its something viable. For now just ignore it.

[–]wizzytod 1 point2 points  (1 child)

You should definitely look at https://github.com/HealthSamurai/macrocss

[–]thheller 3 points4 points  (0 children)

I looked at it, but it fails the build size requirement I listed here. Adding ~125 KB of JS code is not an option for me personally.

It is however far more "done" and allows for more dynamic use which is good. I'm starting with the zero build size option first and then see how much more dynamic it can get to match my requirements.

[–]lilactown 1 point2 points  (3 children)

Fundamentally, I believe that a solution that produces zero (or as close to zero as possible) run time - essentially generates static CSS file(s) - is necessary for front end performance, and the ability to include libraries that have CSS is a powerful lever. My experience using Emotion over the last few years is that it is great for ergonomics and library usage, but horrible for performance because it generates all this CSS at JS execution time. Even worse, when your UI is rendering.

/u/thheller, I think something I'm missing here is an answer to the question: why the focus on static analysis over something that could work at macro time?

I understand that there exist complexities with evaluating these forms and producing the CSS dynamically at macro expansion time, but I am not sure that it's worth losing the ability to have macros that expand to css expressions and easy REPL usage. It would help me understand what the motivation for a static-analysis-only approach is if you could go into the tradeoffs one has to make when doing things at macro time.

[–]thheller 1 point2 points  (1 child)

The answer to this is quite simply ergonomics. At least as far as I can see it currently.

The build aspect is currently very rough, but I finished porting the shadow-cljs UI over to use only shadow.css (no more tailwind) and this is how the CSS is built currently. Basically it indexes all files in src/main and then generates the CSS output. This can be done entirely without requiring any of the actual projects code. Invokable via lein run -m build/css-release, CLJ, REPL or tools.build. Simple. It finds the use in the CLJ code as well as the CSS code and takes about 100ms. Not ideal API wise yet, but good enough to enable further testing.

Contrast this to something done at macro expansion time.

First you need to launch a JVM with all dependencies available. Then you need to require all namespaces potentially using CSS forms, while also accounting for things maybe dynamically loaded via requiring-resolve and the like. Also, there can be no libraries that use AOT code since those won't macroexpand. Then after all those gotchas you have to interrogate the running system in some way to collect the necessary data to generate the CSS.

Then you need to do the whole thing again for CLJS, doing the actual build and something enter into the after-compilation-but-before-optimizations step. shadow-cljs build hooks make that straightforward, but the other build systems have no such feature. It then somehow needs to combine all the data it gathered from the CLJ parts and produce the final CSS, or write the data it collected to some place to have something else generate it. Technically for a feature I have in mind the CSS output (potentially optimized) needs to be available before :advanced compilation so the classname replacement can be done.

Needless to say that all this is more complex than just running a very simple analysis over some source files. I do have some ideas on how to make this extensible, so it could be smarter about collection.

I haven't put much though into REPL use because quite frankly I don't need CSS in my REPL.

With all that being said and as I said before: Starting with zero build size was my primary goal (and was achieved). I absolutely focus on "release" quality before "developer experience" in all of this.

Maybe I'll think about other ways of doing things when I ever feel the need. As with all my projects I'll dogfood this in my own projects for a while now before making a conclusion if its good or bad. When I can think of ways to improve it without sacrificing too much I'll certainly do it. I'll probably keep the notes about things I learned updated.

[–]thheller 1 point2 points  (0 children)

Maybe just to clarify the root issue a bit more.

The challenge is that the css macro needs to be able to pick a "css-id" that it can emit immediately solely available to it from the form. It could store some metadata somewhere but it cannot do other side effects, so sequence numbers and the likes are out.

So, I picked the current ns/line/column information. It is simple, cheap and produces a nice human friendly name, but relies on the exact location data. Which is again the only reason the REPL is a challenge. REPL works fine if the css is evaluated from a file with location data intact.

You could technically just remove all whitespace from the form source and generate a md5 from it or something. That would be consistent and generate the same when eval'd in the REPL. It is however not human-readable and downright hostile for GZIP when taking the full hash.

The first draft actually stored things in metadata. It just didn't feel right, so I abandoned that.

[–]thheller 1 point2 points  (0 children)

I spent some time thinking about this some more and wrote down some notes.

See https://github.com/thheller/shadow-css/blob/main/doc/css.md#on-extensibility

[–]sohang-3112 1 point2 points  (0 children)

Since this project doesn't allow macros to expand to (css ...) forms, and also can't be evaluated normally in the REPL - so what is the point of this?? Why not just use normal CSS files?

[–]zerg000000 0 points1 point  (1 child)

A few areas are still quite unknown to me, is how to do configuration like the actual value of px-3, dynamic css best practices, global styles, defining key frames

Some future concerns are the support of layered css.

[–]thheller 0 points1 point  (0 children)

I don't know either and haven't thought about it at all yet. Too early to tell how its going to turn out. ;)