Notes
Blog

Appearances
OSS
Published December 7th, 2020
Tailwind is an atomic CSS framework that has taken the frontend world by storm. It gives developers without a deep understanding of design the ability to build visually gorgeous, modern user interfaces.
If you have not seen it before, here is the canonical Tailwind example from their original homepage:
Many people think Tailwind is cool because it uses atomic CSS. Here is the thing though: Tailwind is awesome despite using atomic CSS, not because of it.
Hear me out.
We have had atomic CSS frameworks for almost a decade but none of them have been as critically acclaimed as Tailwind. What makes it different?
The key to Tailwind’s popularity is the painstakingly constructed system of design tokens at the core of the framework. The system’s carefully selected constraints give developers just the right guardrails. They make it obvious whether a choice is good or bad by offering only discrete steps.
This does require some design taste, but most frontend engineers I know have developed that over the years of building user interfaces. Tailwind’s system lets them turn that taste into implementation without requiring a lot of design skill — it helps them cross “the gap”.
Tailwind’s system is a masterpiece of design. I, and many other developers all around the world, feel empowered by and love it.
The atomic CSS framework is basically a delivery mechanism that allows developers to apply the system to their UIs. It’s undeniable that it has a fantastic developer experience: once you get used to the custom vocabulary you feel like you are flying!
However, we have learned over the past decade that atomic CSS has downsides:
Users still have to add a separate setup for the custom CSS they inevitably need (coined “bailwind”). You cannot get by on just Tailwind in the real world. Not having a dedicated place for custom styles in the same system can cause maintenance issues down the line.
Due to file-size considerations, Tailwind does not include all variants (e.g. hover:, sm:) for all utilities by default. It leaves it to you to manually configure which ones you need for every single CSS property.
Atomic CSS is not ideal for performance. No tooling can extract the per-page critical CSS, so you end up shipping more CSS to the browser than necessary. The bigger and more dynamic the app, the more unnecessary code you will ship.1
Brent Jackson, the creator of one of the original atomic CSS libraries, said it best in his post on atomic CSS:

“This methodology was created before React was released and was intended for use in template-based user interfaces, including Rails and PHP. It was never designed for functional component-based UI and doesn’t take advantage of this new paradigm.”

“This methodology was created before React was released and was intended for use in template-based user interfaces, including Rails and PHP. It was never designed for functional component-based UI and doesn’t take advantage of this new paradigm.”
Now, here is the thing: you can have your cake and eat it too. You can use Tailwind’s marvelous system and fantastic developer experience without the downsides of atomic CSS.
How? twin.macro.
Let me illustrate. Here is the canonical Tailwind example built with twin.macro and React:
Unsurprisingly, the result looks identical — we are still using the same system after all. Even the code looks the same, except that we use the tw prop instead of the class attribute!
However, under the hood this automatically compiles the class names to the actual CSS they refer to (with the css prop):
You get to use Tailwind’s system and developer experience and take advantage of all the benefits of CSS-in-JS:

Extending your elements with custom styles is as simple as using the css prop, no extra separate setup required to “bailwind”:

<span class="token keyword">import</span> <span class="token string">"twin.macro"</span>  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span>   <span class="token attr-name">tw</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-center md:text-left<span class="token punctuation">"</span></span>   <span class="token attr-name">css</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`     &:hover {        background-image: url("/bg.png");     }   `</span></span><span class="token punctuation">}</span><span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span> 
React JSX

Extending your elements with custom styles is as simple as using the css prop, no extra separate setup required to “bailwind”:

You can use all variants in all combinations with all utilities allowing for even more expression within the system. Since twin.macro runs at build-time, you don’t have to worry about configuration or file size:

<span class="token keyword">import</span> <span class="token string">"twin.macro"</span>  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">tw</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sm:hover:first:bg-black<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">...</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> 
React JSX

You can use all variants in all combinations with all utilities allowing for even more expression within the system. Since twin.macro runs at build-time, you don’t have to worry about configuration or file size:

You get fully automatic critical CSS extraction and code splitting. Users will only load exactly the styles they need for the page they requested — nothing more and nothing less! CSS performance does not get better.1

You get fully automatic critical CSS extraction and code splitting. Users will only load exactly the styles they need for the page they requested — nothing more and nothing less! CSS performance does not get better.1
The ideal setup for both developer and user experience!
Lesson: use twin.macro to leverage Tailwind’s marvelous system and developer experience without the downsides of atomic CSS.
Rather than taking two steps forward with Tailwind’s system and one step backward with atomic CSS, let’s take five steps forward. Together.
CSS-in-JS automatically extracts the critical CSS for the requested page and inlines it into a <style> tag. That means the first-paint with CSS-in-JS will always be faster as it saves both an extra network request for the .css file as well as sending less CSS code to the client. While the Time To Interactive for small apps will be slightly slower (as the JavaScript bundle includes the CSS-in-JS library you use) once your app grows the network request for the larger CSS file can outweigh any CSS-in-JS library and can cause a slower Time To Interactive as well. 
Discuss on HackerNews · Discuss on Twitter · Edit on GitHub
Previous post
Margin considered harmful
About this place
Welcome to my personal website! I’m @mxstbr, a JavaScript Engineer from Austria 🇦🇹 in love with React and Node.
Social Stuff

source