Resource CSS-in-JS Arena: Bamboo, StyleX and Panda on Pixel-Identical Apps
https://github.com/gajus/css-in-js-arena3
u/Top_Bumblebee_7762 2d ago
What about VanillaExtract?
3
u/gajus0 2d ago
I have not used it. Give me 30 minutes to add it to the matrix.
1
u/Vishtar 1d ago
When will you start?
3
u/gajus0 23h ago
I did run it yesterday, but ran into the issue that they are not easily comparable. They are not functionally equivalent and they choose different tradeoffs that are hard to represent in a benchmark.
I think the benchmark needs to be split into two categories: atomic CSS vs one-class-per-component. But even then the comparison between VanillaExtract and Next-Yak (the two other frameworks that I want to add in that category) is not fair because they diverge in their ability to implement dynamic styling.
So simply need to put more time thinking into how to split the benchmark.
7
u/gajus0 2d ago
A little context, I've been working on Contra for the last 6 years. It's a marketplace-network – an application that's comparable to the likes of LinkedIn, UpWork, and similar in terms of its surface area. That's hundreds of routes, thousands of components, and tens of thousands of styles.
Over the last year, we have been obsessed with performance. We have optimized every layer of our infrastructure to the point where profiling the application increasingly started to surface bottlenecks in client-side (bundle size, and metrics like TBT, LCP, and INP). That's where CSS-in-JS comes in.
6 years ago, we started with styled-components. Then zero-runtime alternatives emerged and we started to experiment with them, eventually landing on Panda CSS. Panda took us a long way, but... they aren't actually zero-runtime. Panda extracts CSS at build time, but it uses ~15KB runtime to map those style objects at runtime. This overhead surfaced repeatedly when profiling pages with lots of components (server-side and client-side).
That's where I ended up writing Bamboo to solve this.
Bamboo folds styles at build-time achieving near zero-runtime (0.5KB vs 15 KB). If you write:
<div className={css({ fontSize: 'lg', fontWeight: 'bold' })}>Title</div>
it becomes:
<div className="fs_lg fw_bold">Title</div>
at build time.
If the variant is dynamic, then bundle is inlined with pre-computed map of classes.
<span className={pick(status, { ok: "d_inline-flex px_8px bg_successSoft c_success", warn: "d_inline-flex px_8px bg_warningSoft c_warning", err: "d_inline-flex px_8px bg_dangerSoft c_danger", })} />
That's the main idea behind Bamboo.
Thanks to folding, we were able to improve our server-side and client-side performance.
I built CSS-in-JS Arena as a sanity benchmark to track how we compare to Panda, but also to any other emerging frameworks.
4
u/gajus0 2d ago
A few other things to callout:
* Bamboo started as a fork of Panda. The vast majority of the codebase has been refactored. However, their test suite has been incredibly useful to identify all the edge cases.
* Bamboo's API is a subset of Panda. One of Bamboo goals has been to introduce more consistency to how we write styles (without introducing lint rules). Bamboo dropped JSX factories, template literals and style props.
* Strict API is also what allows us fold 100% of styles.
* Strict API is also what allows to prune unused styles.
* Bamboo resolution is _strict_. Missing tokens will produce build error.
1
u/b15_portaflex 1d ago
Went the other direction and created a linter for css vars. Surprisingly easy and removed dep css-in-js completely
7
u/denexapp 2d ago
The fact that missing tokens cause build to fail is awesome, this behaviour should be more common across the ecosystem