Phoenix Support for Tailwind V3
Chris McCord posted a blog announcing new library to provide TailwindCSS support without
using NPM
. The library downloads the new Tailwind CLI released as a part of Tailwind V3.
This means that if you use Tailwind for your application you can remove the NPM
dependency and the libraries previously required to minify
your CSS
as a part of the assets
build pipeline (autoprefixer
,postcss
, postcss-import
). Less is always better so I was keen to give it ago.
This Blog
I previously wrote about moving to fly.io which references the instructions for adding
Tailwind and using esbuild
. Below are the steps I used to remove the dependendices I had added as part of upgrading tp Phoenix 1.6
.
Delete Some Files
First I removed the following files:
/met/assets/package-lock.json
/met/assets/package.json
/met/assets/postcss.config.js
Then I deleted the node_modules
directory. That felt really good :)
Modify Configurations
Following the instructions in the library documentation I made the
following changes. All of these changes are a copy/paste
from the instructions.
/met/config/config.exs
...
config :tailwind,
version: "3.0.10",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]
...
/met/config.dev.exs
...
watchers: [
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
]
...
/met/mix.exs
...
"assets.deploy": [
"tailwind default --minify",
"esbuild default --minify",
"phx.digest"
]
...
The final change was to update the /met/assets/tailwind.config.js
to remove purge
and replace it with content
.
/met/assets/tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
content: [ # <- this is new
'./js/**/*.js', # <- this is new
'../lib/*_web.ex', # <- this is new
'../lib/*_web/**/*.*ex' # <- this is new
],
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
}
}
},
variants: {},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio')
]
};
Fly.io DockerFile
After testing that the application worked locally (Which it did), it was time to update the DockerFile
used by
fly.io
. The two changes were to remove node
and npm
from the build and to remove npm install
. I have included
the whole file for reference.
ARG BUILDER_IMAGE="hexpm/elixir:1.12.3-erlang-24.1.4-debian-bullseye-20210902-slim"
ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"
FROM ${BUILDER_IMAGE} as builder
# install build dependencies
# RUN apt-get update -y && apt-get install -y build-essential git nodejs npm \
# && apt-get clean && rm -f /var/lib/apt/lists/*_*
RUN apt-get update -y && apt-get install -y build-essential git \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
# prepare build dir
WORKDIR /app
# install hex + rebar
RUN mix local.hex --force && \
mix local.rebar --force
# set build ENV
ENV MIX_ENV="prod"
# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config
# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile
# COPY priv priv - Moved down
# Compile the release
COPY lib lib
COPY priv priv
COPY assets assets
# RUN cd assets && npm install
RUN mix assets.deploy
RUN mix compile
# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/
COPY rel rel
RUN mix release
# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}
RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
WORKDIR "/app"
RUN chown nobody /app
# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/prod/rel ./
USER nobody
# Create a symlink to the application directory by extracting the directory name. This is required
# since the release directory will be named after the application, and we don't know that name.
RUN set -eux; \
ln -nfs /app/$(basename *)/bin/$(basename *) /app/entry
CMD /app/entry start
The whole process took me about 15 minutes. Thanks to Chris and the Phoenix Team for another great library.