How to Build A React TS Tailwind Design System
Tags: tutorial, react, typescript, tailwindcss
Published: 2021-02-16 22:25:56

![Blog post cover](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zl2xcu7d5zoou9givgdg.png)

# Why you would want to do it and a step-by-step guide on how.


What is this❓ Another "how to" article (post)?!

Yes, it is.

What's in it for you?

Hear me out, it's going to be worth it.

By the end of this post, you will be able to roll out a starting point for your own React + TypeScript + Tailwind + Styled Components design component library. Also, you will learn how to craft a workflow using powerful tools like StoryBook, Chromatic, and TSDX. Besides that, we will see and learn a bit about setting up stuff like React Testing Library and how to fix some of those annoying TS errors we encounter.

Moreover, I will attempt to explain why and how I went down this same road, presumably, in an entertaining and informing way 🤓.

> If you want to skip ahead to the "How to" section [click here](#setting-up-tsdx), or if you want to go straight ahead to the final code, [check out the repository here](https://github.com/hamatoyogi/react-tw-blog-post).

## Table Of Contents:

- **The Background**
  - [A Cautionary Start](#a-cautionary-start)
  - [The Problem](#the-problem)
  - [What is a design system?](#what-is-a-design-system)
  - [To the Drawing Board](#to-the-drawing-board)
  - [A Tale of Wind](#a-tale-of-wind)
  - [Enter TSDX](#enter-tsdx)
  - [Getting UI Feedback with Chromatic](#getting-ui-feedback-with-chromatic)
  - [Bringing It All Together](#bringing-it-all-together)
- **How To**
  - [Setting up TSDX](#setting-up-tsdx)
  - [Installing Tailwind](#installing-tailwind)
  - [Adding Styled Components](#adding-styled-components)
  - [Adding React Testing Library](#adding-react-testing-library)
  - [Sanity Check with TSDX Example](#sanity-check-with-tsdx-example)
  - [Setting up Chromatic](#setting-up-chromatic)
  - [Preparing for Production](#preparing-for-production)
  - [Viewing your Tailwind Configuration](#viewing-your-tailwind-configuration)
- **Suffix**
  - [Publishing](#publishing)
  - [Other Tailwind-in-JS Solution](#other-tailwind-in-js-solutions)
  - [Conclusion](#conclusion)
  - [Resources](#resources)

## [A Cautionary Start](#a-cautionary-start)

This whole post started with this tweet:

{% twitter 1354855711285665799 %}

Seeing as it got some attention, I was bound by Twitter law to strap in, knuckle down and start typing.

For me, a lot of things around how I learn are a part necessity, part interest, and always a journey. It is important to understand the journey to realize if you have gotten to your destination.

So, here I am again, at a new place with new challenges ahead of me... I have written some stories about [my humble beginnings](https://medium.com/the-hamato-yogi-chronichels/a-tale-of-an-elder-millennial-how-i-started-my-tech-journey-part-1-b07a339957d1), as well as some tales of [other challenges I've faced as an engineer](https://medium.com/the-hamato-yogi-chronichels/how-we-moved-from-wordpress-to-react-and-raised-80-million-6e80b88be28f).

This time it started at a new company I joined not too long ago. About a month in, I was tasked with implementing a white labeling solution for one of the products to a client. For the sake of this post, what the company does is not that important. I was lead down this path because there are about 5 different applications and more in thoughts and planning. Being in the startup world, that's just the way it goes.

Like I was saying, I started working on white labeling one of the products, not knowing much about what was at my disposal in terms of assets, eg. shared packages, component libraries, etc. I cracked my knuckles and started browsing through the different organizations Github Repos and started talking to people, trying to understand what is there that I can use to achieve the task at hand.

I ended up finding 3 different "common" React component libraries that weren't used across all the applications, but some... It was divergent... Being a so to speak early-age startup, a lot of the code in the repositories was (and still is) "gung hoed". This is not saying anything critical about other engineers, I've been there, done that...

All of the front-end apps are built with React and adopted [Styled Components](https://styled-components.com/) at some point. Some had also SaSS mixed in, some had [TypeScript](https://www.typescriptlang.org/), and some [Flow](https://flow.org/).

I had to start working on my task, so I did just that, hoping I could find my way through while learning about why the situation is as it is and how we could improve. To get my task done, I looked into leveraging Styled Components [theming functionality](https://styled-components.com/docs/advanced#theming). But what I found out was that there were parts of the theme being passed from the app and some getting overridden by one of the shared packages.

I also set out to [Twitter](http://www.twitter.com) to gauge my approach:

{% twitter 1316410473718439936 %}

In the end, I ended up with a solution I also shared:

{% twitter 1316776818779795456 %}

It was clear to me that this is an ok solution for now, but this has raised the need to think about what we would need to do if some other client would want a white label of its' own.

After discussing all the fun I had with the rest of my team and our former discussions around our shared packages, I was aware that there were processes that have started regarding building a design system, but were nipped in the bud for various reasons. I thought it was a good time to get this process going again and my colleagues and engineering leadership (luckily 😅) agreed as well.

## [The Problem](#the-problem)

- Scattered components across repositories and applications.
- Different teams working on several products.
- No way for developers to know what they can use.
- Designers repeating design or re-designing components that already exist.
- Product has no visibility as to what is available for new features.

What we aim to solve was reducing the number of components that are being written across the organization, share code, and be able to see what we have, while having a single repository to develop where we can manage versioning and distribution.

The solution is inevitable - we need a design system.

## [What is a design system?](#what-is-a-design-system)

There is a lot of material out there about the correct definition of this term, as well as different opinions around, and I've read half a dozen posts and explanations what it is. It seems to be something else for different people in relation to their occupation.

For developers, this could be a shared component library (or package) like Material-UI, ChakraUI, BaseUI, etc. But, for designers, it could be a sketch file that has all the designs in one place. I've even heard product people call it a UI-Toolkit.

I think the general definition I liked the most is by [Audrey Hacq](https://medium.com/@audreyhacq) as stated in her post "[Everything you need to know about design systems](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969)":

> "A Design System is the single source of truth which groups all the elements that will allow the teams to design, realize and develop a product."

{% twitter 1217569188614557696 %}

Design systems are all the rage nowadays. They are key for product design consistency, better stakeholder communication, and allow rapid development of new applications. Like it or don't they are a curse and a blessing. They help with cross alignment, but they are hard to create, adopt and maintain correctly.
 
[![Untitled.png](https://i.postimg.cc/mDhx5844/Untitled.png)](https://postimg.cc/ft4g37kq)
[https://www.learnstorybook.com/design-systems-for-developers/react/en/introduction/](https://www.learnstorybook.com/design-systems-for-developers/react/en/introduction/)

## [To the Drawing Board](#to-the-drawing-board)

Something I neglected to mention earlier is that one of the shared libraries we had was using [Bit](https://bit.dev/) and we had a collection of Bit components, which were used sparingly across applications. If you are not familiar with it, the gist is that you can build, version, and distribute components individually through their cloud. A really powerful concept of super modularity. The promises on their webpage get you excited about building something truly composable.

That's why for the first iteration I came up with this diagram:

[![Drawing-Borad-1.png](https://i.postimg.cc/V6TyQnsC/Drawing-Borad-1.png)](https://postimg.cc/47z2pK9f)

This appeared to be a sound plan. However, things don't always go according to our plans...

The short version is it was not worth it for our use case, in my humble opinion. Even so, I've read an article published on Bit's blog "Bit's and Pieces", conveniently titled "[How we Build a Design System](https://blog.bitsrc.io/how-we-build-our-design-system-15713a1f1833)" which had me truly optimistic at first. Nevertheless, those shiny marketing messages on the Bit homepage fail to fulfill this utopian world of component composition.

The whole process I went through with this piece of tech is worth a blog post of its own (I've even already written down the title in my notes: "The Bit they don't tell you about Bit" 😅).

After meticulously crafting a whole workflow based on every shred of documentation and examples I could find, it didn't sit right. Specifically, around the workflow, I imagined increasing the collaboration and velocity of my team and with Bit, there seemed to be too much overhead for any new coming developer to the project.

The bottom line was, Bit doesn't play nice with Git. In my opinion, having a streamlined process that involves Git, code reviews, and design reviews are crucial.

Having said that, I have nothing bad to say about Bit. I think it has incredible potential, but it still is not fully there, it does not live up to the promise. I will keep a close eye on them though, they might just surprise us just yet.

> {% spoiler There was a happy ending though... %} A few weeks after writing the above lines, I am happy to say that with the help and perseverance of my good friend and teammate [Yonatan Katz](https://www.linkedin.com/in/yontn/), we were able to achieve our desired workflow with Bit. Having a single repo for the entire design system, while publishing each component individually to the [Bit](http://bit.dev) cloud 😃 . Seeing as changing these paragraphs will break the narrative of the story, I'm choosing to keep things as they are with this small note to share the happy ending of that challenge and thoughts. {% endspoiler %} 

As for myself, I needed a new plan...

![https://rpscurriculumandinstruction.weebly.com/uploads/8/1/0/1/81013748/macrafic-big-idea-2-pic-2.jpg](https://rpscurriculumandinstruction.weebly.com/uploads/8/1/0/1/81013748/macrafic-big-idea-2-pic-2.jpg)

## [A Tale of Wind](#a-tale-of-wind)

I'm a dev news junkie and a hype-driven development practitioner (😜 ). As such, I could not stop hearing about TailwindCSS. Every second article or tweet I read mentioned how good Tailwind is. Furthermore, every third podcast I listened to had either [Adam Wathan](https://adamwathan.me/articles/) on it or someone mentioning him.

This tweet by [Nader Dabit](https://twitter.com/dabit3) showcases what I went through:

{% twitter 1359295621119827968 %}

The one piece that stuck with me the most was by [Max Stoiber](https://mxstbr.com/) titled "[Why I Love Tailwind](https://mxstbr.com/thoughts/tailwind)". In it, he makes a great case about the key feature of Tailwind being that the core of the framework is its design tokens:

> "The key to Tailwind's popularity is the painstakingly constructed system of design tokens at the core of the framework."

{% twitter 1359261039196475401 %}

Max goes further on about the downsides he sees and suggests a way to use it without them. The answer - [twin.macro](https://github.com/ben-rogerson/twin.macro).

{% twitter 1350856065210957824 %}

The gist of it is that it is a Babel macro that runs at build time and creates Tailwinds' critical CSS styles that are necessary for any given page as well as being extendable using CSS in JS libraries such as Styled Components or Emotion.

Once again, this seemed a solid plan.

> Some people don't feel the love for Tailwind, and of course, have some valid points. You can read "[Why Tailwind Isn't for Me](https://dev.to/jaredcwhite/why-tailwind-isn-t-for-me-5c90)" by [Jared White](https://dev.to/jaredcwhite) from a different point of view.

## [Enter TSDX](#enter-tsdx)

![og_image.jpg](https://tsdx.io/og_image.jpg)

Once I decided to ditch Bit, to get the ball rolling on this project I needed a way to build a pattern library, or more simply put a package.

Being as JavaScript is dead and TypeScript is the successor (I kid of course! 😉), I wanted to find a way to easily start a repo without fidgeting around with configs too much. That is when I found [Jared Plamer](https://github.com/jaredpalmer)'s project [TSDX](https://tsdx.io/).

> For those of you not familiar with Jared, he is behind projects like [formik](https://github.com/formium/formik) and [razzle](https://github.com/jaredpalmer/razzle). Also, he has been heard to be seen sparingly next to a mad beat maker, wearing a neck chain and boxer shorts with an American bald eagle (shout out [Kenny](https://twitter.com/ken_wheeler)! ✊🏽), while talking into a mic on the [Undefined Podcast](https://undefined.fm/).

The project's tagline sums it all up beautifully:

> "TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages with ease--so you can focus on your awesome new library and not waste another afternoon on the configuration."

It also comes with templates built-in, one of them was specifically what I was looking for `react-with-storybook`.

> One article that helped me find my feet with TSDX was "[Build your First Typescript Package in Under 10 Minutes](https://dev.to/g_abud/publish-a-typescript-package-in-under-10-minutes-4940)" by [Gabriel Abud](https://dev.to/g_abud).

[![4wa3tv.jpg](https://i.postimg.cc/nVgx6bMP/4wa3tv.jpg)](https://postimg.cc/V5q2Chqq)

In short, it does exactly what it promises with a really low learning curve. Furthermore, something really neat that comes in the mix is the `example` directory, which is just a playground React App to test your code. Unlike TSDX, which uses [Rollup](https://rollupjs.org/guide/en/) bundler, it uses [Parcel](https://parceljs.org/) to get it running (not super important, I just find it interesting). You can import the bundled package code into there to battle-test it.

One thing that is good to mention though, is that TSDX comes with preconfigured Github Actions that test and build your package. Something that I didn't know about it and misunderstood was all the matrix testing in `.github/workflow/main.yml` which has a node matrix configuration that spins up different types of operating systems to test the package on.

> Be wary that the Github Action minute calculation for OSs is different. This could lead to spending your budget or free minute allocation pretty quickly. Learn from my experience. I had to learn the hard way and eventually migrated all actions to CircleCI. 😳

## [Getting UI Feedback with Chromatic](#getting-ui-feedback-with-chromatic)

One other tool I like and recommend using with Storybook is Chromatic. I came across it while reading Storybooks' article "[Design Systems for Developers](https://www.learnstorybook.com/design-systems-for-developers/)". It helps you better manage the whole workflow of your component library. You can get feedback from your teammates easily, it helps with visual testing (and makes snapshot testing almost redundant, IMO), It becomes part of your PR process, and publishes your Storybook to the cloud. Besides that, it is pretty simple to set up (I'll get to that later).

## [Bringing It All Together](#bringing-it-all-together)

Alright, I had my tools in place and it was time to start connecting the dots, so to speak. I kicked off a new TSDX project, installed Styled components, and then went on to try and set up `twin.macro`. However, I hit a snag there... In the [Twin example repo](https://github.com/ben-rogerson/twin.examples), there was no Styled Components + Storybook example, so I configured what seemed to make sense. Then I went on to add some samples, imported them into a new Story, and tried to run Storybook. It didn't work as expected. Some of the code worked and other components that were using the `tw` syntax didn't:

```jsx
import React from 'react';
import 'twin.macro';

export const Logo = () => (
  <a
    // Use the tw prop to add tailwind styles directly on jsx elements
    tw='w-32 mb-10 p-5 block opacity-50 hover:opacity-100'
    href='https://github.com/ben-rogerson/twin.macro'
    target='_blank'
    rel='noopener noreferrer'
  >
    <TwinSvg />
  </a>
);

const TwinSvg = () => (
  <svg fill='black' viewBox='0 0 100 35' xmlns='http://www.w3.org/2000/svg'>
    <path d='m31.839 11.667c0-6.2223-3.3515-10.111-10.054-11.667 3.3514 2.3333 4.6082 5.0556 3.7704 8.1667-0.4781 1.7751-1.8653 3.0438-3.4009 4.4481-2.5016 2.2877-5.3968 4.9354-5.3968 10.718 0 6.2223 3.3515 10.111 10.054 11.667-3.3515-2.3333-4.6083-5.0556-3.7704-8.1667 0.478-1.775 1.8653-3.0438 3.4009-4.4481 2.5015-2.2877 5.3967-4.9354 5.3967-10.718z' />
    <path d='m-2.7803e-7 11.667c1.4828e-7 -6.2223 3.3515-10.111 10.055-11.667-3.3515 2.3333-4.6083 5.0556-3.7705 8.1667 0.47806 1.7751 1.8653 3.0438 3.4009 4.4481 2.5016 2.2877 5.3968 4.9354 5.3968 10.718 0 6.2223-3.3515 10.111-10.054 11.667 3.3515-2.3333 4.6083-5.0556 3.7704-8.1667-0.47805-1.775-1.8653-3.0438-3.4009-4.4481-2.5015-2.2877-5.3967-4.9354-5.3967-10.718z' />
    <path d='m50.594 15.872h-3.9481v7.6c0 2.0267 1.3373 1.995 3.9481 1.8683v3.0717c-5.2853 0.6333-7.3867-0.8233-7.3867-4.94v-7.6h-2.9292v-3.2933h2.9292v-4.2534l3.4386-1.0133v5.2667h3.9481v3.2933zm21.324-3.2933h3.6297l-4.9988 15.833h-3.3749l-3.3113-10.672-3.3431 10.672h-3.375l-4.9987-15.833h3.6297l3.0884 10.925 3.3431-10.925h3.2794l3.3113 10.925 3.1202-10.925zm7.8961-2.375c-1.2099 0-2.1969-1.0134-2.1969-2.185 0-1.2033 0.987-2.185 2.1969-2.185s2.1969 0.98167 2.1969 2.185c0 1.1717-0.987 2.185-2.1969 2.185zm-1.7193 18.208v-15.833h3.4386v15.833h-3.4386zm15.792-16.245c3.566 0 6.1131 2.4067 6.1131 6.5233v9.7217h-3.4386v-9.3733c0-2.4067-1.401-3.6734-3.566-3.6734-2.2606 0-4.0436 1.33-4.0436 4.56v8.4867h-3.4386v-15.833h3.4386v2.0266c1.0507-1.6466 2.77-2.4383 4.9351-2.4383z' />
  </svg>
);
```

After that, I tried pulling the output code into our helpful example repo, and that seemed to work.

I went on fiddling and trying stuff, and even reached out to [Ben Rogerson](https://github.com/ben-rogerson) the creator:

{% twitter 1340408014390628352 %}

He did help me understand how to add some Tailwind intellisense for Twin:

{% twitter 1341003061431390210 %}

But I have still not been able to get the above syntax to work in my library. I set it aside and moved on, as I had plans and willingness in my company to start developing the library. However, I am keen to get back to it at some point.

> You can check out the TSDX-Twin repo [here](https://github.com/hamatoyogi/twin-tsdx-component-lib/) that has 1 out of 3 components working.

What I ended up doing was starting a repo from scratch and just going with Styled Components + Tailwind vanilla.

I could go on about the process of selling this whole idea back to my team and mention all the discussions around this project... But that's probably not why you're here 😉 ...

> OK, story time is over. Let's get down to the brass tax!

[![Untitled-1.png](https://i.postimg.cc/JhpVxS9K/Untitled-1.png)](https://postimg.cc/rdRPFZyt)

## [Setting up TSDX](#setting-up-tsdx)

For the sake of this post, I will start up a new repo and take it one step at a time as I write this. I will commit every step, so you can follow along or just check out the commits.

Let's start by bootstrapping a new TSDX project:

```bash
// In your terminal / command line run:

npx tsdx create new-project-name
```

- This will install all the initial packages and create a `new-project-name` folder.
- After that step is done, you will be prompted to choose a template:

[![Untitled-2.png](https://i.postimg.cc/5yhMvvZV/Untitled-2.png)](https://postimg.cc/wRQPYtqf)

- Choose `react-with-storybook`.
- Necessary NPM modules will be installed.
- Once it finishes you will get this confirmation:

[![Untitled-3.png](https://i.postimg.cc/xTmQQxzT/Untitled-3.png)](https://postimg.cc/ZCJ2FLH1)

- Now we can `cd` into the directory and run `yarn start` to start developing in watch mode, but as we are working on a React component library and want to practice Storybook driven development ([SDD](https://medium.com/nulogy/storybook-driven-development-a3c517276c07)) we can just run `yarn storybook` and be on our way.
- Spinning up Storybook on a fresh TSDX project will yield the amazing "snozzberies" component:

[![Untitled-4.png](https://i.postimg.cc/3Rw7H3gP/Untitled-4.png)](https://postimg.cc/rd771XZJ)

Not much to write home about, but a good start.

If we open up our project in our editor this is the folder structure we should see:

[![Untitled-5.png](https://i.postimg.cc/cH5WxPh6/Untitled-5.png)](https://postimg.cc/MMRNtPh8)

**Let's breakdown the folders and files**:

- `.github`: Generated Github actions (If you are unfamiliar with them, I do encourage you to read about them [here](https://github.com/features/actions)), their purpose is to automate your workflows and achieve CI ([continuous integration](https://www.cloudbees.com/continuous-delivery/continuous-integration)). Under this folder we have 2 files:
  - `main.yml`: Instructions for the GitHub action on how to install dependencies, [lint](<https://en.wikipedia.org/wiki/Lint_(software)>) your code, run tests, and build the package. It runs on a different node version matrix, and different operating systems (important to know this as I've mentioned earlier).
  - `size.yml`: This little nugget is here to help you keep track of the size of the package using, the configuration set in `package.json` under the `"size-limit"` property. It is set to 10KB as the default, on the generated output files. You can change it if the need arises. This action runs when you push the code to a Github repository and will fail your checks if you pass the limit.
- `.storybook`: this is where your Storybook configuration lives. You can read more about it in their [Setup section](https://storybook.js.org/docs/react/get-started/setup).
- `.vscode`: This folder is only generated if you use [VScode](https://code.visualstudio.com/). As I used it in this project, it was created and has my workbench settings. I've also added recommended extensions, which you can try and use if you decide to clone this repo.
- `dist`: The bundled output of our library code. This is what will essentially be used by consumers of this package. It holds the minified `esm` ([ES modules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)) and `cjs` ([Common JS](https://en.wikipedia.org/wiki/CommonJS)) files, as well as source maps and `TypeScript` type definitions.
- `example`: Includes our playground React App, which I've mentioned above. This will not be part of the bundle, and will not be published to whatever package manager you choose to use.
- `node_modules`: Where all JavaScript installed package modules live.
- `src`: Here is where the actual magic is. This is the main source file and code that will be compiled out to our `dist` folder. Inside of this directory is the `index.tsx` file, which is where you want to import all other source files you create. On a newly created TSDX project, you get the code for the aforementioned "snozzberies" component.
- `stories`: Where your Storybook [stories](https://storybook.js.org/docs/react/get-started/whats-a-story) live. This is where we want to put the code that we write to display our UI to us. "Snozzberries" component is imported and rendered here.
- `test`: In this directory, we will write our tests. When we first open it, a test for the generated component is there under `blah.test.tsx`.

Other than those folders we also have the regular generic files like:

- `package.json` - which shows our dependency list.
- `gitignore` - ignored files from `git`.
- `LICENSE` - Automatically generated MIT license, change as you wish.
- `README.md` - Generated by TSDX, this file has more information about how to use the tool. If you plan on publishing this project as a package, I encourage you to change this and write down clear instructions on how to install and use your library.
- `tsconfig.json`: The TypeScript configuration file that was generated by TSDX. It comes preconfigured in an opinionated way by the maintainers. I suggest leaving it as it is unless you know what you're doing or want something specifically different.

## [Installing Tailwind](#installing-tailwind)

> ⚠️ Remember: Tailwind CSS requires Node.js 12.13.0 or higher.

To get Tailwind into the mix, you can follow along with their [installation docs](https://tailwindcss.com/docs/installation) (this section will be kind of similar to that, but I think it's helpful to have in this post for ease of use). As I've already done this (several times 😅 ), for TSDX and Storybook compatibility we will need to use the [PostCSS 7 compatibility build](https://tailwindcss.com/docs/installation#post-css-7-compatibility-build).

If you are unfamiliar with [PostCSS](https://postcss.org/), in short, it's a tool for transforming CSS with JavaScript and allows us to use "Tomorrow's CSS today". It is a CSS preprocessor, similar to SaSS, but with some [differences](https://stackshare.io/stackups/postcss-vs-sass).

Let's install our dependencies by running:

```bash
yarn add -D tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9

# alternatively: run with npm install -D ...
```

> Notice I am installing everything as a dev dependency, as this is a package, we want to keep dependencies to a minimum and allow our tools to do their job. For some things, we might want to update our `package.json` "peerDependencies".

Next, we will need to add a `postcss` config in the root of our project:

```jsx
// postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};
```

Now we can initialize Tailwind with:

```bash
npx tailwindcss init
```

That will create a `tailwind.config.js` file in the root as well, where you can add and customize your configuration as needed:

```jsx
// tailwind.config.js

module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
};
```

Next up, we need to include Tailwind in our CSS, in our case, there are 2 things we need:

1. To be able to import our CSS into components, we will need to tell TSDX how to include it with our code. For that, we will need to install `rollup-plugin-postcss` (as TSDX uses rollup).
2. Create a `CSS` file in our `src` directory which we will use in any component in which we want to use Tailwind.

Alright, so now let's add `rollup-plugin-postcss`:

```jsx
yarn add -D rollup-plugin-postcss
```

> TSDX is [fully customizable](https://tsdx.io/customization) and you can add any `rollup` plugin, but be aware that it overrides the default behavior

Now we'll create a `tsdx.config.js` file in the root and in it, we'll put the following code:

```jsx
// tsdx.config.js

const postcss = require('rollup-plugin-postcss');

module.exports = {
  rollup(config, options) {
    config.plugins.push(
      postcss({
        config: {
          path: './postcss.config.js',
        },
        extensions: ['.css'],
        minimize: true,
        inject: {
          insertAt: 'top',
        },
      })
    );
    return config;
  },
};
```

This is giving our `postCSS` path, which tells it what files we want it to run on. The `minimize` key is to allow us to minimize the output. The most important key here is the "`inject`". We set it to "`top`" to tell `postCSS` where inside the `<head>` of our page the CSS will be inserted. It's paramount for Tailwind as it needs to have the utmost priority of any other stylesheet.

Next, for part 2, we will create a `tailwind.css` (can be named anything else) file under the `src` directory and paste this in:

```css
// src/tailwind.css

@tailwind base;
@tailwind components;
@tailwind utilities;
```

Great! This should allow us to get the job done.

Let's check it out on the only component we currently have and see if it works:

```tsx
// src/index.tsx

import React, { FC, HTMLAttributes, ReactChild } from 'react';

// ! Add the CSS import statement !
import './tailwind.css`;

// ...

// we'll add some Tailwind classes on our components to test

export const Thing: FC<Props> = ({ children }) => {
  return (
    <div className="flex items-center justify-center w-5/6 m-auto text-2xl text-center text-pink-700 uppercase bg-blue-300 shadow-xl rounded-3xl">
      {children || `the snozzberries taste like snozzberries`}
    </div>
  );
};
```

Now we'll run our StoryBook (`yarn storybook`) and have a look:

[![Untitled-6.png](https://i.postimg.cc/XYMVySbC/Untitled-6.png)](https://postimg.cc/ppZNgSyW)

That's a nice-looking "snozzberries" component!

[![cbc86920aaaca20d9b3c09bda1e7c72b.jpg](https://i.postimg.cc/rmhVdDVV/cbc86920aaaca20d9b3c09bda1e7c72b.jpg)](https://postimg.cc/R6H5yVJy)

At this point, it would be a good time to sort and prepare our package a bit so we can have more than 1 component. For that, we'll change the name of the file that holds our beloved "snozzberries" component from `index.tsx` to `Thing.tsx`. Then, we'll create a new `index.tsx` file where we'll export all of our components and let TSDX do its thing:

```tsx
// index.tsx:

export * from './Thing';

// We use the "*" export to get everything out of our file: components and types.
```

Now, let's see we haven't broken anything and see that our test is functioning by running:

```bash
yarn test
```

And we get this output:

[![Untitled-7.png](https://i.postimg.cc/9fmHGcNr/Untitled-7.png)](https://postimg.cc/G45fRnRC)

[![Untitled-8.png](https://i.postimg.cc/dVmKkh6b/Untitled-8.png)](https://postimg.cc/bZJ54NKg)

You might be wondering: "What's going on here?"

Well, Jest doesn't know how to read `CSS`. Also, it doesn't really care about it either, so we will have to mock it for him with [`identity-obj-proxy`](https://github.com/keyz/identity-obj-proxy) (to learn more about why go [here](https://jestjs.io/docs/en/webpack#mocking-css-modules)). Let's add it:

```bash
yarn add -D identity-obj-proxy
```

Next, we will need to point Jest at the correct type of files by adding this snippet to our `package.json` file:

```bash
// package.json
...
  "jest": {
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    }
  },
...
```

Now we can run the test again, and behold the new results:

[![Untitled-9.png](https://i.postimg.cc/x8c0L98K/Untitled-9.png)](https://postimg.cc/wRY8p8hv)

## [Adding Styled Components](#adding-styled-components)

So now that we have all that going for us, let's see how Styled Components gets in the mix...

First, let's install the package and its TypeScript types:

```bash
yarn add -D styled-components @types/styled-components
```

Now let's keep it simple and start by building a `Button` component (original, yes I know...):

```tsx
// src/Button.tsx

import React, { FC } from 'react';
import styled from 'styled-components';

const StyledButton = styled.button`
  background-color: blue;
  color: white;
`;

export interface ButtonProps {
  text: string;
}

export const Button: FC<ButtonProps> = ({ text }) => {
  return <StyledButton>{text}</StyledButton>;
};
```

> I'm assuming basic knowledge in Styled Components. If you are new to it, [check out the doc site](https://styled-components.com/).

We will need to add it to our `index.tsx`:

```bash
export * from './Thing';
export * from './Button';
```

Add a Story for it, so we can see it:

```tsx
// stories/Button.stories.tsx

import React from 'react';
import { Meta, Story } from '@storybook/react';
import { Button, ButtonProps } from '../src';

const meta: Meta = {
  title: 'Button',
  component: Button,
  argTypes: {
    text: {
      control: {
        type: 'text',
      },
    },
  },
  parameters: {
    controls: { expanded: true },
  },
};

export default meta;

const Template: Story<ButtonProps> = (args) => <Button {...args} />;

export const SCButton = Template.bind({});

SCButton.args = { text: 'Button' };
```

And voila! Our very ugly button:

[![Untitled-10.png](https://i.postimg.cc/k4MMXQdY/Untitled-10.png)](https://postimg.cc/Y4VBdm0Q)

We can do better of course... Let's remove our styles and add some Tailwind classes:

```tsx
// src/Button.tsx

import React, { FC } from 'react';
import styled from 'styled-components';

const StyledButton = styled.button``;

export interface ButtonProps {
  text: string;
}

export const Button: FC<ButtonProps> = ({ text }) => {
  return (
    <StyledButton className='px-8 py-2 font-semibold text-white transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400'>
      {text}
    </StyledButton>
  );
};
```

And now we have this handsome fellow:

[![Untitled-11.png](https://i.postimg.cc/bwbw6YTg/Untitled-11.png)](https://postimg.cc/PCXHJj0v)

We still have some styles in our Styled Components, which we don't really need, and our JSX is a bit long and messy. If we combine our classes into our Styled Components, that should clean it up and make our concerns a bit more clear. To achieve this, we will use the `[attrs` API](https://styled-components.com/docs/api#attrs) which allows us to attach props to our styled component:

```tsx
// src/Button.tsx

import React, { FC } from 'react';
import styled from 'styled-components';

const StyledButton = styled.button.attrs(() => ({
  className:
    'px-8 py-2 font-semibold text-white transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400',
}))``;

export interface ButtonProps {
  text: string;
}

export const Button: FC<ButtonProps> = ({ text }) => {
  return <StyledButton>{text}</StyledButton>;
};
```

This method allows great flexibility. How so? Say we now want to change the text color by the button "variant". We can do this by adding a prop to our `Button`, and we can either change it by changing our used Tailwind class name or use the prop and change it via our Styled Component string interpolation.

First, we'll add a `variant` prop to our component interface, and add 2 possible values:

```tsx
export interface ButtonProps {
  text: string;
  variant?: 'default' | 'warning';
}
```

Pass it in:

```tsx
// we set our "default" variant to... Um, well, to "default" ¯\_(ツ)_/¯

export const Button: FC<ButtonProps> = ({ text, variant = 'default' }) => {
  return <StyledButton variant={variant}>{text}</StyledButton>;
};
```

Hold on now! we have a TypeScript error!

[![Untitled-12.png](https://i.postimg.cc/Y0GHh8DR/Untitled-12.png)](https://postimg.cc/q6TYFcT6)

Notice the squiggly line under "variant"? In a nutshell, TS is telling us "you are passing in a parameter I don't know about". Let's fix that:

```tsx
// attr function needs the type, but also the "styled" function itself

const StyledButton = styled.button.attrs(
  ({ variant }: { variant: ButtonVariants }) => ({
    className: `px-8 py-2 font-semibold text-white transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400`,
  })
)<{ variant: ButtonVariants }>``;

// extract the type out from the interface for reuse.

type ButtonVariants = 'default' | 'warning';
export interface ButtonProps {
  text: string;
  variant?: ButtonVariants;
}

// There are a ton of other fancy ways of doing this in TS.
```

Back to business... So, one way to change our text color using our new `variant` prop would be just using [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) and choosing a different Tailwind class name for `text`. The other would be to use that same prop in our Styled Components backticks:

```tsx
// Option 1️⃣ :
const StyledButton = styled.button.attrs(
  ({ variant }: { variant: ButtonVariants }) => ({
    className: `px-8 py-2 font-semibold ${
      variant === 'default' ? 'text-white' : 'text-red-700'
    } transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400`,
  })
)<{ variant: ButtonVariants }>``;

// Option 2️⃣ :

const StyledButton = styled.button.attrs(() => ({
  className: `px-8 py-2 font-semibold text-white transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400`,
}))<{ variant: ButtonVariants }>`
  color: ${({ variant }) => (variant === 'warning' ? 'red' : '')};
`;
```

The tradeoff with option `2` is not having Tailwinds' utility classes and color themes to help us style things. However, mixing and matching both approaches is mighty powerful if you think about it.

One last thing that can be helpful is using a library like `[tailwind-classnames](https://github.com/muhammadsammy/tailwindcss-classnames)` which helps validate you are using correct class names, and if you don't TS will yell at you. It has the full capability and API of the known `[classnames](https://www.npmjs.com/package/classnames)` library, as it is just an extension of it.

## [Adding React Testing Library](#adding-react-testing-library)

I am not going to start explaining [why you should be using React Testing Library](https://kentcdodds.com/blog/introducing-the-react-testing-library), or [how it's different from Enzyme](https://medium.com/@boyney123/my-experience-moving-from-enzyme-to-react-testing-library-5ac65d992ce). I'll just say that I think It's great and that in my opinion, you should be using it.

With that out of the way... Let's add it to our project by running:

```bash
yarn add -D @testing-library/react @testing-library/jest-dom
```

Next, let's add a test for our `Button` component:

```tsx
// test/Button.test.tsx

import * as React from 'react';
import { render } from '@testing-library/react';

// This below import is what gives us the "toBeInTheDocument" method
import '@testing-library/jest-dom/extend-expect';

// As we are using the Component Story Format we can import it from our
// previously written story.
import { SCButton as Button } from '../stories/Button.stories';

describe('Button', () => {
  it('should render the button without crashing', () => {
    // render the button and get the getByRole method
    const { getByRole } = render(<Button text='test' />);

    // getByRole as its name gets a selector by its role.
    // in this case we're looking for a `button`.
    // then we make sure it's in the document
    expect(getByRole('button')).toBeInTheDocument();
  });
});
```

We also want to make sure that besides rendering, we can click it. So we'll check that as well:

```tsx
// ... same imports except:
// we've added the fireEvent method to simulate a user click
import { render, fireEvent } from '@testing-library/react';

describe('Button', () => {
  //... our former test
  it('should call the onClick method when a user clicks on the button', () => {
    // mock out our OnClick function
    const mockClick = jest.fn();

    const { getByRole } = render(<Button text='test' onClick={mockClick} />);

    // we store a variable with the button element
    const buttonElement = getByRole('button');

    // Simulate a user clicking on the button
    fireEvent.click(buttonElement);

    expect(mockClick).toHaveBeenCalledTimes(1);
  });
});
```

Let's try and make sure the tests work with `yarn test`.

But what's this 😱 ?

[![Untitled-13.png](https://i.postimg.cc/tgpp237R/Untitled-13.png)](https://postimg.cc/5X7htFwD)

The test failed due to a TypeScript error... 🤦🏽‍♂️

No fear! We can fix it... We'll go back to our `Button` file:

```tsx
// src/Button.tsx

// add the relevant type import
import React, { FC, ButtonHTMLAttributes } from 'react';

// ...

// We'll add the relevant missing type by extending our interface:
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  text: string;
  variant?: ButtonVariants;
}

// make sure we pass all the rest of the props to our component:

export const Button: FC<ButtonProps> = ({
  text,
  variant = 'default',
  ...rest
}) => {
  return (
    <StyledButton variant={variant} {...rest}>
      {text}
    </StyledButton>
  );
};
```

And now we are all green!

[![Untitled-14.png](https://i.postimg.cc/0N02sthJ/Untitled-14.png)](https://postimg.cc/CZdVC4hF)

One more test that is worth demonstrating is for our button with the dynamic Tailwind class. If you recall, we're testing option 2️⃣ :

```tsx
const StyledButton = styled.button.attrs(
  ({ variant }: { variant: ButtonVariants }) => ({
    className: `px-8 py-2 font-semibold ${
      variant === 'default' ? 'text-white' : 'text-red-700'
    } transition duration-500 ease-in-out transform rounded-lg shadow-xl bg-gradient-to-r from-red-300 to-blue-300 hover:from-pink-400 hover:to-indigo-400`,
  })
)<{ variant: ButtonVariants }>``;
```

We can easily test that we're expecting to have our `text-white` class when we're with the `default` variant and that we have the class `text-red-700` for the `warning` variant. Let's add that test:

```tsx
it('should have the right text color class name for variants', () => {
  // we extract the "rerender" method to test both variants
  const { getByRole, rerender } = render(<Button text='test' />);

  const buttonElement = getByRole('button', { name: 'test' });

  // if you recall, passing no variant, defaults to "default" variant.
  // this is a bit robust, but it serves to illustarte the point
  expect(buttonElement.classList.contains('text-white')).toBe(true);
  expect(buttonElement.classList.contains('text-red-700')).toBe(false);

  // render the other "warning" variant
  rerender(<Button text={'test'} variant='warning' />);

  // test the opposite of the above:
  expect(buttonElement.classList.contains('text-white')).toBe(false);
  expect(buttonElement.classList.contains('text-red-700')).toBe(true);
});
```

> If you add any dynamic CSS styles with Styled Components use [`jest-styled-components`](https://github.com/styled-components/jest-styled-components) for testing.

## [Sanity Check with TSDX Example](#sanity-check-with-tsdx-example)

Now that we've tested our newly added component, if we'd like to gain more confidence that our button will work with our outputted and bundled code, we can use the TSDX example repo.

For that, we'll build our code with this command:

```bash
yarn build
```

Then we can move to our `example` folder and install our dependencies:

```bash
cd example && yarn install
```

Next, we'll import our Button and add it to our example App:

```bash
// example/index.tsx

import 'react-app-polyfill/ie11';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// the importing location is automatically `dist` folder
import { Thing, Button } from '../.';

const App = () => {
  return (
    <div>
      <Thing />
      <Button text="test" />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
```

We'll boot up the example app with `yarn start`, then we'll visit `http://localhost:1234` and we should see this:

[![Untitled-15.png](https://i.postimg.cc/QN4CCyCQ/Untitled-15.png)](https://postimg.cc/4ncsFwzy)

So, there we have our Button under our "snozzberries" component. Looks like it's all working!

![https://thumbs.gfycat.com/CheapFeistyDassierat-mobile.jpg](https://thumbs.gfycat.com/CheapFeistyDassierat-mobile.jpg)

## [Setting up Chromatic](#setting-up-chromatic)

As I've mentioned, Chromatic is the perfect tool to [build, test, and collaborate on design systems](https://www.chromatic.com/solutions/design-systems). To get up and running, you can [follow along with their docs](https://www.chromatic.com/docs/setup), or just go and sign up at the [Chromatic site](https://www.chromatic.com/).

Once you've finished signing up, go ahead to your dashboard and create a project, you can choose an existing GitHub repo to start with. Once the project has been created, you'll need to install the Chromatic package:

```bash
yarn add --dev chromatic
```

Then you can publish your Storybook with the following command:

```bash
npx chromatic --project-token=<your_project_token>
```

This process will also guide you to complete the process and create an `npm` script for you:

[![Untitled-16.png](https://i.postimg.cc/13ZQ3GG0/Untitled-16.png)](https://postimg.cc/PPKc68FN)

Opening the "continue setup" link we get to this screen:

[![Untitled-17.png](https://i.postimg.cc/MHFpLvNP/Untitled-17.png)](https://postimg.cc/QKcD9xX1)

Now we can test and show how Chromatic works but clicking the "Catch a UI change" button. For that, let's change something in one of our components. Good ol' "Snozzberries" background is a good enough candidate:

```tsx
// src/Thing.jsx

// ...

// I've changed the bg-blue-300 class to bg-yellow-300 which is the background color:
export const Thing: FC<Props> = ({ children }) => {
  return (
    <div className='flex items-center justify-center w-5/6 m-auto text-2xl text-center text-pink-700 uppercase bg-yellow-400 shadow-xl rounded-3xl'>
      {children || `the snozzberries taste like snozzberries`}
    </div>
  );
};
```

Once again, let's run the Chromatic script, but now we can use our newly added `npm` script that has our project token:

```bash
yarn chromatic
```

This time, at the end of the process we'll see a message and an error:

[![Untitled-18.png](https://i.postimg.cc/yxdNnFQ3/Untitled-18.png)](https://postimg.cc/47DZxhTX)

Then going back to the Chromatic website this is what we see:

[![Untitled-19.png](https://i.postimg.cc/bvcs8SJ4/Untitled-19.png)](https://postimg.cc/gxsYHJ3K)

Now click on the "Welcome" component (our "snozzberries" component, that we should have renamed in its story 😬 ), which will lead us to the comparison screen:

[![Untitled-20.png](https://i.postimg.cc/G2z2kMQ1/Untitled-20.png)](https://postimg.cc/phhRvJ40)

On the right, we can see the new "state" of the component highlighted by green. Note, that this isn't the actual color we put, but just "what has changed". The three buttons on the top right can be toggled to show the actual new visual, click on the "Diff" will show us just that:

[![Untitled-21.png](https://i.postimg.cc/bw2sK9zQ/Untitled-21.png)](https://postimg.cc/hXcDmxQj)

We can click "Accept change + continue", which leads us to more explanations of the feedback process.

[![Untitled-22.png](https://i.postimg.cc/9fdzfXw0/Untitled-22.png)](https://postimg.cc/wtB9fpVY)

Chromatic allows us to create a workflow around building a UI library, where you can collaborate with your developer teammates and designers to streamline better communication. To achieve this, it is highly recommended to [integrate this with your CI](https://www.chromatic.com/docs/ci). In doing so, you can make this part of a PR process: when reviewing code changes, you review the UI changes as well.

## [Preparing for Production](#preparing-for-production)

After we've finished developing, we want to make sure our package is ready to be published and consumed properly. For that, one more handy tool TSDX offers us is its `size` check script. The default size limit for our package is defined in our `package.json` under the `size-limit` property:

```jsx
// package.json

{
 // ...
  "size-limit": [
    {
      "path": "dist/react-tw-blog-post.cjs.production.min.js",
      "limit": "10 KB"
    },
    {
      "path": "dist/react-tw-blog-post.esm.js",
      "limit": "10 KB"
    }
  ],
 //...
}
```

To run it we should make sure all of the code is built, and then we can run the `size` script by executing:

```bash
yarn build && yarn size
```

But what's this?

[![Untitled-23.png](https://i.postimg.cc/DyRzd4qm/Untitled-23.png)](https://postimg.cc/N57YQMCw)

We only have 2 components and the size is over 300KB??? That doesn't seem right.

This is happening, because we missed something in our configuration... To be more exact, we forget about a key config when [preparing any project using Tailwind for production](https://tailwindcss.com/docs/optimizing-for-production) - purging our CSS. Without this step, our CSS bundle consists of `294.0KB` (Gziped).

Following Tailwinds "[Remove unused CSS](https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css)" section, we can add this configuration to our package:

```jsx
// tailwind.config.js

module.exports = {
  purge: [
    './src/components/**/*.tsx',

    // This is a convention for files that only include Styled Components.
    // If you don't use this convention feel free to skip it.
    './src/components/**/*.styled.tsx',
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

Another important note is that for Tailwind to actually run the purging process, it must be run with `NODE_ENV` that is set to `production`. So, let's modify our build script:

```jsx
// package.json

{
  // ...
  scripts: {
    // ..
    build: 'NODE_ENV=production tsdx build';
    // ...
  }
  // ...
}
```

Finally, we can rerun `yarn build && yarn size`. Now our output will be:

[![Untitled-24.png](https://i.postimg.cc/7Y562nJp/Untitled-24.png)](https://postimg.cc/QHrhL5bb)

Still not under the TSDX default of `10KB`, but much much better. Like the green text in the CLI output says, you can increase your limits as needed.

There are many more things to do to optimize your bundle size output, most of them around how and what you use out of Tailwind. For example, you can disable the [preflight](https://tailwindcss.com/docs/preflight) setting which will remove all the CSS resets and shave off some more KBs.

In the end, you should consolidate and minimize what you use from the default Tailwind configuration. Adjust for your needs, and try to get your designers to choose and minimize the options. You probably don't need `[84` colors](https://tailwindcss.com/docs/customizing-colors) for your project...

## [Viewing your Tailwind Configuration](#viewing-your-tailwind-configuration)

One more nifty tool that can help developers and designers alike working on a project like this is [`tailwind-config-viewer`](https://github.com/rogden/tailwind-config-viewer). As stated on the repo's page:

> "A local UI tool for visualizing your Tailwind CSS configuration file."

It helps see what you've chosen as your Tailwind configuration. You can use `npx` on any project that has a `tailwind.config.js` file and just see what it has. Just run:

```bash
npx tailwind-config-viewer
```

[![Untitled-25.png](https://i.postimg.cc/WzCsnxqx/Untitled-25.png)](https://postimg.cc/Lgkd4CTV)

Besides showcasing your chosen config, you can hover over any class and just copy it with a click of a mouse.

## [Publishing](#publishing)

In this post I am not going to dive into this subject, it deserves a post of its own. However, with the setup I've mentioned here, publishing to `npm` or `github` packages has some more setup and configuration entailed, but not much. The build artifact following these aforementioned steps is ready to be taken and put on any registry.

I plan on doing a follow-up post on how to publish using Github packages + CircleCI / Github Actions + [Changesets](https://github.com/atlassian/changesets). Perhaps, I'll even address how to publish to `Bit`.

This is already a far too long post as it is 😅.

## [Other Tailwind-in-JS Solutions](#other-tailwind-in-js-solutions)

I've already mentioned `twin.macro`, and just recently learned about [Compiled CSS in JS](https://compiledcssinjs.com/), which apparently should work with it as well.

{% twitter 1361226725053460481 %}

Another very Intriguing project is `Twind`. Which, I've learned about after tweeting my plans about writing this post. One of the project maintainers, [Sascha Tandel](https://twitter.com/saschatandel), reached out to me and asked me to consider writing a little bit about it as well. I have not had the chance to dig into it too much and try it out, but I did read the docs and it is quite interesting.

Here's what Sascha had to say about it:

> "[Twind](https://twind.dev/) is a no-build-step tailwind-first CSS-in-JS library which allows seamless integration with existing Tailwind HTML using the [twind/shim module](https://twind.dev/docs/handbook/getting-started/using-the-shim.html). This feature can be used together with your favorite framework without any additional setup. `twind/shim` dynamically detects used Tailwind classes within the HTML document, creates the corresponding CSS rules, and injects these into a stylesheet. Here is [an example](https://esm.codes/#aW1wb3J0ICdodHRwczovL2Nkbi5za3lwYWNrLmRldi90d2luZC9zaGltJwoKZG9jdW1lbnQuYm9keS5pbm5lckhUTUwgPSBgCiAgPG1haW4gY2xhc3M9Imgtc2NyZWVuIGJnLXB1cnBsZS00MDAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIiPgogICAgPGgxIGNsYXNzPSJmb250LWJvbGQgdGV4dChjZW50ZXIgNXhsIHdoaXRlIHNtOmdyYXktODAwIG1kOnBpbmstNzAwKSI+CiAgICAgIFRoaXMgaXMgVHdpbmQhCiAgICA8L2gxPgogIDwvbWFpbj4KYA==) to play with. By shipping the compiler (rather than the resultant output) there is a known and fixed cost associated with styling. No matter how many styles you write or how many variants you use, all that your users will ever have to download is approximately 12KB of code (which is less than styled-components or your average purged Tailwind build). On the server, we can use [twind/shim/server](https://twind.dev/docs/modules/twind_shim_server.html) to generate the initial CSS to be included in the HTML. <br/><br/>
> Unlike Tailwind, Twind is not limited to the restrictions of a class name strings as input. One pain-point commonly felt when using utility CSS is long and unwieldy lines of code consisting of class names, often denoting styles at various breakpoints, which are quite hard to comprehend. It is not uncommon for a single element to have tens of rules applied to it. Twind provides a grouping syntax to combine common variants or prefixes. Both responsive and pseudo variants are supported in various combinations: `bg-red-500 shadow-xs md:(bg-red-700 shadow) lg:(bg-red-800 shadow-xl)`. <br/><br/>
> Because Twind is generating CSS during runtime there is no need to restrict the usage of variants. Every variant can be applied to every class. Additionally, variants can be stacked like `hover:focus:text-blue-700`. The Twind documentation site contains an overview of [all extensions](https://twind.dev/docs/handbook/getting-started/tailwind-extensions.html).<br/><br/>
> As a convenient escape hatch for all those one-off styles which aren't supported by Tailwind, Twind allows [writing arbitrary CSS](https://twind.dev/docs/handbook/getting-started/css-in-js.html) making it a full CSS-in-JS solution."

Some more appealing things that came out of our DMs were that the team is working on a `[styled` module](https://github.com/tw-in-js/twind/pull/7) and [TypeScript support](https://github.com/tw-in-js/typescript-plugin). Also, the package has its own "CSS-in-JS" baked in, so no need for Styled Components, Emotion, or Goober.

Twin has a similar API to `twin.macro` and granted, it is still in early versions (v0.15.1 at the time of writing), however, it might have a great future ahead of it. I for one, can't wait to try it out! For now, [here is a Codesandbox with React](https://codesandbox.io/s/twind-apply-react-typescript-73pm9) to play around with yourself 😃: 
{% codesandbox 73pm9 %}

## [Conclusion](#conclusion)

I hope I helped guide you through how to get a project like this going. I know I've enjoyed writing this, and I've learned a lot by doing so. I think the tooling I've mentioned in this post is pretty solid and absolutely helps boost productivity, but I know it's not so easy to set up. That is why I wrote this down, so someone doesn't have to go through the same hassles I did.

Surely, some tweaks and improvements can be made to this approach. Obviously, this is an opinionated way of building something like this. After all, I am one person and I have, like, you know, my opinion, man.

[![you-opinion.png](https://i.postimg.cc/CL2RPQVh/you-opinion.png)](https://postimg.cc/6yd6Tz3g)

Hopefully (🤞🏽) if you've read this down to here, you've enjoyed this post. If you did, please share this around, comment, like, and push the subscribe button 😜.

Cheers! 🍻

### [Resources](#resources):

- [Blog post Repo](https://github.com/hamatoyogi/react-tw-blog-post)
- [Design Systems For Developers/](https://www.learnstorybook.com/design-systems-for-developers/)
- [Tailwind CSS](https://tailwindcss.com/)
- [TSDX](https://tsdx.io/)
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
- [Styled Components](https://styled-components.com/)
- [Chromatic](https://www.chromatic.com/)
- [Twind](https://twind.dev/docs/index.html)
- [twin.macro](https://github.com/ben-rogerson/twin.macro)
- [Build vs Buy: Component Libraries edition](https://kentcdodds.com/blog/build-vs-buy-component-libraries-edition)
- [Adele - Design Systems and Pattern Libraries Repository](https://adele.uxpin.com/)
- Some [Helper functions for Tailwind CSS variable colors](https://gist.github.com/hamatoyogi/728e696772d8c91ddfac7f8648340b51) I wrote.