Lit components with tailwind styles

— Updated
Photo of James Garbutt
James Garbutt

Tailwind and many other CSS libraries were not designed with web components in mind, and come with some non-obvious difficulties when trying to use them in such a codebase.

This is a brief guide on how to use such a library with your Lit components.

Out of the box, tailwind basically provides some global CSS classes and injects the associated CSS at build time (via postcss).

This conflicts with how web components work, since each web component has its own natively scoped stylesheet rather than inherting any global styles. This is why Tailwind will not be much use to us without further setup.

To solve the gaps tailwind comes with, we will:

  • Inject tailwind styles into regular CSS files
  • Use import attributes to import those CSS files
  • Use esbuild to pull those CSS files into the bundle or same output directory

To begin, we need the following dependencies for our build:

  • esbuild for bundling our code and carry our CSS files across (via imports)
  • postcss for injecting tailwind styles
  • tailwindcss for tailwind itself (postcss plugin)

We can install these like so:

In our case, we are going to use postcss-cli for processing our CSS files. If you use rollup or another bundler already, you may be able to use a postcss plugin instead.

When creating components, we want to use import attributes to import our CSS files rather than embedding CSS in our sources.

However, at the time of writing this article, the new with syntax is not fully supported. Temporarily, we still have to use the old assert syntax, until esbuild implements full support for the new standard.

We can do this like so:

As you can see, we want to our my-element.css file to contain tailwind mixins, and we want to use the resulting classes in our element's render method.

The CSS file (my-element.css) would look like this:

We will then use tailwind to replace those mixins with CSS, including only the styles our render method referenced.

Before we run our build, we need to configure tailwind and postcss.

In our tailwind.config.js, we can write:

In our case, our sources are TypeScript, so we have the initial @type comment to give us auto-completion for the object's properties.

The rest of this file is straight forward. The important part is content, specifying where our sources are so tailwind can detect which classes we have used.

In our postcss.config.js, we simply want to tell postcss to use tailwind as a plugin:

We're going to do a few steps in our build:

  1. Bundle our sources (TypeScript in this case, including our CSS imports)
  2. Apply tailwind styles to the build output in-place

To do this, we can use an npm script:

You can see the magic here in our build:js script is esbuild's --loader option, which we have set to .css=copy. This basically means any CSS files we import via an ESM import statement will be copied across to the same directory as the esbuild JS output (bundle.js in our case).

So if we import my-element.css, we will expect two files in our directory:

  • my-element.css (probably under another name, using esbuild's chunk naming)
  • bundle.js

Finally, the build:css script then replaces (via the -r flag) those CSS files with copies which now have the tailwind styles injected.

We now have a build script! You can run this via npm run build and should see two new files appear as mentioned before.

You may have noticed one thing we have lost by having our CSS in external files: we can no longer template variables into our CSS like so:

There are many challenges in supporting this usage and still passing it through tailwind. For this reason, it is not recommended.

Instead, in places we really need to do this, we should use CSS variables or do the computation statically at build-time (replace values in our CSS at build time).

To solve this with CSS variables, we can do the following:

my-element.css:

my-element.ts:

The initial styles entry in this case is only being used for setting the values of some CSS variables, and never used for actual styling. We can then reference those variables in our external CSS files.

An example repository very similar to this guide is available here:

https://github.com/43081j/tailwind-lit-example