Learn Elixir and Phoenix: Add Tailwind CSS

Second post in my series to learn Elixir and Phoenix. In this post we talk about adding Tailwind CSS to a Phoenix project.

A cup of coffee is a good thing to start a journey into Tailwind CSS.

Here is the second post in my series about learning Elixir and Phoenix by building a read-it-later services. Last time I outlined my little learning project and did the initial setup.

This time I want to add my preferred CSS Framework Tailwind CSS to the project. This approach can be applied to any other fresh Phoenix project.

For those who do not know Tailwind CSS yet, it is a utility-first atomic CSS framework that lets you build custom designs without leaving your HTML. A bit like Bootstrap, but more atomic and without any custom components. I switched over to it because I am sick of overriding components and fighting against presets.

The assets folder

In the last post I described the asset folder like that

assets contains the typical static files (CSS, JS, images). But it also contains a preconfigured modern asset pipeline based on webpack.

Actually, it is a bit more. There is an app.js file inside this folder, that connects the styling, that is based on SASS, and the necessary code to drive the Channel and LiveView features of Phoenix. All these things are processed and bundled by webpack. So in the end you have a small JavaScript project inside this folder, that you can extend with your code where necessary.

Planned changes

  • Remove SASS and add PostCSS. Tailwind is based on PostCSS and I prefer to only have one preprocessor in my project.
  • Add Tailwind CSS.
  • Configure PurgeCSS to clean up the resulting CSS file based on the code of the lib folder.

Step 1: Remove SASS

npm uninstall sass-loader node-sass

Step 2: Add Tailwind CSS and PostCSS

npm install -D tailwindcss postcss-loader

Step 3: Create an empty Tailwind CSS config

Tailwind CSS can be highly customized. This happens inside a file named tailwind.config.js, which we create in the assets folder with the following command.

npx tailwind init

The resulting file looks like that.

module.exports = { purge: [], theme: { extend: {}, }, variants: {}, plugins: [], }

Step 4: Configure PurgeCSS

If we don’t configure PurgeCSS, which is already part of Tailwind CSS, the resulting CSS File would start at 1.7 megabytes. Way to large. PurgeCSS removes all unused CSS rules and strips down the file to a reasonable size. The tool is configured in the tailwind.config.js we just created.

To do its job, PurgeCSS must know all the files it should check for CSS class names. A sane default for a Phoenix project looks like that.

module.exports = { purge: [ "../**/*.html.eex", "../**/*.html.leex", "../**/views/**/*.ex", "../**/live/**/*.ex", "./js/**/*.js", ], theme: { extend: {}, }, variants: {}, plugins: [], };

If you want to learn more about this, take a look at the guide Controlling File Size, which is part of the Tailwind CSS documentation.

Step 5: Configure PostCSS

To configure PostCSS we create a file named postcss.config.js in the assets folder with following content.

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

Step 5: Change app.css to include Tailwind CSS

Rename assets/css/app.scss to assets/css/app.css and replace the original content with the following lines to import Tailwind CSS.

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

Then you can safely remove assets/css/phoenix.css from the project and change the import of the CSS file in assets/js/app.js at the top of the file to look like that.

// We need to import the CSS so that webpack will load it. // The MiniCssExtractPlugin is used to separate it out into // its own CSS file. import "../css/app.css";

Step 6: Configure webpack

Now that all the tools we want to use are configured, we need to change the webpack.config.js in the assets folder. 

Just replace the rule for CSS files with the following code.

{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader", "postcss-loader" ], },

Step 7: Change package.json

The last step is, that you have to change a line in the file assets/package.json to set the NODE_ENV environment variable for production builds.

"deploy": "NODE_ENV=production webpack --mode production"

Optional Step 8: Apply Tailwind CSS to the HTML

For this project and the source code in the github project, I applied Tailwind CSS to the default landing page of a Phoenix project. The solution is somewhat rough, but you get an idea and can also see in the files assets/tailwind.config.js and assets/css/app.css how to extend Tailwind with plugins or apply it to HTML elements. In the further course of the project I will implement the whole thing cleaner.

Optional Step 9: Tweak your editor (VSCode-only)

If you are using VSCode like I do, I suggest installing the extensions for Stylelint and Tailwind CSS Intellisense. The project on github includes a valid setup for stylelint. Just look into the file assets/stylelint.config.js.

I described the necessary steps to configure this in a seperate post.

Where do all the files from the JavaScript project end up?

That was a question I asked me several times, when I tried to verify the output of the JavaScript project. The results created by webpack can be found in the priv/static folder in the root directory of the Phoenix project.

SourceDestination
./assets/css/*./priv/static/css/
./assets/js/*./priv/static/css/
./assets/static/*./priv/static/

Summary

Now we are done with the next step in the project. I hope this was less boring than the initial post with the standard setup.

Still, not much Elixir and Phoenix, but I learned something new about the anatomy of a Phoenix project and how production builds work. And I have my beloved CSS framework in place.

The next steps are either user administration, registration and authentication. Or I will add some models and controllers and then go over to user management. 

But I think that user management should be next. I come from Django, and I was always glad that the topic was off the table right at the beginning. 

The code

I share the code to this project on github. Every post gets its own tag on the repository, so you can easily switch to the code of a given post.

oliverandrich/learn-elixir-and-phoenix-project


Reference & Credits