What is Truss
Truss is our niche CSS-in-JS library that I do not expect anyone else to use đ , and only exists because:
- We started a large React SPA in 2020 before Tailwind won, and at the time preferred Tachyons syntax (shorter atomic class names)
- Truss v1, built as a small DSL on top of Emotion, supported very robust cross-component library / application boundary styling that our apps still leverage
- When StyleX came out, Truss v2 cribbed its architectural approach, and is now build-time CSS like all the other cool kids đ
- Now AIs are smart enough to write React UIs in Truss as well as Tailwind đ , so our productivity is still up. đ
And also we just really like it. đ
Benchmark Results
Anyway, on Reddit I recently saw a CSS-in-JS benchmark go by, written by one of the authors of Bamboo CSS, which is a Panda-like, build-time CSS-in-JS framework (if Iâve got that right).
I naively wondered âhow would Truss do on this?â đ âŚturns out quite well!
Our fork is here for you to click through the details, but the two key tables are:

We got a lot of medals! And swept the most important rows for CSS size:

Honestly I did not expect this result, because Truss has always prioritized DX for maintaining large, complicated applications & underlying component libraries, more so than âthe smallest possible outputâ.
Such that even if Truss had lost by ~10-100% on any of these axes/tests, I would likely have asserted the results were too small to matter, and just keep using Truss anyway. đ
So itâs a convenient surprise that we actually do pretty well. :-)
Why Did We Win?
The explanation for why we win, particularly against S-tier optimized libraries like StyleX, is very simple: our class names are shorter than everyone elseâs.
I.e. while StyleX hashes its atomic class names to .x1u7kmwd because âthat is the correct thing to do at Facebook scaleâ (or something like that đ
), Truss leans into our Tachyons abbreviations like Css.df.mt2.$ that already have to be unique and so just outputs class names like df mt2.
And thatâs it â nothing actually that magical. đ¤ˇ
AND ALSO TOTALLY WRONG! đ¤Ś
Our names are shorter than Bambooâs and Pandaâs, which average ~16 characters because they encode the property & value directly into the name.
But StyleXâs hashed names average ~7 characters, and Trussâs names average ~9. So after our initial âcutely shortâ mt2 class names, the rest of our semi-human-readable class names end up longer overall.
So the real reason we win? Our even longer class names actually compress shorter because, being semi-human-readable, they have less entropy. đ¤Ż
I.e. StyleXâs hashed names are essentially âtoo randomâ, and donât compress as well as Trussâs semi-human-readable abbreviations that repeat a lot of the same patterns & prefixes.
We confirmed this by renaming the class names in both benchmark output files to the same random 8-character hashes, and re-compressing: once the naming scheme is the same, Trussâs ~20% compressed lead shrinks to ~1%.
I will admit I had no idea this âuse mt2 for class namesâ would positively affect compression size when starting Truss v2âit just seemed like a neat idea, and honestly I was doing it for better DX (seeing mt2 in Chrome DevTools). That we got better compression as a free bonus, I did not even realize until writing up this blog post. đ
CSS Specificity Tangent
I originally went down an âALSO WRONG!â rabbit trail about how StyleX vs. Truss output sizes were different because of their different handling/encoding of CSS specificity rules. But that was also a nothingburger.
Truss purposefully uses/steals StyleXâs specificity approach nearly verbatim: we categorize every CSS property into the same CSS shorthand vs. longhand tiers StyleX uses (i.e. an atomic class name setting margin-top should override a class name setting margin, which the browser wonât necessarily do by default).
That said, we use the assigned priority differentlyâStyleX encodes the priority into the selector itself (either via a @layer or the repeated :not(#\#) hack), while for Truss we (maybe naively) lean into total control of output order, and just sort the stylesheet by the priority order, so the last definition wins.
(Although weâre not entirely hack-free eitherâTruss doubles the class name, i.e. .sm_g0.sm_g0, on media query rules, exactly as StyleX does.)
Initially I thought this difference in âselector encodingâ vs. âoutput orderingâ mattered, and that it was what nudged Truss ahead of StyleX in terms of lower output size.
But even StyleXâs :not(#\#) hack (which might be repeated 1-7x per rule, depending on the propertyâs tier, effectively acting as a @layer polyfill), compresses very well. Specifically, there were 1,440 copies of :not(#\#) in StyleXâs original benchmark output, but dropping them all by enabling the useCSSLayers flag saved a grand total of 70 bytes.
I.e. as a mental model takeaway, the same 10-character string repeated 1,000s times in a file ends up, post-compression, being essentially free.
What about Tailwind?
Just for kicks, I also added Tailwind to this same benchmark, in this branch, and we lose a few medals:

But we still win all the output size metrics, where Tailwind is one of the laggards:

Honestly I havenât taken the time to ask the LLM âwhy is Tailwind a laggardâ, when in theory itâd use ~relatively similar âlots of repeated patternsâ names like Truss, and so should compress really well. Itâs easy enough to âjust ask the LLMâ, but then very hard to trust/audit that the answer is accurate â i.e. my earlier CSS specificity tangent was directly from me trusting an overly-confident LLM on its first few assertions.
The medals we lost were to build-time/dev-time metrics, where the Tailwind compiler is ~10-30% faster than Truss, but on small enough numbers that 𤡠I think itâs a wash.
Disclaimer, I did try & benchmark hack our build times to beat Tailwind, and we got closer đ, but couldnât actually pull ahead, at least with the current Babel/JS pipeline. Maybe next hack day! đ