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

Change log:

  • 2022-01-11: Upgrade to Tailwind CSS v3

Objectives

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

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

Problem

In Tailwind CSS v1

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.

As you can guess, the final css file size is a little big

In Tailwind CSS v2, it introduced 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.

With the config in frontend/tailwind.config.js, we can tell Tailwind which css classes are used. The unused css will be removed from the final css, this decreased the final css size.

In Tailwind CSS v3, it enabled JIT (Just-in-Time) all the time.

JIT

Tailwind CSS works by scanning all of your HTML, JavaScript components, and any other template files for class names, then generating all of the corresponding CSS for those styles.

In order for Tailwind to generate all of the CSS you need, it needs to know about every single file in your project that contains any Tailwind class names.

In frontend/tailwind.config.js

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

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

const contentPaths = [...projectPaths];
console.log(`tailwindcss will scan ${contentPaths}`);

module.exports = {
    content: contentPaths,
    theme: {
        extend: {},
    },
    plugins: [],
}

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>

Tailwind 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 JIT, we have below solutions.

Solution 1

We can write safelist, which contains css classnames we need

module.exports = {
  content: [
  ],
  safelist: [
    'bg-red-500',
    'text-3xl',
    'lg:text-4xl',
  ]
  // ...
}

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 contentPaths = [...projectPaths, ...pyPackagesPaths];
console.log(`tailwindcss will scan ${contentPaths}`);

module.exports = {
  content: contentPaths,
  theme: {
    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.

Live Reload

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

Edit frontend/webpack/webpack.config.dev.js

devServer: {
  host: "0.0.0.0",
  port: 9091,
  headers: {
    "Access-Control-Allow-Origin": "*",
  },
  writeToDisk: true,
},

Notes:

  1. Remove inline: true, and hot: true from the devServer section
  2. You can also check Python Webpack Boilerplate doc to learn more.

Under frontend, restart npm run start

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 any text or 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!

There is another tool Browsersync which can do the similar 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

Change log:

  • 2022-01-11: Upgrade to Tailwind CSS v3

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

Django SaaS Template

It aims to save your time and money building your product, developed by Michael Yin

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