Learn Modern ClojureScript

CSS in ClojureScript: Why it might be a bad idea?

Over the years I used many ways of authoring CSS in web projects, from writing CSS in a single file to using purely runtime CSS-in-JS libraries. A common approach that I've seen in most projects is to have CSS file per corresponding JS file and serve them as a single bundle. This works well, and you are free to choose any CSS preprocessor (SASS, Stylus, etc.). Often times handwritten CSS split into modules is just enough. This approach works regardless of what programming language you use on the fontend, since styles are processed at build time and linked to HTML at runtime via handwritten class names.

Then there's a different approach, make CSS first class citizen in application code. There are two common routes to take here: CSS Modules, where you still keep stuff in CSS files, but a build tool generates a mapping of class names to unique identifiers that you can import in your JS code. Or you can go full CSS-in-JS, where you write styles in JS files.

I think both CSS Modules and styles-in-code have their place, depending on how you build software. As a solo engineer, I prefer to have my styles in ClojureScript code. I like the idea of having everything in one place, I can easily refactor styles, and add dynamic visuals to my UI. Majority of such libraries if not all of them also generate unique class names. I actually never really felt a need for scoped styles even in projects with relatively large codebase and engineering team. All I really want is to keep using inline styles and leverage full power of CSS.

Unfortunately, inline styles do not support typical stuff like nested selectors, pseudo-classes and media queries. And for me that's the real benefit of using CSS-in-JS libraries. Some years ago I built cljss, CSS-in-CLJS library inspired by Emotion, and today in 2024 I created uix.css, based on same principles, but with smaller API surface and a smarter compiler. Am I happy with it? Most certainly. Do I recommend using it? Well, it depends.

As an engineer, I love how uix.css looks in code and how it works under the hood. And what, you get source maps from CSS to ClojureScript right in your browser? What the heck!

I'm using the library myself in personal projects and I can see how it can be used in a team of experienced engineers. However, I believe that authoring CSS via data DSL is not the best approach. When you switch to data DSL, you immediately loose access to essential CSS tooling that normally is available for CSS in any code editor, things like syntax highlighting, linting, and auto-completion. In JavaScript world CSS-in-JS has all of that via editor plugins, that enable CSS tooling contextually, for blocks of CSS written as JavaScript strings. This is not common in Clojure world, where most embedded languages are expressed as data. One thing that I miss is being able to copy and paste styles from DevTools into my CSS file, can't do that with data DSL.

However, with adoption of LLMs like GitHub Copilot, this problem seems to be going away, at least partially. Often times I find Copilot autocompleting my CSS-in-CLJS code correctly, reusing design tokens like colors and spacing. I'm terrible at remembering syntax for complex CSS rules, like gradients, and Copilot helps me with that. This is huge, since suddenly contextual autocompletion doesn't depend on language-specific tooling.

So, is it a bad idea to use data DSL for CSS? I think not so much, but again, think about your team and who are those people that will be editing CSS code. Maybe they are designers who are not familiar with Clojure, be prepared to teach them basics of it. That's a great idea actually!