Using tailwind v3 with lit elements
Published: 2022-12-06 19:18:43

Quite often I see people asking around the Lit community about how to use tailwind inside their Lit elements. This is going to be a brief overview of how you can make that happen.

I've written about this before, but the solutions I used in the past were often inconsistent or flaky. Fortunately, we now have a much more concrete solution available to us!

## Setup

For simplicity, I'm going to go with a slightly unrealistic setup:

* [lit-element-starter-ts](https://github.com/lit/lit-element-starter-ts)
* directly calling `postcss` CLI on our built JS files

I'll assume you already have some similarly (or hopefully better) setup repo.

This means our source files will live in `src/` and our built files will live in `./`. For example, `src/my-element.ts` becomes `lib/my-element.js` on TypeScript build in this case.

## Installing tailwind dependencies

To add tailwind to this setup, we need the following:

* [`postcss`](https://github.com/postcss/postcss)
* [`tailwindcss`](https://tailwindcss.com/)
* [`postcss-lit`](https://github.com/43081j/postcss-lit)
* [`stylelint` (optional)](https://github.com/stylelint/stylelint)
* [`stylelint-config-standard` (optional)](https://github.com/stylelint/stylelint-config-standard)

The last two are just nice to have, allowing us to lint our source CSS.

All of these are npm packages you can install with `npm i`.

## How it'll all fit together

We will be using [PostCSS](https://github.com/postcss/postcss) to process our source files' CSS.

As part of that, PostCSS will call [tailwind](https://tailwindcss.com/) to handle the processing of tailwind's classes and what not.

We might also call [stylelint](https://github.com/stylelint/stylelint) so we can lint our source CSS.

In the end, we will have PostCSS write the now transformed sources back out to our build output.

## Configuring postcss

Tailwind is essentially just a processor we usually call via either the Tailwind CLI or PostCSS.

In our case, we're going with PostCSS. So let's add a config file `postcss.config.cjs`:

```js
module.exports = {
  syntax: 'postcss-lit',
  plugins: {
    tailwindcss: {
      config: './tailwind.config.cjs'
    }
  }
};
```

At the time of writing this, I had some trouble using ESM for these config files, hence the `.cjs` filename. Feel free to try it with `.js` or `.mjs` depending on your situation.

Two important things happen in the config file above:

1. We need to tell PostCSS how to read JS/TS files, specifically those containing Lit elements. We do that by specifying the `syntax` as [`postcss-lit`](https://github.com/43081j/postcss-lit).
2. We tell PostCSS we want to load tailwind as a plugin, to allow us to make use of tailwind's classes and everything else

## Configuring Tailwind

Tailwind normally assumes you store your CSS separate from your JS.

For example, it will scan your JS sources for CSS classes so it can strip them (similar to JS tree shaking) from your CSS files.

This shouldn't work in our case because our CSS and JS lives in the same single file most of the time. In theory, this means every class would be "used" because tailwind would scan its own CSS for usages of its own classes... not what we want.

To solve this, we configure tailwind with a `tailwind.config.cjs` like so:

```ts
const {tailwindTransform} = require('postcss-lit');

module.exports = {
  content: {
    files: ['./*.js'],
    transform: {
      ts: tailwindTransform
    }
  }
};
```

We're basically telling it two things here:

1. Our source files which make use of tailwind's classes live at `./*.js`. In this dumbed down case, this is true since we're transforming the already built (via `tsc`) JS. In your case, however, you may want to point it at your actual source files (the typescript).
2. A transform which will strip the CSS before tailwind does its class detection, so it doesn't detect its own CSS as usages

## Configuring stylelint (optional)

Now that you have PostCSS setup, you can do all sorts of useful stuff. For example, we can setup stylelint to lint our source CSS.

To do that, you can create a `.stylelintrc.json` like so:

```ts
{
  "extends": "stylelint-config-standard",
  "customSyntax": "postcss-lit"
}
```

Again, we're just telling stylelint how to interpret our sources and to apply the standard lint rules.

You can execute this via `npx stylelint src/**/*.ts` for example.

If you use a bundler, maybe look for a stylelint plugin if you wish.

## Run it

We can now do, for example:

```bash
npx postcss -r my-element.js
```

This would apply tailwind's transforms to our `my-element.js` file.

Again, this is a slightly unrealistic setup since we're transforming our TypeScript build output with PostCSS.

In real world repos you probably want this to run against a glob, like `postcss -r build/**/*.js`. Or if you use a bundler, use a postcss plugin in your bundler to apply it as part of the bundling process.