Optimize Tailwind CSS in Django

Michael Yin

Last updated on November 17 2021

Table of Contents

Django Tailwind CSS, Alpine.js Tutorial Series:

  1. Introduction
  2. How to Setup Tailwind CSS with Django (Part 1)
  3. How to Setup Tailwind CSS with Django (Part 2)
  4. Optimize Tailwind CSS in Django
  5. Render Django Form with Tailwind CSS Style
  6. Integrate Alpine.js with Django (Part 1) (coming soon)
  7. Integrate Alpine.js with Django (Part 2) (coming soon)
  8. Build Task List with Tailwind CSS, Alpine.js and Django (coming soon)
  9. Django Form Validation in Tailwind Modal (Alpine.js) (coming soon)
  10. Django Form Validation in Tailwind Modal (Alpine.js + HTMX) (coming soon)
  11. How to deploy Django Tailwind CSS project with Docker (coming soon)

The source code is on Github/django-tailwind-alpine-htmx, demo is on Heroku

Objectives

By the end of this chapter, you should be able to:

  1. How to optimize Tailwind using PurgeCSS
  2. What is Tailwind JIT mode and the benefit.
  3. How to make web page auto reload on code change during development

Problem

To make the development experience as productive as possible, Tailwind generates thousands of utility classes for you, most of which you probably won’t actually use.

So we can remove the unused css code from the final built css to reduce the file size, which can mek the page load faster.

PurgeCSS

Let's do a test.

$ cd frontend
$ npm run build
$ ls -ahl build/css

4.8M app.aa4649b1a8cb309921b1.css

Let's update frontend/tailwind.config.js

const Path = require("path");
const pwd = process.env.PWD;

// We can add current project paths here
const purgePaths = [
  Path.join(pwd, "../django_tailwind_app/templates/**/*.html"),
  // add js file paths if you need
];

console.log(`tailwindcss purge by scanning ${purgePaths}`);

module.exports = {
  purge: purgePaths,
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

Notes:

  1. Here we defined purgePaths variables, which contains all paths we want PurgeCSS to scan
  2. We set purge: purgePaths, to make it work.

Let's test again:

$ npm run build

tailwindcss purge by scanning django_tailwind_project/django_tailwind_app/templates/**/*.html

$ ls -ahl build/css

14K app.aa4649b1a8cb309921b1.css

As you can see, now the built CSS file size is very small!

JIT

Tailwind CSS v2.1 introduces a new just-in-time compiler for Tailwind CSS that generates your styles on-demand as you author your templates instead of generating everything in advance at initial build time.

Let's update frontend/tailwind.config.js to set the mode option to jit

const Path = require("path");
const pwd = process.env.PWD;

const purgePaths = [
  Path.join(pwd, "../django_tailwind_app/templates/**/*.html"),
];

console.log(`tailwindcss purge by scanning ${purgePaths}`);

module.exports = {
  mode: 'jit',                         // new
  purge: purgePaths,
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

This will make the Tailwind build faster during the development, it can also help us find problem of the purge feature when developing.

Purgeable HTML

If we enable purge in Tailwind CSS, we should be careful when writing template.

<h1 class="text-{% if True %}4xl{% endif %} mb-4">Hello, world!</h1>

PurgeCSS can not understand text-{% if True %}4xl{% endif %}, so text-4xl will not be added to the final css file.

Instead, we should use

<h1 class="{% if True %}text-4xl{% endif %} mb-4">Hello, world!</h1>
  1. Don't use string concatenation to create class names
  2. If we want to add class in the JS, we should also select a complete class name

3-party package

Sometimes, we might use 3-party packages which use some Tailwind css classnames, to make it work with PurgeCSS, we should let Tailwind scan the paths. So Tailwind can know the css class names which are used and add them to the final built css file.

Solution 1

We can write safelist.txt, which contains some css classnames, and add it to the purge array.

Or we can even use some tools to generate it. tailwind-safelist-generator

Solution 2

Or we can tell Tailwind to scan the package code.

const Path = require("path");
const pwd = process.env.PWD;
const pySitePackages = process.env.pySitePackages;

// We can add current project paths here
const projectPaths = [
  Path.join(pwd, "../django_tailwind_app/templates/**/*.html"),
  // add js file paths if you need
];

// We can add 3-party python packages here
let pyPackagesPaths = []
if (pySitePackages){
  pyPackagesPaths = [
    Path.join(pySitePackages, "./python_package/**/*.html"),
    Path.join(pySitePackages, "./python_package/**/*.py"),
    Path.join(pySitePackages, "./python_package/**/*.js"),
  ];
}

const purgePaths = [...projectPaths, ...pyPackagesPaths];
console.log(`tailwindcss purge by scanning ${purgePaths}`);

module.exports = {
  mode: 'jit',
  purge: purgePaths,
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

Notes:

  1. As you can see, we set Python site packages path as ENV variable pySitePackages
  2. It will scan html, py and js of the python_package.
  3. I will talk more about this solution in later chapter.

LiveReload

By default, the webpack-dev-server will reload/refresh the page when file changes are detected

Edit frontend/webpack/webpack.config.dev.js to remove below code.

inline: true,
hot: true

from the devServer object.

  1. Under frontend, restart npm run start

Our frontend/tailwind.config.js look like this

const Path = require("path");
const pwd = process.env.PWD;

const purgePaths = [
  Path.join(pwd, "../django_tailwind_app/templates/**/*.html"),
];

console.log(`tailwindcss purge by scanning ${purgePaths}`);

module.exports = {
  mode: 'jit',
  purge: purgePaths,
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

Run Django server

(env)$ python manage.py runserver

Now check on http://127.0.0.1:8000/, and in the console of the devtools, you will see:

[WDS] Live Reloading enabled.

Next, let's update django_tailwind_app/templates/index.html to change the css

We will see if we change the code in the Django template, the page will reload automatically. You do not need to manual refresh the page anymore!

Workflow

  1. When we fist visit the page, a Websocket connection is established to ws://127.0.0.1:9091/sockjs-node/998/n0fibtlv/websocket
  2. webpack-dev-server is serving on 127.0.0.1:9091
  3. When we update our Django template, Tailwind will regenerate the css because of the jit mode.
  4. Since the final css change, webpack-dev-server send message through Websocket to let the page reload again.

There is another tool Browsersync which can do the same work, django-tailwind use this tool because it does not use Webpack

Django Tailwind CSS, Alpine.js Tutorial Series:

  1. Introduction
  2. How to Setup Tailwind CSS with Django (Part 1)
  3. How to Setup Tailwind CSS with Django (Part 2)
  4. Optimize Tailwind CSS in Django
  5. Render Django Form with Tailwind CSS Style
  6. Integrate Alpine.js with Django (Part 1) (coming soon)
  7. Integrate Alpine.js with Django (Part 2) (coming soon)
  8. Build Task List with Tailwind CSS, Alpine.js and Django (coming soon)
  9. Django Form Validation in Tailwind Modal (Alpine.js) (coming soon)
  10. Django Form Validation in Tailwind Modal (Alpine.js + HTMX) (coming soon)
  11. How to deploy Django Tailwind CSS project with Docker (coming soon)

The source code is on Github/django-tailwind-alpine-htmx, demo is on Heroku


Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, and modern frontend tech.

He has published some ebooks on leanpub and tech course on testdriven.io.

He is also the founder of the AccordBox which provides the web development services.


Table of Contents

Frontend Guide For Python Dev

This FREE guide help Python developers to learn the Modern frontend tech

Learn More

This book will teach you how to build a SPA (single-page application) with React and Wagtail CMS

Read More

Subscribe

Get notified about new great Web Development Tutorial