In my current Phoenix project, I'm dealing with to separate sets of CSS files. The frontend is still using plain CSS while the admin side is using Tailwind. As Elixir Phoenix defaults to Tailwind CSS, you have some extra configuration work to do to have the toolchain build and process both variants.

In my project, I have two entry points in the assets/css folder. One is "app.css" (the Tailwind version), the other one is public.css (the plain CSS file one). The directory structure looks like this:

$ ls -lh assets/css
total 56
-rw-r--r--  1 pclaerhout  staff   174B Oct 21 13:26 app.css
-rw-r--r--  1 pclaerhout  staff    22K Nov 20 14:24 public.css

We'll need to tell mix to build both files.

If you open the config/config.exs file, you'll see something like this by default:

config :tailwind,
  version: "3.4.3",
  my_app: [
    args: ~w(
      --config=tailwind.config.js
      --input=css/app.css
      --output=../priv/static/assets/app.css
      --minify
    ),
    cd: Path.expand("../assets", __DIR__)
  ]

We'll start with adding another entry there for the public.css file:

config :tailwind,
  version: "3.4.3",
  my_app: [
    args: ~w(
      --config=tailwind.config.js
      --input=css/app.css
      --output=../priv/static/assets/app.css
      --minify
    ),
    cd: Path.expand("../assets", __DIR__)
  ],
  public: [
    args: ~w(
      --config=tailwind.config.js
      --input=css/public.css
      --output=../priv/static/assets/public.css
      --minify
    ),
    cd: Path.expand("../assets", __DIR__)
  ]

As you can see, I've simply duplicated the my_app entry, renamed it and updated the paths in there.

The next thing you need to do is to reconfigure the mix assets.build and mix assets.deploy commands to also build and deploy that second file. If you check your mix.exs file under aliases, you will see that the command assets.build and assets.deploy are not really commands but aliases which invokes other commands. By default, running mix assets.build runs these commands:

mix tailwind my_app
mix esbuild my_app

Running mix assets.deploy runs:

mix tailwind my_app --minify
mix esbuild my_app --minify
mix phx.digest

To also build the public.css file, we can update the aliases to:

defp aliases do
  [
    setup: ["deps.get", "ecto.setup", "assets.setup", "assets.build"],
    "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
    "ecto.reset": ["ecto.drop", "ecto.setup"],
    test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
    "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
    "assets.build": [
      "tailwind my_app",
      "tailwind public", # <-- Added this one
      "esbuild my_app"
    ],
    "assets.deploy": [
      "tailwind my_app --minify",
      "tailwind public --minify", # <-- Added this one
      "esbuild my_app --minify",
      "phx.digest"
    ]
  ]
end

You also have to update the assets.deploy command to include the public profile.

If you now run mix assets.build or mix assets.deploy, both sets will be built correctly.

To use the stylesheets in a template, you can simply refer to them:

<link phx-track-static rel="stylesheet" href={~p"/assets/public.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />

If you want to know more details about asset handling and deployment, it's covered in the documentation: