Migrating to v9

View Source

This guide covers upgrading from Phoenix Duskmoon UI v6/v7/v8 to v9. The v9 release is a major rewrite — components now render as HTML Custom Elements and the CSS system has been replaced entirely.

Overview of Breaking Changes

Areav6/v7/v8v9
CSS frameworkDaisyUI@duskmoon-dev/core
Tailwind versionv3v4+
CSS plugin directive@plugin "daisyui"@plugin "@duskmoon-dev/core/plugin"
RenderingPlain HTMLHTML Custom Elements (<el-dm-*>)
Module pathsPhoenixDuskmoon.Component.ButtonPhoenixDuskmoon.Component.Action.Button
Package managernpmBun
Elixir version~> 1.12~> 1.15

Step-by-Step Migration

1. Update Elixir dependency

# mix.exs — before
{:phoenix_duskmoon, "~> 7.0"}

# mix.exs — after
{:phoenix_duskmoon, "~> 9.0"}
mix deps.get

2. Replace npm packages with bun

Remove old npm dependencies and install the new ones:

# Remove old packages
bun remove daisyui

# Install new packages
bun add @duskmoon-dev/core @duskmoon-dev/elements

@duskmoon-dev/core replaces both DaisyUI and the old phoenix_duskmoon/theme

  • phoenix_duskmoon/components CSS exports. @duskmoon-dev/elements provides the custom element packages that components render as.

3. Update CSS imports

This is the most significant change. The old CSS import pattern using DaisyUI and phoenix_duskmoon exports is replaced entirely.

/* ❌ v6/v7/v8 — REMOVE these */
@plugin "daisyui";
@import "phoenix_duskmoon/theme";
@import "phoenix_duskmoon/components";
/* ✅ v9 — ADD these instead */
@import "tailwindcss";
@plugin "@duskmoon-dev/core/plugin";

The @duskmoon-dev/core/plugin directive registers the full design system with Tailwind v4: theme variables, component classes, and design tokens. The old phoenix_duskmoon/theme and phoenix_duskmoon/components CSS export paths no longer exist.

Optional: Import built-in themes for light/dark mode support:

@import "tailwindcss";
@plugin "@duskmoon-dev/core/plugin";
@import "@duskmoon-dev/core/themes/sunshine";
@import "@duskmoon-dev/core/themes/moonlight";

4. Register custom elements in JavaScript

v9 components render as <el-dm-*> custom elements. These must be registered in your app.js:

// Register all elements at once
import "@duskmoon-dev/elements/register";

// Or register individually (smaller bundle)
import "@duskmoon-dev/el-button/register";
import "@duskmoon-dev/el-card/register";
import "@duskmoon-dev/el-input/register";
// ... add the elements you use

Without registration, components will render empty — the custom element's <template> content is inert until the element is defined.

5. Set up LiveView hooks

v9 introduces JavaScript hooks for client-side interactions. Add them to your LiveSocket:

import * as DuskmoonHooks from "phoenix_duskmoon/hooks";

let liveSocket = new LiveSocket("/live", Socket, {
  params: { _csrf_token: csrfToken },
  hooks: DuskmoonHooks,
});

If you have existing hooks, merge them:

import * as DuskmoonHooks from "phoenix_duskmoon/hooks";
import MyHooks from "./my_hooks";

let hooks = { ...DuskmoonHooks, ...MyHooks };

See the Hooks guide for details on each hook.

6. Update module imports (if importing individual modules)

Component modules have been reorganized into 8 categories. If you import individual modules directly, update the paths:

# ❌ v6/v7/v8 paths (no longer exist)
alias PhoenixDuskmoon.Component.Button
alias PhoenixDuskmoon.Component.Form.Input

# ✅ v9 paths
alias PhoenixDuskmoon.Component.Action.Button
alias PhoenixDuskmoon.Component.DataEntry.Input

If you use use PhoenixDuskmoon.Component in your view helpers (recommended), no changes are needed — all components are still imported with the same dm_ prefixed function names.

7. Update CSS class overrides

If you target DaisyUI class names in custom CSS, update them to the BEM naming convention:

/* ❌ v6/v7/v8 DaisyUI classes */
.btn-primary { ... }
.card-body { ... }
.form-control { ... }

/* ✅ v9 BEM classes */
.dm-btn--primary { ... }
.dm-card__body { ... }
.dm-input { ... }

The naming convention is:

  • Block: dm-component (e.g., dm-btn, dm-card)
  • Modifier: dm-component--variant (e.g., dm-btn--primary)
  • Element: dm-component__element (e.g., dm-card__header)

Verifying the Migration

After completing all steps:

  1. Run mix compile --warnings-as-errors to catch any removed module references
  2. Start the dev server with mix phx.server and check browser console for:
    • Missing custom element warnings (add missing import ... /register lines)
    • CSS errors (verify @plugin directive is correct)
  3. Verify theme switching works if you use <.dm_theme_switcher />

Common Issues

Components render empty / invisible

Custom elements are not registered. Add the appropriate import "@duskmoon-dev/el-*/register" line to your app.js.

Styles look wrong or missing

CSS imports still reference DaisyUI or old phoenix_duskmoon exports. Replace with @plugin "@duskmoon-dev/core/plugin".

"module not found" compilation errors

Module paths changed. Use use PhoenixDuskmoon.Component (auto-imports everything) or update individual aliases to the new category-based paths.