View Source Tailwind Setup

Beacon has built-in support for Tailwind, any page can use tailwind classes out of the box and a stylesheet will be generated and served to style the pages correctly.

A default and simple configuration is already provided by Beacon, if you don't need custom config or plugins then you can skip this guide. Otherwise keep reading to set up a custom Tailwind configuration with more advanced features.

Objective

Make sure the proper Tailwind version is installed, create a Tailwind config in the ESM format, and if you intend to use plugins, then bundle everything together in a single module.

Steps

  • Install Tailwind v3.3.0 or higher
  • Install Esbuild
  • Tailwind config in the ESM format
  • Heroicons
  • Install plugins
  • Bundle the config
  • Use the config in your site configuration

Let's go through each one to set up Tailwind properly for your sites.

Tailwind v3.3.0 or higher

Any recent Phoenix application should have the tailwind library already installed and updated but let's double check by executing:

mix run -e "IO.inspect Tailwind.bin_version()"

If it fails or the version is lower than 3.3.0 then follow the tailwind install guide to get it installed or updated. It's important to install a recent Tailwind version higher than 3.3.0

Esbuild

Similar to Tailwind, any recent Phoenix application should have it installed already but let's check by executing:

mix run -e "IO.inspect Esbuild.bin_version()"

If it fails then follow the esbuild install guide to get it installed. Any recent version that is installed should work just fine.

Config in the ESM format

We need the tailwind config file in the ESM format, ie: default export instead of module.exports. That's because Beacon uses Tailwind to generate stylesheets for published pages (your deployed site) and also to preview pages in the Visual Editor (in your admin interface). The former uses the tailwind-cli binary (a node application) while the latter compiles the stylesheet in the browser, so we need to reuse the same config file in both environments and ESM is the format that works in this scenario.

Most likely the existing config assets/tailwind.config.js was created in the CommonJS format, so given a file like this:

module.exports = {
  theme: {
    colors: {
      'blue': '#1fb6ff'
    }
  }
}

Replace how the module is exported to have a valid ESM config:

export default {
  theme: {
    colors: {
      'blue': '#1fb6ff'
    }
  }
}

More info at https://tailwindcss.com/blog/tailwindcss-v3-3#esm-and-type-script-support

Heroicons

Some built-in components use Heroicons, which must be configured properly to let the Tailwind compiler find the SVG icon files.

See the Heroicons guide for more information.

Install plugins

The default config generated by Phoenix requires the @tailwindcss/froms plugin so we need to install it first. Execute in the root of your project:

npm install --prefix assets --include=dev @tailwindcss/forms

Bundled config

We'll change 3 files to make it work:

  1. config/config.exs

Open the file config/config.exs, find the :esbuild config, and add a new tailwind_bundle that will look like this:

config :esbuild,
  version: "0.23.0",
  my_app: [
    # omitted for brevity
  ],
  # add this block
  tailwind_bundle: [
    args: ~w(tailwind.config.js --bundle --platform=node --format=esm --target=es2020 --outfile=../priv/tailwind.config.bundle.js),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]
  1. config/dev.exs

Open the file config/dev.exs, find the :watchers key in the endpoint config, and add a new tailwind_bundle that will look like this:

config :my_app, MyAppWeb.Endpoint,
  # omitted for brevity
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:my_app, ~w(--sourcemap=inline --watch)]},
    esbuild: {Esbuild, :install_and_run, [:tailwind_bundle, ~w(--watch)]}, # add this line
    tailwind: {Tailwind, :install_and_run, [:my_app, ~w(--watch)]}
  ]
  1. mix.exs

In the list of aliases, add the following command in both "assets.build" and "assets.deploy":

"esbuild tailwind_bundle"

It will look like this:

defp aliases do
  [
    # omitted for brevity
    "assets.build": ["tailwind my_app", "esbuild my_app", "esbuild tailwind_bundle"],
    "assets.deploy": [
      "tailwind my_app --minify",
      "esbuild my_app --minify",
      "esbuild tailwind_bundle",
      "phx.digest"
    ]
  ]
end

Site Configuration

Note that if you're setting up the environment for the first time and have no site created yet, for example if you're following the "Your first site" or the "Create a blog" guide, you won't have any site configuration to update. In this case, you can skip this step and come back to it later after executing the beacon.install command.

Open the file lib/my_app/application.ex (replace my_app with your actual application name), find the configuration of the site you'll be using this Tailwind config and add the tailwind_config key pointing to the bundled file:

tailwind_config: Path.join(Application.app_dir(:my_app, "priv"), "tailwind.config.bundle.js"),

It will look somewhat like this:

@impl true
def start(_type, _args) do
  children = [
    # omitted for brevity
    {Beacon,
     sites: [
       [
         site: :my_site,
         repo: MyApp.Repo,
         endpoint: MyAppWeb.Endpoint,
         router: MyAppWeb.Router,
         tailwind_config: Path.join(Application.app_dir(:my_app, "priv"), "tailwind.config.bundle.js") # add this line
       ]
     ]},
    MyAppWeb.Endpoint
  ]

  # omitted for brevity
end

Remember to replace my_app with the actual name of your application.