Engineering

Atomic CSS-in-JS: twind

Headshot, Derek Foster
By Derek Foster

Turbocharge your style writing by combining two popular CSS patterns to ship the least amount of code, while writing the least amount of code. Learn how to use the twind compiler.

Arrow iconBack to all articles
Atomic CSS-in-JS
Engineering

Atomic CSS-in-JS: twind

Headshot, Derek Foster
By Derek Foster

Turbocharge your style writing by combining two popular CSS patterns to ship the least amount of code, while writing the least amount of code. Learn how to use the twind compiler.

CSS is amazing. It lets us bring variety and visuals to the web. While most developers view writing styles as a chore, I love writing CSS because of the power to control a web page's look and feel. But I do agree that writing styles often feels like a chore… So how do we remedy that feeling? Instead of tedious typing of selectors, rules, properties, animations, and all that CSS has to offer, how can we bring style writing back to the fun and excitement of creating on the web?

Style Writing

At its core, style writing has three primary concerns:

  • DX (Developer eXperience) - development speed, keep styles close to HTML
  • Performance - ship the least amount of CSS possible, e.g. minified and without unused selectors
  • Maintainability - styles are overridable, easy to switch themes if needed, don't have to think about vendor prefixes or what is/is not supported, etc.

Let's keep our top-level question in mind: how can we shift style writing away from a 'chore' and more towards visualizing what a web page will be? This question is entirely about the first of our principles, DX. Performance and maintainability are equally important in the grand scheme of software development, but when it comes to actually developing software in real time, it is an afterthought. Don't believe me? Donald Knuth said it best: premature optimization is the root of all evil.

The ultimate goal of this post is to explore a solution that will give you the most bang for your buck when it comes to those 3 principles above. Atomic CSS & CSS-in-JS on their own dramatically improve DX by way of abstracting performance and maintainability concerns for you. But even better than using one or the other is using both: Atomic CSS-in-JS. Before we deep dive into twind, "the smallest, fastest, most feature complete Tailwind-in-JS solution in existence", let's touch on the two technologies that are dominating the CSS landscape right now that made twind possible.

Atomic CSS: Tailwind

TailwindCSS is the most popular CSS framework to use for implementing 'Atomic CSS'. If you want more background on Atomic CSS, I recommend this article from CSS Tricks.

A utility-first CSS framework, Tailwind comes with shorthand class names you can use directly on your HTML. It works by injecting all of the pre-written class names into your .css files, and during build-time removes the class names you didn't use - immediately giving you the least amount of CSS needed for your project.

Maintainability here is a matter of preference. The most common argument against Tailwind (and all of Atomic CSS) is that your HTML gets bloated with extremely long lists of class names. If you want to reuse styles, this is not very maintainable because you will need to copy those lists to the new HTML structure. If you use component based frameworks like React, this isn't a problem; you can style a component using Tailwind, and then use that component wherever you want.

Lastly on Tailwind, it is completely customizable via tailwind.config.js (and even via postcss.config.js if you really wanna get crazy with the customization). In tailwind.config.js you can override the defaults used for class names, or even create new class names. This option is especially useful for implementing themes and enforcing them throughout your project.

Example

Here's a quick example of using Tailwind in a React project:

tailwind example Simple yellow box with 'hey' inside of it, styled with TailwindCSS

Considerations

Now that you are a bit more familiar with Tailwind, here are a couple of considerations when deciding to adopt this nifty piece of technology:

  • Steep learning curve/heavy investment upfront. It will take some time to get used to writing styles "the Tailwind way", but once you're familiar with the API, you will be flying through development. IMO, the best resource out there is this interactive cheatsheet.
  • Active development. TailwindCSS and tooling around it is constantly evolving and stable releases are very stable. Documentation is up to date and easy to work with. You will be in good hands going forward :) A cool new feature I like: the Tailwind Just-in-Time Compiler which speeds up development even more and enables all Tailwind variants out of the box.
  • Nice integration with text editors. Because the API is defined, there are plugins for all popular plugins to integrate with Tailwind class names. Even less typing!

CSS-in-JS: styled-components

On the other hand of our style writing journey is CSS-in-JS. We will be using the immensely popular library, styled-components. The main benefit of CSS-in-JS is that it allows CSS to be processed by JS; all of the libraries in the JavaScript ecosystem are available to use to optimize CSS.

More specifically, styled-components does automatic injection of critical CSS. This means that the CSS included on a page is only the CSS you need. This is similar to Tailwind only including the CSS you used in your HTML in the final file sent to the browser.

Another important benefit of styled-components is class name hashing: styled-components will automatically generate class names for you, ensuring that there is no class name collision. It's also a slick way to introduce a bit more 'security' into your app, since the class names are gibberish - potential bad actors don't gain any insight into your codebase/component structure.

The most impactful feature of styled-components is dynamic styling. Directly in JS, you can toggle your component's visual state based on whatever criteria you set. In plain HTML/CSS/JS, you would have to interact directly with the DOM to do this.

One feature that styled-components has that's not as clean in Tailwind is referencing other class names within your CSS. You can directly use a styled-component within other styled-components to target those styles, like this:

const  InnerThing = styled.div``

const OuterThing = styled.div`
	${InnerThing} {
		margin-bottom: 200px; // we can apply styles to \`InnerThing\`s that are children of \`OuterThing\`s.
	}
`

Doing the same thing in Tailwind is certainly possible, but you will have to introduce a class name to your HTML for only this one reason. Not ideal.

Example

Let's see that same example from the Tailwind section, but this time use styled-components:

styled-components example Using the tailwind cheatsheet linked here, I put the CSS rules Tailwind uses into styled-components

Note: unfamiliar with rem CSS unit? It stands for 'root em', and it's based off the font-size of the root element, which for all web pages is the html element. You can change it by setting the font-size of html in CSS, but 16px is the de facto standard for web pages.

Considerations:

  • Easy to learn. If you know CSS, you will be able to use styled-components very quickly. If you know any preprocessor like SCSS, you will be an expert right out of the gate.
  • Actively maintained. There doesn't seem to be active development towards bigger/new features, but the GitHub repo has maintainers and takes pull requests for any bug fixes/features people want to solve. I can't find the tweet, but I remember Max Stoiber, the creator of styled-components, tweeting that the team passed on maintaining the library to people in the community. If you find the tweet, or find my recollection of this to be incorrect, feel free to share with us by emailing developers@useanvil.com.
  • Decent integration with text editors. There are integrations with all popular text editors for styled-components, but the formatting is usually pretty tough since styles are within backticks (using the JS feature called a tagged template literal).

Atomic CSS-in-JS: twind

CSS-in-JS & Atomic CSS share one 'feature', which is never having to leave your HTML/JS file when writing styles. That by itself is a huge DX benefit, but we want the best of both! Especially when it comes to authoring styles: typing mt-4 is way quicker than margin-top: 1rem. Multiply that typing time-gain by a few hundred or thousand, and you are saving quite a bit of time.

twind enables that shorthand syntax while giving you the full benefit of JavaScript's processing power. Even better, if you are coming from TailwindCSS or styled-components already, the API will be very familiar to you.

On the CSS-in-JS side of things, twind ships a compiler to the browser to convert tailwind classes into actual CSS. By moving Tailwind from build-time to run-time, we are able to toggle visual states easily & effectively, like using dynamic props from styled-components.

If you want to server render pages (commonly referred to as Server Side Rendering, or SSR), twind supports that. It's a bit of a pain to set up, but that's only because the twind project is so new. As twind grows in popularity, I wouldn't be surprised if abstractions are made to enable this quickly and easily for projects that need it.

The last feature of twind I'll cover is feature parity with Tailwind, including variants. Variants in Tailwind enable responsive design, dark mode, hover states, and more. While some are enabled out of the box, not all are enabled for each class. Additionally, you might want to configure the out of the box variants to not be included at all. All of this back and forth with variants is directly tied to build size and build-time. Each variant enabled will multiply the total number of classes generated in Tailwind, as well as the number of classes purged at the end of the build.

Since twind ships the compiler, it's able to handle all variants on the fly. There's no need to statically generate class names, only to purge most of them if they aren't used!

There are many more features twind supports. If you're curious you can read more about them here: https://github.com/tw-in-js/twind#features.

Example

Let's see our running YellowBox + 'hey' example, using twind:

twind example Same simple example, using twind. Copied and pasted Tailwind's classes into the tw tagged template literal

Considerations:

  • Not widely adopted yet. Tailwind and styled-components have huge communities behind them, so there's a bit more safety in numbers for those technologies. twind is small in comparison to those two technologies, but it's growing! 2.3k stars on GitHub, almost used in 1000 projects.
  • No major release yet. Currently the version is v0.16.17, but the project maintainers are working on a v1.0 release: https://github.com/tw-in-js/twind/releases/tag/v1.0.0-next.38
  • Faster than styled-components in the browser. Since twind processes classes at run-time, it's not faster than Tailwind itself. But it's still lightning fast ⚡️

Best of both worlds

Wrapping things up, what is the real purpose of combining TailwindCSS with styled-components? The biggest benefit is in performance. Styled-components applies most of its magic at run-time. Automatic critical CSS, classname generation, etc. all happen in the browser as a user is on your site. Tailwind heavily relies on build-time, which means the user gets a lot of speed benefits at the expense of the developer. Configuration is needed for Tailwind projects and rebuilding projects during development can take a while.

By using twind, you are getting the -in-JS benefits at run-time, while using all of Tailwind's functionality that you previously had to configure, like variants. twind is also more performant than most CSS-in-JS providers, so you are getting a boost in UX over styled-components. Pretty sweet deal!

The fundamental driver behind twind is still DX. Tailwind itself was created to give a better experience for developers and twind takes it a step further in optimizing those styles. DX is subjective, so if you're not a fan of Tailwind syntax/the ecosystem around it, you do have some options. Personally, I would stick with styled-components for all the reasons above. If you want zero-run-time CSS-in-JS to truly get the best run-time performance, there is a CSS-in-JS provider that is for 'those who want it all'. astroturf is a CSS-in-JS provider that also aims to push all optimizations to build-time.

But for those like me who want the best of both worlds like Hannah Montana, twind gives us performance wins in the browser, rapid style writing from Tailwind, and close to 0 configuration to give us CSS-in-JS benefits. No longer is style writing a chore, but a way to quickly bring websites to life and have fun doing it. Have any comments, questions or concerns? Or any alternatives to the technology I talked about here? I'd love to hear from you @ developers@useanvil.com. Or hit me up @fostimus on Twitter. Style on, my friends!