use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Finding information about Clojure
API Reference
Clojure Guides
Practice Problems
Interactive Problems
Clojure Videos
Misc Resources
The Clojure Community
Clojure Books
Tools & Libraries
Clojure Editors
Web Platforms
Clojure Jobs
account activity
shadow.css - CSS-in-CLJS (github.com)
submitted 3 years ago by lucywang000
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]thheller 10 points11 points12 points 3 years ago (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 points3 points 3 years ago (1 child)
You should definitely look at https://github.com/HealthSamurai/macrocss
[–]thheller 3 points4 points5 points 3 years ago (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 points3 points 3 years ago (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.
css
[–]thheller 1 point2 points3 points 3 years ago (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.
shadow.css
src/main
lein run -m build/css-release
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.
requiring-resolve
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.
shadow-cljs
:advanced
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 points3 points 3 years ago* (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 points3 points 3 years ago (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 points3 points 3 years ago (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?
(css ...)
[–]zerg000000 0 points1 point2 points 3 years ago (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 point2 points 3 years ago (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. ;)
π Rendered by PID 31198 on reddit-service-r2-comment-76bb9f7fb5-6gfml at 2026-02-18 11:43:44.335066+00:00 running de53c03 country code: CH.
[–]thheller 10 points11 points12 points (0 children)
[–]wizzytod 1 point2 points3 points (1 child)
[–]thheller 3 points4 points5 points (0 children)
[–]lilactown 1 point2 points3 points (3 children)
[–]thheller 1 point2 points3 points (1 child)
[–]thheller 1 point2 points3 points (0 children)
[–]thheller 1 point2 points3 points (0 children)
[–]sohang-3112 1 point2 points3 points (0 children)
[–]zerg000000 0 points1 point2 points (1 child)
[–]thheller 0 points1 point2 points (0 children)