Tailwind CSS
I absolutely love working with Tailwind CSS. It allows me to focus on development without worrying too much about the design. Tailwind CSS provides almost everything you need out of the box, so you rarely switch from your template to your stylesheet and back. Tailwind CSS is a utility first framework, meaning that it uses a set of utility classes, and you decide what utilities you need for your design (e.g., pt-4, text-center and rotate-90). Although it makes for some pretty ugly HTML, but for all my projects, it has been an absolute time-saver! Unfortunately, it is not so easy to configure Tailwind CSS for Wagtail CMS. I hope this blog post will guide you through this process.
Installing Tailwind CSS in Wagtail CMS
In earlier blog posts I already talked about using Wagtail CMS in a Docker container (e.g., Dockerize Wagtail CMS for your development environment). For our Wagtail CMS project we use the file structure below:
my-project/ ├── website/ │ ├── apps/ │ ├── media/ │ ├── website/ │ │ ├── frontend/ │ │ ├── settings/ │ │ ├── static/ │ │ ├── templates/ │ │ ├── __init__.py │ │ ├── urls.py │ │ └── wsgi.py │ └── manage.py ├── docker-compose.yml ├── Dockerfile └── requirements.txt
Now we want to include Tailwind CSS inside this container. Of course, you could download Tailwind CSS to you web server:
<link href="static/css/tailwind.min.css" rel="stylesheet">
or use Tailwind CSS with a CDN link:
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
However, you won't be able to take full advantage of the framework (see the wagtail documentation). To get the most out of Tailwind CSS, you really should install it as a PostCSS plugin, and here it gets a bit more complicated.
To install Tailwind CSS as a PostCSS plugin you would need the Node Package Manager (npm). You could install npm in your Wagtail CMS container by adding it to the system packages in your docker file:
$ apt install nodejs npm
Alternatively, you could add a npm container in your docker-compose configuration file and bind it to your Wagtail CMS container, but there is even a much simpler solution:
$ cd website $ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npm init -y
This will run a published image npmjs/npm-docker-baseline:12-alpine and executes the npm init -y command. The npm command will initialize your project and used with the -y flag it will automatically populate all options with the default npm init values in a package.json file. But what do those flags in the docker run command mean?
- The --rm flag tells docker that the container should automatically be removed after we close docker.
- The -v flag tells docker which folders should be mount to make them accessible inside the container. In this example we link the current working directory (i.e., /my-project/website/) with the ${PWD} command to the application directory (i.e., /usr/src/app/).
- The -w flag lets the command being executed inside directory given (i.e., /usr/src/app/).
Next, we want to install Tailwind CSS:
$ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npm install --save tailwindcss
The above command will install the tailwindcss module into /node_modules in the current directory. Moreover, the --save flag will add the tailwindcss module to the package.json file. The install command will also generate a package-lock.json file, but you can ignore this file because it is updated automatically after every npm command. All modules that are listed in the package.json file will be installed with the npm install command. Often it is more effective to edit the package.json file and specify all modules you need in there. Our file structure should now look something like this:
my-project/ ├── website/ │ ├── apps/ │ ├── media/ │ ├── node_modules/ │ ├── website/ │ │ ├── frontend/ │ │ ├── settings/ │ │ ├── static/ │ │ ├── templates/ │ │ ├── __init__.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── package-lock.json │ └── package.json ├── docker-compose.yml ├── Dockerfile └── requirements.txt
Our package.json file now contains the following:
{ "name": "app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "tailwindcss": "^2.2.19" } }
Add Tailwind as a PostCSS plugin
Now we have installed Tailwind CSS, but we are not able to properly use it. We have to setup PostCSS first before we can start using Tailwind CSS. Since Tailwind CSS does not automatically add vendor prefixes to the CSS it generates, it is also recommended to install autoprefixer to handle this for you.
$ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npm install --save postcss postcss-cli autoprefixer
Note
Be aware that the npmjs/npm-docker-baseline:12-alpine container uses node 12.18.0. The latest version postcss-cli requires node 12.20.0 or higher (see postcss-cli issue). To overcome this problem, you should install postcss-cli 8.x:
$ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npm install --save postcss-cli@^8
Next, you have to add tailwindcss and autoprefixer to your postcss.config.js configuration file at the root of your project. This means that the PostCSS process should perform CSS processing with the tailwindcss and the autoprefixer plugins.
// postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, } }
Include Tailwind in your CSS
We want PostCSS to compile our stylesheets. We will place our stylesheets in the frontend/ directory and we want to compile the finished stylesheets in the static/ directory. To include Tailwind CSS in our project all we have to do is include Tailwind’s base, components, and utilities styles in our stylesheet:
/* frontend/css/website.css */ @tailwind base; @tailwind components; @tailwind utilities;
Tailwind CSS requires a build process which ensures that the Tailwind CSS code is inserted according to the directives used and the Tailwind configuration in place. So, we have to add a build script to the package.json file which compiles your stylesheets:
// An extract of the package.json file "scripts": { "test": "echo \"Error: no test specified\" && exit 1" "build": "postcss website/frontend/css/website.css -o website/static/css/website.css" },
This build script is executing PostCSS processing for file website/frontend/css/website.css (this is the CSS file in which we’ve inserted the Tailwind directives) and outputting the result to file website/static/css/website.css. To execute the build script we use:
$ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npm run build
The only thing that we have to do now is to include the stylesheet in the templates/base.html file:
<link rel="stylesheet" type="text/css" href="{% static 'css/website.css' %}">
That's it! You are now able to use Tailwind CSS in your project. But wait, there is more...
Create your custom Tailwind CSS configuration
Tailwind CSS allows you to customize what tailwind generates. This is great for typography and custom colours. You can also specify how to configure Tailwind CSS for production. Tailwind generates thousands of utility classes for you, most of which you probably won’t actually use. So, you should configure Tailwind’s purge option to tree-shake unused styles and optimize your final build size. You can configure Tailwind CSS by creating a tailwind.config.js file using the Tailwind CLI tool:
$ docker run \ --rm \ -v ${PWD}/:/usr/src/app/ \ -w /usr/src/app/ \ npmjs/npm-docker-baseline:12-alpine \ npx tailwind init
This will create a minimal tailwind.config.js file at the root of your project:
// tailwind.config.js module.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: {}, plugins: [], }
If you want to change the definition of custom fonts and custom colours you can add them to the tailwind.config.js file:
// An extract of the tailwind.config.js file theme: { extend: { colors: { brand: '#ff486a', error: '#ff6363', white: '#ffffff', black: '#000000', red: '#ff0000', }, fontFamily: { 'sans': ['"Trebuchet MS"', 'Tahoma', 'sans-serif'], 'body': ['Didot','"Times New Roman"','serif'], }, }, },
You can now use custom colours or fonts (e.g., font-sans, or text-red) in your templates. To be able to configure Tailwind’s purge option you have to specify which folders and files to scan. You can also specify a list of classes that should always be used.
// An extract of the tailwind.config.js file purge: { content: ['website/templates/**/*.html'], safelist: [], },
We scan all html files in the templates/ folder and remove all unused Tailwind CSS styles.
Note
Every time you change the tailwind.config.js file you have to run the build script for all the changes to take place.
Now we have fully integrated Tailwind CSS into our Wagtail CSM container. Your file structure should look something like this:
my-project/ ├── website/ │ ├── apps/ │ ├── media/ │ ├── node_modules/ │ ├── website/ │ │ ├── frontend/ │ │ │ └── css/ │ │ │ │ └── website.css │ │ ├── settings/ │ │ ├── static/ │ │ │ └── css/ │ │ │ │ └── website.css │ │ ├── templates/ │ │ │ └── base.html │ │ ├── __init__.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ └── tailwind.config.js ├── docker-compose.yml ├── Dockerfile └── requirements.txt
Conclusion
That's it! I hope you have a better understanding of how to integrate Tailwind CSS in your Wagtail CSM container. In this blog post I only covered how to setup and configure Tailwind CSS, but if you want to know more about Tailwind CSS I recommend to check out their website, which is filled with examples.