Skip to content
Development
Skill

/phoenix-tailwind-daisyui

Use when styling Phoenix 1.8 UI — Tailwind v4 app.css (@source, @plugin, no tailwind.config.js), dynamic classes, daisyUI themes/dark mode, core_components (<.input>, <.button>, <.icon>), Layouts.app, flash, accessibility. Not for LiveView forms/lifecycle or JS hooks (use

BOOST
From plugin
elixir-phoenix-guide
16924 skills3 hooks
Install
$ npx -y skills add j-morgan6/elixir-phoenix-guide --skill phoenix-tailwind-daisyui --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/phoenix-tailwind-daisyui

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when styling Phoenix 1.8 UI — Tailwind v4 app.css (@source, @plugin, no tailwind.config.js), dynamic classes, daisyUI themes/dark mode, core_components (<.input>, <.button>, <.icon>), Layouts.app, flash, accessibility. Not for LiveView forms/lifecycle or JS hooks (use

SKILL.md

phoenix-tailwind-daisyui.SKILL.md
name: phoenix-tailwind-daisyui
description: "Use when styling Phoenix 1.8 UI — Tailwind v4 app.css (@source, @plugin, no tailwind.config.js), dynamic classes, daisyUI themes/dark mode, core_components (<.input>, <.button>, <.icon>), Layouts.app, flash, accessibility. Not for LiveView forms/lifecycle or JS hooks (use phoenix-liveview-essentials)."

Phoenix UI: Tailwind v4, daisyUI and core_components

> Verified against Phoenix 1.8, LiveView 1.2, Tailwind 4.3, daisyUI 5.5 — October 2026

RULES — Follow these with no exceptions

1. **Wrap every page in `<Layouts.app flash={@flash}>`**, and pass `current_scope={@current_scope}` when the app has auth. In 1.8 the app layout is a function component that you call from the template; it is not applied automatically. It already renders the flash group, so don't render a second one. 2. **Tailwind is configured in `assets/css/app.css`.** There is no `tailwind.config.js`. Keep `@import "tailwindcss" source(none)`, and add an `@source` line for every directory outside `lib/my_app_web` that contains class names. Tailwind scans only the listed paths. 3. **Write every class name as a complete literal.** `"text-#{color}-500"` produces no CSS. Map values to full strings (`%{error: "badge-error"}`) or use function clauses. Safelist with `@source inline("...")` only when the values really come from data. 4. **Use daisyUI's semantic colors rather than raw palette colors.** Write `bg-base-100`, `text-base-content`, `btn-primary`, `text-error` and the `*-content` pairs, not `bg-white`, `text-gray-900` or `text-red-600`. The semantic colors switch with the light/dark theme; hard-coded colors don't. 5. **Use `core_components.ex` first, and extend it rather than writing one-off markup.** To get a new look, add a variant, an attr or a new component in that module, and keep its `attr`/`slot` declarations, including `values:` and `required:`. 6. **Passing `class` to `<.button>` or `<.input>` replaces the default classes; it doesn't add to them.** `<.button class="w-full">` loses `btn`. Pass the full class list (`class="btn btn-primary w-full"`) or add a variant. 7. **Icons are `<.icon name="hero-…">` with a literal name.** The suffixes are none (outline 24), `-solid`, `-mini` and `-micro`. `icon/1` accepts only `name` and `class`; any other attribute is a compile warning and isn't rendered. 8. **`<.button>` declares only `href navigate patch method download name value disabled`.** `type="button"` (which you need for a non-submitting button inside a form) is an "undefined attribute" warning, and that warning fails `mix precommit` (`compile --warnings-as-errors`). Add `type` to the button's `include:` list. 9. **Accessibility you add yourself:** a `label` on every `<.input>` (a placeholder is not a label), an `aria-label` on every icon-only button, `<button>` or `<.link>` for anything clickable (never `phx-click` on a `div`), and `<.focus_wrap>` around modal content.

Related skills: `phoenix-liveview-essentials` covers forms with `to_form`, streams, JS commands and colocated hooks. `deployment-gotchas` covers `assets.deploy` and the digest in releases.

---

What the generator sets up

| Piece | Where | What it is | |---|---|---| | Tailwind CLI | `{:tailwind, "~> 0.5"}` in `mix.exs`; `config :tailwind, version: "4.3.3", my_app: [...]` in `config/config.exs` | A standalone binary (no Node) that `mix tailwind.install` downloads to `_build/tailwind-<target>-<version>` | | daisyUI | `{:daisyui, github: "saadeghi/daisyui", tag: "v5.5.20", sparse: "packages/bundle", app: false, compile: false}` in `mix.exs` | A git dep that lands in `deps/daisyui/packages/bundle/`; there is no npm | | Heroicons | `{:heroicons, github: "tailwindlabs/heroicons", tag: "v2.2.0", sparse: "optimized", ...}` | SVGs in `deps/heroicons/optimized`, turned into `hero-*` classes by `assets/vendor/heroicons.js` | | Entry CSS | `assets/css/app.css` | All Tailwind and daisyUI configuration | | Dev watcher | `tailwind: {Tailwind, :install_and_run, [:my_app, ~w(--watch)]}` in `config/dev.exs` | Rebuilds on save | | Aliases | `assets.setup`, `assets.build` (`compile`, `tailwind my_app`, `esbuild my_app`), `assets.deploy` (`tailwind my_app --minify`, `esbuild my_app --minify`, `phx.digest`) | |

`@plugin "daisyui/packages/bundle/daisyui"` resolves because the tailwind profile sets `env: %{"NODE_PATH" => [Path.expand("../deps", __DIR__), Mix.Project.build_path()]}`, so bare imports are looked up in `deps/` and `_build/<env>/`. To upgrade daisyUI, bump the `tag:` and run `mix deps.update daisyui`. To upgrade Tailwind, bump `version:` in `config.exs` and run `mix tailwind.install`. Apps generated by earlier 1.8.x releases may vendor the plugin as `assets/vendor/daisyui.js` and `daisyui-theme.js` and point `@plugin` at `../vendor/...` instead. Check which form your app uses before you edit anything.

app.css, annotated

@import "tailwindcss" source(none);            /* turn OFF automatic source detection */
@import "phoenix-colocated/my_app/colocated.css";  /* LiveView ColocatedCSS output (in _build) */
@source "../css";
@source "../js";                               /* classes toggled from JS */
@source "../../lib/my_app_web";                /* every HEEx template and component */
@source "../../_build/dev/phoenix-colocated/my_app/*/";  /* dev reload of colocated CSS */

@plugin "../vendor/heroicons";                 /* hero-* classes */
@plugin "daisyui/packages/bundle/daisyui" { themes: false; }   /* components, no built-in themes */
@plugin "daisyui/packages/bundle/daisyui-theme" { name: "dark"; prefersdark: true; /* --color-* */ }
@plugin "daisyui/packages/bundle/daisyui-theme" { name: "light"; default: true; /* --color-* */ }

@custom-variant phx-click-loading (.phx-click-loading&, .phx-click-loading &);
@custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &);
@custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &);
@custom-variant
Read more
Ships withelixir-phoenix-guide

· Changelog A Claude Code plugin (with Codex support for the skills) that keeps Claude's Elixir and Phoenix code idiomatic and current.

Get the whole plugin

Other skills on elixir-phoenix-guide.