Hacker News new | ask | show | jobs
Show HN: Compiled, buildtime atomic CSS in JavaScript and all your favorite APIs (compiledcssinjs.com)
47 points by madou 1995 days ago
13 comments

Yet another case of documentation saying “JS” when they mean “React, specifically”. Every single example I can see here uses React, but if this deserves the CSS-in-JS moniker, please give at least one example of usage with vanilla JS. It’s like peak jQuery all over again.
We're probably at that point again where a developer 'knows' React but will completely flounder when faced with anything outside of that ecosystem. Same with jQuery, same with Rails and Ruby.
I see it all the time.

Tons of developers coming out of bootcamps (and even a few designers with no coding experience) who know how to string together a few react components but have problems with basic JS.

It's not necessarily bad, it's just higher level coding. What's bad is when these people get hired as engineers.

I've started seeing this quite a lot recently when working with beginners. Worst one recently was someone who had extreme difficulty understanding how to make different web pages using only HTML, but much more mild versions of this have been cropping up in frequently for me over the last year.
Since JavaScript evolved to incorporate many of the APIs that jQuery pioneered at some point knowing only jQuery was not such a problem.

But even before this evolution much of jQuery was just sugar to make cross-browser problems manageable.

I am not actually aware, I think, of any developer that just knows React but if they exist I suspect they would have an even harder time of it when deprived of their favored framework than those developers back in the day that just knew jQuery.

Query selectors likely came from jQuery's Sizzle engine, but most of the jQuery APIs, all methods, were never adopted. The best I can think of that did get adopted is closest which uses a query selector to find a matching ancestor node.

The reason why most of the jQuery approach is not adopted is because it is so incredibly slow. First you have to consider that query selectors, at their best performance (Google Chrome), are about 460x slow than the old DOM methods. All the jQuery approaches would be slowness that multiplies on top of the already slow query selectors. In Firefox query selectors are about 10,000x to 250,000x slower than the standard DOM methods.

From a standards perspective there is no incentive to go down that path as it cripples the interface it describes.

I was pretty much thinking just of query selectors as I think that would be the most problematic thing for a jQuery trained developer to do without.
The differentiating feature of Atlassian’s @compiled package is not the “build time atomic css” - it’s that you can distribute components that use @compiled on NPM without requiring your consumer to futz with Webpack include paths for e.g. CSS files. Do I have that right? @madou is the extraction shipping yet? The docs have a few places that say “coming in 2021” - what’s the status?

For those interested in build-time CSS in JS, also called Zero-runtime CSS in JS, you might also want to check out:

- Linaria (https://github.com/callstack/linaria) I use on my personal site. I ran into trouble/bugs trying to integrate it with Notion’s build process; the Webpack/babel bits are very brittle.

- Treat requires that you write the JS that computes your styles in esperare xxx.treat.js files which is annoying from my point of view - I want single file components. (https://seek-oss.github.io/treat/)

- No idea about this one, but it’s more recent. Has some other interesting things in the inspiration section of the README https://github.com/CraigCav/css-zero

Have also been using Linaria on a side project recently, enjoying it a lot so far. I skipped most of the other css-in-js solutions when I saw they where using runtime js to manipulate styling which intuitively just feels unnecessary for 99% of the pages style. Very happy with Linaria so far.
I’ve been working on one that runs on top of React Native Web. Compiles to atomic CSS on web, and StyleSheet on native.

It marries in-line style props (like JSXStyle) with SwiftUI style stack views.

It’s very much alpha, on my GitHub.

So you took something written in a regular grammar and made it into the same thing except with a very complex context free grammar with support for recursion?

I hate to be a grumpy puss but using this sounds like a step backwards.

Well, in the long run we’ve learned that maintaining a large and complex UI’s CSS by hand is very tedious and error-prone. A miss configured style that, for example, makes the Airbnb “Book It” unreadable for some % of the population can lose millions of dollars, but never throw a stack trace.

So, engineers working on UI toolkits often reach for CSS code generation to ease maintenance. At first it was all PHP templates. Then it was Sass (206), is a popular purpose-built language for generating CSS which saw a lot of use in the Rails community. Sass is very good at its job because it handles a lot of CSS niceties in nice ways, but it’s a lot of extremely domain specific complexity. A whole new Turing-complete language JUST for CSS? Not ideal.

The next step is to move all the UI definition into a single place, i using a single language that abstracts the underlying HTML/CSS/JS files & semantics. We do that by writing JS, and having a compiler program extract the CSS bits. This gets us ideal performance & ideal maintainability at the cost of build complexity. This is a worth while trade, because in the long run the compiler will get so good that the cost becomes minimal; compare to other ecosystems, for example the Kotlin compiler is quite complex, but I never think about x86 assembly when writing Kotlin.

Then you go back to HTML with attributes to control style, which is where we were before CSS.

It's good to have a level of separation between style and content. The problem with HTML and CSS is that often times you need to change your HTML to change the style of your application.

This is because HTML+CSS was meant to be for documents. It took ages to evolve into what we have today and it still has a lot of baggage. Designing apps with it is a hack which became mainstream.

If we want to improve the situation, I think we need a couple of standards to define application content and application style which can be compiled to HTML + CSS (as browsers are probably going to stay around for a bit longer).

Component based libraries (like react) are well placed to do something like this and it would help developers faced with this uneven separation of style, content and markup.

I view the current Cambrian explosion of CSS-in-JS approaches as a distributed search for the “new standard”. Better to have 1000s of developers working on the problem then have 6 people on a committee make another bad, short-sited standard that becomes “cruft” in 4 years.

If you views React’s JSX as the “content” part of content/style break, then we’re not too far off. It compiles down to HTML :)

> The next step is to move all the UI definition into a single place, i using a single language that abstracts the underlying HTML/CSS/JS files & semantics.

That sounds like a gigantic step backwards, and one which does not follow the example you've mentioned.

The point of Sass was to introduce backwards-incompatible changes to CSS which adds nice human-readable syntactic sugar to CSS. Being turing-complete is besides the point and even it's regular use. In fact, I worked in projects where Sass was introduced just for it's support for nesting and partials alone.

Mixing content and presentation in scripts misses any of the lessons from the last couple of decades. It feels like yet another example of the cargo cult of javascript, where people mindlessly argue that dragging everything into a script solves anything at all.

> Mixing content and presentation in scripts misses any of the lessons from the last couple of decades.

Agreed - although this is not a JavaScript thing, but a react thing I think (maybe also Vue? No idea about that). E.g. Angular still has CSS (actually SASS) and HTML in separate dedicated files, and then a another separate file that contains the JavaScript (actually typescript) for the logic itself.

I do not see the benefit at all of putting all these things in one single file, apart from trivial tiny simple things (angular allows you to create single-file components for instance) or for making "To-Do with React" type tutorials look easy. Once you get more than 100-200 lines of code + html + CSS then it is time to separate the files.

I would rather have my files be Page.component, Header.component, Details.component, Article.component than have my three files be Page.html, Page.css, Page.js.

What is the benefit of separating code by file type? If your reasoning is to reduce coupling, I think this is false simplification - again, in complex applications with lots of interaction, there is no escaping the coupling of DOM nodes, JS logic, and styles. Any feature changes change is going to need to modify all three. Maybe for a mostly read-only site de-coupling these things makes sense, and you can achieve your goals with something like Bootstrap, where you compose a bunch of reusable CSS classes. But for the majority of work I’ve done over the last 8 years, that decoupling model doesn’t work. It’s an even harder nest of snakes when you multiply 3 screen sizes x 2 input methods x N pages where CSS could be used - again for something simple, it’s fine to just collapse columns on mobile (the typical CSS column-css-6 style) but frequently you want this stuff to be much more responsive to user device and input than decoupled styles allows.

> I would rather have my files be Page.component, Header.component, Details.component, Article.component than have my three files be Page.html, Page.css, Page.js.

...except that Page.css at most only adds minor tweaks that cascade over the site's styling, and arguably already should be a part of the site's main styling definitions.

> What is the benefit of separating code by file type?

Separation of concerns. This lesson was learned the hard way. Must we keep on relearning it?

> I think this is false simplification - again, in complex applications with lots of interaction, there is no escaping the coupling of DOM nodes, JS logic, and styles.

This assertion is quite wrong and misguided. Styling is a property of a site's visual identity. It is not a property of a component or a specific interaction state. Any well-designed site follows a uniform visual identity which spans over all aspects pertaining to user interactions. Moreover, UI components are also uniform and reused. There is no excuse.

React doesn't require mixing styles and content. You can use regular CSS files just fine.
> Mixing content and presentation in scripts misses any of the lessons from the last couple of decades

If you're using React or Vue, chances are your content is coming from JSON in fetch requests, etc, not from the HTML/JSX in your components. In those cases HTML/JSX is the presentation. The content/presentation separation still exists.

Personally I'm a fan of cxs [0] which is (or purports to be) atomic among other strengths. From the readme:

  - 0.7 KB (gzipped)
  - Zero dependencies
  - High performance ("fast af")
  - Deduplicates repeated styles
  - Dead-code elimination
  - Media queries and pseudoclasses supported
Works with any framework or with none, apparently, although I've only used it with Preact. Supports themes. Everything I need and nothing I don't.

[0] https://github.com/cxs-css/cxs

I think csx is probably the best choice amongst the compute-styles-at-runtime CSS-in-JS libraries. The brutal simplicity has a lot to offer. For most projects csx is perfectly fine. However it can’t beat a more complex zero-runtime precompiled styles system on paper — but it’s an optimization tradeoff you could choose if you need it in the future.
There are already quite a few alternatives in the comments, but I’ll just add that I have really come around to the Tailwind way of doing this. On my current project, I haven’t had to write much CSS at all other than a handful of animations, and it all looks consistent and custom. It’s a breath of fresh air.
Is tailwind similar to the toolkit Compass was providing back in the day?
I was building a similar solution 4 years ago [1] but got stuck with integration to Webpack. I’m very glad someone succeeded to do so. [1]: https://github.com/iddan/stylesheet
Doesn't @emotion's babel plugin already do this, or am I missing something?
Not quite. Emotion doesn't do the atomic splitting - ie. one class name per style rule. This splitting results in ugly looking HTML but if two components are using some common subset of CSS rules they will partially reuse the same css classes.

It is closer to https://www.styletron.org/ in that regard.

What was wrong again with runtime CSS? By the smell of it seems to be performance, so: can somebody point to performance comparisons, or give a brief of the advantages?
There's nothing wrong with runtime CSS. Frontend devs just like to come up with elaborate solutions to problems that don't exist
From my understanding of frontend performance, JS parsing is blocking.

So reducing the JS "load" is a performance win on the client.

It’s all about time to interactive.

For a big complex app (eg, https://www.notion.so which I work on), a significant fraction of JS code size is spent describing styles. There can also be JS cycles spent doing color math like lighten, darken, hue shifting on base colors, etc. With runtime CSS-in-JS, all of that style JS code must be parsed and executed by the JS runtime on every page load by every user. That’s a lot of CPU time and user wait time multiplied out.

Zero-runtime improves page load performance by moving this extra code from the JS vm to static CSS files. The majority of style computation happens at build time. The CSS can then be downloaded and parsed in parallel with the JS (woo threads?). The separation may improve caching compared to all-JS solution as well, depending on how granular the CSS and JS output files are.

Oh!, now I get it: CSS static was not good, so welcome JS generated CSS.

But that was not good either, so welcome buildtime JS generated CSS.

Brilliant!

Yes, best of both worlds
hamburger emoji for a menu icon is for sure an underused design pattern
Indeed. But it will take of now. Everybody wants to click that delicious hamburger.
Could someone please explain the following line to me?

export const Button = styled.button``;

Why is there a string right after the field "button" of the object "styled"? How is this valid JS? What does it do?

checkout javascript "template literal tags"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Thanks! So it's a special way to call a function, with the functions arguments being the parts of the given template literal string as split by the template literals and the template literal values themselves?

JS syntactic sugar complexity is getting out of hand.

I don't know why that space is there, it could be accidental or a deliberate stylistic quirk of the author, but it seems harmless (if not idiomatic).

> How is this valid JS? What does it do?

I'm curious if Python (or a similar whitespace-sensitive syntax) is your first/familiar programming language.

The extra whitespace here does nothing.

The semicolon in JS is an (optional) expression separator, similar to in Bash/Shell. It can occur at the end of a line or at the beginning of a line, or in between lines. Or in between two expressions on a single line. Any whitespace chars before or after it are irrelevant/ignored.

It is not clear how this response is meant to answer the question in the post you replied to; there was no mention of whitespace.

Their question was:

>Why is there a string right after the field "button" of the object "styled"?

which unambiguously refers to the double backtick characters between "styled.button" and the semicolon in this expression:

> export const Button = styled.button``;

That is, in JavaScript, an empty string. There is not whitespace and Python has nothing at all to do with the question. Similarly, an explanation of what a semicolon does in JavaScript does not answer the question.

I also can't answer the question for them as I haven't kept up with newer JavaScript standards, but I thought it important that you realise you misunderstood something here.

Possibly a HN formatting issue (or the commenter typed wrong first time and later edited) but the backticks weren't showing up previously.

Sorry @Traubenfuchs for the misguided explanation.

As another commenter posted, this is a template tag. Technically equivalent to doing something like styled.button('') but the template tag syntax would be used for consistency with other calls in the code (where parameterised template strings would be passed to the button/element/component method)

Isn't this the same as Styletron is doing? https://www.styletron.org
Styletron create “atomic” (single property) CSS classes as your application runs in production because it has no macro, compiler, or bundled plugin. “Build-time” means the CSS is somehow extracted from the JS files and inserted into HTML or static CSS files during the bundling process, so that the JS to compute the styles never runs on the client/production. There’s only whatever code is needed to add the generated class names to the DOM nodes created by React (etc) when the app runs.
why is there such a prevalence of mawkish phraseology in the javascript community...

i was reading the landing page for a js library and it said something like "battery-pack included", makes me feel kind sick....or maybe i'm just a grumpy sod

"Batteries included" was invented by rails folks IIRC.
why? css exists because one shoud not need js just for styling! js is dangerous for me!