DEV Community

James Garbutt
James Garbutt

Posted on

Using tailwind v3 with lit elements

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:

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:

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 to process our source files' CSS.

As part of that, PostCSS will call tailwind to handle the processing of tailwind's classes and what not.

We might also call 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:

module.exports = {
  syntax: 'postcss-lit',
  plugins: {
    tailwindcss: {
      config: './tailwind.config.cjs'
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

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.
  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:

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

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

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:

{
  "extends": "stylelint-config-standard",
  "customSyntax": "postcss-lit"
}
Enter fullscreen mode Exit fullscreen mode

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:

npx postcss -r my-element.js
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
twokay profile image
Tom Shaw • Edited

Can this be updated to work with the updated starter project and show an example of tailwind being used inside a component?

The only parts that need altering for anyone else wondering:

my-element.js - add in the base styles for tailwind

MyElement.styles = css `
      @tailwind base;
      @tailwind components;
      @tailwind utilities;
    :host {
      display: block;
      border: solid 1px gray;
      padding: 16px;
      max-width: 800px;
    }
  `;
Enter fullscreen mode Exit fullscreen mode

update a style inside your html (I added bg-amber-900 into the H1 element)

    render() {
        return html `
      <h1 class="bg-amber-900">${this.sayHello(this.name)}!</h1>
      <button @click=${this._onClick} part="button">
        Click Count: ${this.count}
      </button>
      <slot></slot>
    `;
Enter fullscreen mode Exit fullscreen mode

And you get all the tailwind goodness :)

MyElement.styles = css `
/*
! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
*//*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/

*,
::before,
::after {
  box-sizing: border-box; /* 1 */
  border-width: 0; /* 2 */
  border-style: solid; /* 2 */
  border-color: #e5e7eb; /* 2 */
}
...etc
Enter fullscreen mode Exit fullscreen mode