•   #elixir   •   #phoenix   •   #tailwindcss

Configuring Tailwind CSS for Phoenix

If you haven't heard of Tailwind CSS, it's a CSS framework that provides composable, low-level utility classes to describe your user interface. I recently rewrote all of Progress Plum using Tailwind and found it to be incredibily pleasing. With that said, I will walk you through how to include Tailwind CSS in your Phoenix project.

Installing Tailwind

For all of the commands listed, they should be executed from the assets directory.

Tailwind CSS recommends using PostCSS. Add PostCSS as loader for Webpack.

# for npm
npm install postcss-loader --save-dev

# for yarn
yarn add postcss-loader --dev

You can install Tailwind using your preferred JavaScript package manager from the assets directory of your Phoenix project.

# for npm
npm install tailwindcss --save-dev

# for yarn
yarn add tailwindcss --dev

Configuring CSS Generation

Add css/tailwind.css as a new file. Add the following lines to add Tailwinds's CSS classes.

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

// Add custom classes after tailwind imports.

Also add postcss.config.js to configure PostCSS to use Tailwind.

module.exports = {
  plugins: [
    require('tailwind'),
    require('autoprefixer')
  ]
};

Update your Webpack config file. The sample below is mostly based on the generated Webpack config when you create a new project with Phoenix.

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new TerserPlugin({
        cache: true,
        parallel: true,
        sourceMap: false
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    './js/app.js': './js/app.js'
  },
  output: {
    // Update the output file name as well as path
    filename: '[name]',
    path: path.resolve(__dirname, '../priv/static/')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.css$/,
        // Use the postcss-loader
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      // create a generatated css file
      filename: './css/app.css'
    }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
  ]
});

Make sure your js/app.js file imports your Tailwind CSS file. Webpack can extract the CSS and agreggate it into a single file using MiniCssExtractPlugin.

import css from '../css/tailwind.css';

Production Build Optimizations

Tailwind generates a lot of useful utility classes to build your user interface. But it's very likely that you won't use a majority of them. To drastically reduce the file size of our production CSS file, we can use Purgecss to look through our code and remove any CSS classes that aren't being used. For perspective, I dropped Progress Plum's CSS file down to 20 KB using Purgecss, 5 KB with gzip.

Add Purgecss as a Webpack plugin. We will also need glob-all and webpack-merge to help with a production configuration.

# for npm
npm install purgecss-webpack-plugin glob-all webpack-merge --save-dev

# for yarn
yarn add purgecss-webpack-plugin glob-all webpack merge --dev

Now update your production webpack configuration for Purgecss to scan for files where you used CSS classes. If you're using JavaScript for client-side rendering, be sure to include the paths to those files so Purgecss can scan them as well.

const path = require('path');
const merge = require('webpack-merge');
const base = require('./webpack.config.js');

let glob = require("glob-all");
let PurgecssPlugin = require("purgecss-webpack-plugin");

// Extractor specific to Tailwind
class TailwindExtractor {
  static extract(content) {
    return content.match(/[A-Za-z0-9-_:\/]+/g) || [];
  }
}

let purge = new PurgecssPlugin({
  paths: glob.sync([
    path.resolve(__dirname, "../lib/my_app_web/live/**/*.ex"),
    path.resolve(__dirname, "../lib/my_app_web/templates/**/*.eex"),
    path.resolve(__dirname, "../lib/my_app_web/templates/**/*.leex"),
    path.resolve(__dirname, "../lib/my_app_web/views/**/*.ex"),
  ]),
  extractors: [
    {
      extractor: TailwindExtractor,
      extensions: ["ex", "eex", "leex"]
    }
  ]
});

module.exports = merge(base, {
  plugins: [
    purge
  ]
});

Now you're all set for PurgeCSS to do its magic.

Wrap Up

Now you should be set to start using Tailwind CSS in your Phoenix application. Check out Tailwind's docs for more information on the classes and configuration.

Alex Garibay's Picture
Alex Garibay Founder of Progress Plum. Avid Elixir developer.