Don’t get me wrong. I’m a big fan of CSS. It does what it does really well, and you can’t beat the backward compatibility of web standards. Sometimes, though, I get a little annoyed with the way some of the more modern features are implemented, and with some of the missing features.
For most of the time I’ve written CSS I’ve used some sort of preprocessor, like Sass or PostCSS to handle these shortcomings. These usually have their own sets of downsides, like some weird language rules in Sass’s case or leaning on the standard in ways that mean annoyances aren’t really fixed.
@media. It’s annoying and has been a problem for a while.
This isn’t a new idea. CSS in JS is obviously a thing, but that usually implies having component code with CSS right alongside it in some sort of JS based syntax. There’s also JSS which is a library for writing CSS with JS.
It’s pretty trivial to iterate that array and convert it into some CSS. I pretty quickly realized it was nice to not have to quote properties, so I wrote in support for converting property names in camelCase to kebab-case.
I also decided that numbers should get a
px added after them, since I tend to type
px a lot when writing CSS, so that this is valid:
I knew I wanted nesting, and since I was using Esbuild to post-process the generated CSS anyway, I was able to lean on it to handle browser unnesting (since browser support isn’t quite there on nesting). Building nesting support was pretty trivial. As I iterated style properties, if the value of a property was an object I assumed it was a new nested scope:
The nice thing about this model is that it gives you some nice tricks for code reuse. Want to factor someting out into a variable? Feel free:
In this case the
red500 variable is doubly nice compared to a css variable because it’s a good bit shorter than typing out the variable (it would have been
var(--red-500), and most editors give way better tools for refactoring JS variables than CSS variables.
You can still declare CSS variables within this syntax, and even shorten them for yourself while writing them:
You can also factor out and reuse whole sets of properties:
It’s even easy to define reusable media queries:
The default export being an array is handy for expanding imported rulesets without having to worry about collisions:
All in all, I thought this was a fun little experiment. What do you think?