Setting up Tailwind CSS with Flask
Tags: flask, webdev, python, css
Published: 2020-11-16 17:48:26

Recently, I have decided to switch from Tachyons to Tailwind and while I really like Tailwind and the ecosystem around it, setting it up in my Flask project was quite a pain for me. I guess it's fairly simple if you are a front end developer and have some CSS build system in place but I was using no such thing and was simply importing all CSS libraries using the link tag. And while it's possible to import Tailwind using the link tag, most of the stuff that makes it so powerful, such as customization, is only possible when using a proper build pipeline.

## Set up your Flask app

We will be using Flask-Assets to manage our assets pipeline so let's first install it.

```bash
pip install Flask-Assets
```

Next, let's install Tailwind, PostCSS (a CSS preprocessor used by Tailwind), Autoprefixer and PurgeCSS.

```
npm install tailwindcss postcss-cli autoprefixer @fullhuman/postcss-purgecss
```

Now, in your Flask app, let's create a CSS bundle that will take all source CSS files and processes them using PostCSS.

```python
from flask_assets import Environment, Bundle

assets = Environment()
assets.init_app(app)

css = Bundle('src/css/*.css', filters='postcss', output='dist/css/main.css')
assets.register('css', css)
```

## Set up PostCSS

The tricky part was setting up the PostCSS (using the `postcss.config.js` file) because for some reason `webassets` (which is used internally by Flask-Assets) changes its working directory when executing `postcss` binary and executes it inside the directory where the currently processed CSS file is. PostCSS is able to find your `postcss.config.js` (which is typically in the root directory of your project) by walking up the directory until it finds one but Tailwind expects its config file (`tailwind.config.js`) to be in the current working directory by default so you have to explicitely tell it where to find it.

That's why we have to set all paths relative to the `postcss.config.js` file (using `__dirname` variable) instead of relative to the current working directory.

Below is our final `postcss.config.js`:

```javascript
const path = require('path');

module.exports = (ctx) => ({
    plugins: [
        require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')),
        require('autoprefixer'),
        ctx.env === 'production' && require('@fullhuman/postcss-purgecss')({
            content: [
                path.resolve(__dirname, 'templates/**/*.html')
            ],
            defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
        })
    ]
})
```

Also, note that in production we use PurgeCSS to remove all unused CSS classes by scanning all HTML templates in your project.

## Set up Tailwind CSS

Finally, let's customize Tailwind using the `tailwind.config.js`  file (in your project root) to see that all is set correctly.

```javascript
const defaultTheme = require('tailwindcss/defaultTheme')

module.exports = {
    future: {
        // removeDeprecatedGapUtilities: true,
        // purgeLayersByDefault: true,
    },
    purge: [],
    theme: {
        extend: {
            fontFamily: {
                sans: ['Inter var', ...defaultTheme.fontFamily.sans],
            },
        },
    },
    variants: {},
    plugins: [
        require('@tailwindcss/ui'),
    ],
}
```

This post was originally published on [my personal blog](https://jakubsvehla.me/posts/setting-up-tailwind-css-with-flask/).