Adding Tailwind CSS to a Phoenix project
Koen van Gilst / December 27, 2021
5 min read • ––– views
Last week the Phoenix team published an installer that allows you to add Tailwind CSS to your project without adding NPM or any other additional dependencies. I gave it a try over the weekend, but I ran into some issues. To make it easier for others I've written this step-by-step guide to get you up and running.
I'm new to Phoenix myself, so I tried to spell out every step I took. Even things that are (probably) obvious to more experienced Phoenix developers. For people that want to skip to the end, here's a link to the result of this tutorial on Github.
Start with a new project
Create a new Phoenix project by running the following command:
mix phx.new phoenix_tailwind
Answer yes
to install the dependencies and follow the instructions to start the Phoenix app.
Installing Tailwind
Next, we'll add the new Tailwind dependency to our project. In the mix.exs
file, add the following to the deps
section:
defp deps do
[
{:phoenix, "~> 1.6.4"},
{:tailwind, "~> 0.1", runtime: Mix.env() == :dev},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.6"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.17.5"},
# etc...
]
end
In config/config.exs
specify the Tailwind version you want to use:
config :esbuild,
version: "0.13.5",
default: [
args:
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]
config :tailwind,
version: "3.0.7",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
In the assets
folder, create a tailwind.config.js
file with:
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration
module.exports = {
content: ['./js/**/*.js', '../lib/*_web.ex', '../lib/*_web/**/*.*ex'],
theme: {
extend: {}
},
plugins: [require('@tailwindcss/forms')]
};
Run the following command to install the new Tailwind dependency:
mix deps.get
And then run the Tailwind task:
mix tailwind.install
mix tailwind default
Adding a Tailwind watcher
During development we want the Tailwind CLI to keep watching our files for changes. We'll add the following watcher to our config/dev.exs
file:
watchers: [
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]},
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
]
Adding minification for production builds
In production we want Tailwind to minify our CSS files. To make this happen, update the following line in the mix.exs
file:
defp aliases do
[
setup: ["deps.get"],
"assets.deploy": ["esbuild default --minify", "tailwind default --minify", "phx.digest"]
]
end
Loading Tailwind
We're ready with the configuration on the Phoenix side. All we have to do now is make sure the Tailwind classes get loaded in our main assets/css/app.css
file:
@tailwind base;
@tailwind components;
@tailwind utilities;
/* import './phoenix.css'; */
body {
@apply bg-gray-900;
}
/* Alerts and form errors used by phx.new */
I've had to remove the default styling from Phoenix (i.e. Phoenix.css
), as I couldn't get it to work with Tailwind. There's an open issue for this on Github and I'll update this tutorial accordingly if there's a solution.
I've also changed the body styling here to illustrate that the @apply
functionality works (and to make the page look nice). Since Tailwind is now taking care of our CSS, we should comment out the following line in assets/js/app.js
:
// We import the CSS which is extracted to its own file by esbuild.
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
// import "../css/app.css";
Adding some demo classes
Tailwind is now completely configured and ready to use. Let's take it for a spin and replace some of the default HTML with the following.
In lib/phoenix_tailwind/templates/page/index.html.heex
:
<section class="flex flex-col w-screen h-screen justify-center items-center">
<h1 class="text-6xl font-black text-gray-200">
Hello
<span
class="text-transparent bg-clip-text bg-gradient-to-br from-pink-600 to-red-200"
>Tailwind</span
>
</h1>
<p class="text-gray-200 text-xl mt-4">Let's get started!</p>
</section>
In lib/phoenix_tailwind_web/templates/layout/root.html.heex
remove the existing header. It should then look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<%= csrf_meta_tag() %>
<%= live_title_tag assigns[:page_title] || "PhoenixTailwind", suffix: " · Phoenix Framework" %>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
</head>
<body>
<%= @inner_content %>
</body>
</html>
(Re)start the server
We're now ready to take the new Phoenix app with Tailwind for a spin. Start the server with:
mix phx.server
And visit the page: http://localhost:4000/
You should see the following in your browser:
I hope you found this tutorial useful and please let me know on Twitter if anything can be improved or is missing.
I was unable to get the Tailwind CSS IntelliSense plugin to work using this
setup. It seems to rely on Tailwind being installed in the node_modules
folder. There is an open issue for this on
Github
and I will update this tutorial accordingly if there's a solution.