# `PhiaUi.Components.DarkModeToggle`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/components/display/dark_mode_toggle.ex#L1)

Dark mode toggle button component powered by the `PhiaDarkMode` vanilla JS hook.

Clicking the button toggles the `.dark` CSS class on the `<html>` element.
The preference is persisted in `localStorage` under two keys for backward
compatibility:

- `localStorage['phia-mode']` — canonical key (`"light"` or `"dark"`)
- `localStorage['phia-theme']` — legacy key (kept for apps that depended on the old key)

The hook also fires the `phia:theme-changed` custom DOM event after each
toggle, allowing other hooks (notably `PhiaChart`) to re-initialise with
the correct ECharts dark theme.

## How dark mode works

PhiaUI dark mode is CSS-class-driven, not media-query-driven:

1. `theme.css` contains `@custom-variant dark (&:where(.dark, .dark *));`
   which defines the `dark:` Tailwind variant to match any element inside
   a `.dark` ancestor.
2. The `PhiaDarkMode` hook adds or removes `.dark` from `<html>` on click.
3. All dark-mode styles cascade automatically from the CSS custom properties
   defined in the `@theme` block.

This approach gives the user (not the OS) full control over the mode, and
allows toggling mid-session without any server round-trip.

## Setup

1. Copy the hook (or run `mix phia.add dark_mode`):

       # priv/templates/js/hooks/dark_mode.js
       import PhiaDarkMode from "./hooks/dark_mode"

2. Register it in `app.js`:

       import PhiaDarkMode from "./hooks/dark_mode"
       let liveSocket = new LiveSocket("/live", Socket, {
         hooks: { PhiaDarkMode }
       })

3. Add the `dark` custom variant to `priv/static/theme.css`:

       @custom-variant dark (&:where(.dark, .dark *));

## Anti-FOUC (Flash of Unstyled Content)

Without special handling, the browser renders the page in light mode for a
brief moment before JavaScript loads and restores the dark preference from
`localStorage`. This causes a visible "flash" on reload.

Prevent it by adding this inline `<script>` to the `<head>` of your layout
**before** any `<link>` stylesheet tag:

    <script>
      (function() {
        // Restore dark/light mode before first paint to avoid FOUC
        var mode = localStorage.getItem('phia-mode') || localStorage.getItem('phia-theme');
        if (mode === 'dark' || (!mode && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
          document.documentElement.classList.add('dark');
        }
        // Restore color theme preset (set by PhiaTheme hook)
        var ct = localStorage.getItem('phia-color-theme');
        if (ct) document.documentElement.setAttribute('data-phia-theme', ct);
      })();
    </script>

The script is intentionally tiny (no external dependencies) so it executes
synchronously before any CSS is parsed. It checks `phia-mode` first (new
key), then falls back to `phia-theme` (legacy), then falls back to
`prefers-color-scheme` media query for first-time visitors.

## Color theme switching (separate from dark mode)

Dark mode (light/dark) and color theme (zinc, blue, rose, etc.) are two
independent dimensions. Use the `PhiaTheme` hook (from `mix phia.theme
install`) for runtime color preset switching:

    <%!-- Dropdown that switches the active color preset at runtime --%>
    <select phx-hook="PhiaTheme" id="color-theme-select">
      <option value="zinc">Zinc</option>
      <option value="blue">Blue</option>
      <option value="rose">Rose</option>
    </select>

    <%!-- Or a set of click targets --%>
    <button phx-hook="PhiaTheme" id="btn-blue" data-theme="blue">Blue</button>

## Usage example

    <%!-- In the topbar of shell/1 --%>
    <:topbar>
      <.mobile_sidebar_toggle />
      <span class="ml-2 font-semibold">Acme</span>
      <div class="ml-auto flex items-center gap-2">
        <.dark_mode_toggle id="topbar-dm-toggle" />
        <span class="text-sm text-muted-foreground">Jane</span>
      </div>
    </:topbar>

# `dark_mode_toggle`

Renders the dark mode toggle button.

The button renders **both** the sun icon (light mode indicator) and moon icon
(dark mode indicator) simultaneously. The `PhiaDarkMode` hook controls which
icon is visible via Tailwind's `dark:` variant:

- `.dark:hidden` — sun is shown in light mode, hidden in dark
- `.hidden.dark:block` — moon is hidden in light mode, shown in dark

This technique avoids a JavaScript-driven icon swap and relies purely on CSS,
which means the correct icon is visible as soon as the `.dark` class is
toggled on `<html>` — even before any re-render.

The `aria-label` on the button reads `"Switch to dark mode"` statically.
This is intentional: the hook updates the label dynamically after mounting.
Server-rendered HTML always defaults to the "before click" state.

## Example

    <.dark_mode_toggle id="my-toggle" />

    <%!-- With extra classes for positioning --%>
    <.dark_mode_toggle id="sidebar-toggle" class="ml-auto" />

## Attributes

* `id` (`:string`) (required) - Unique ID for the toggle button element. Required because `phx-hook`
  targets elements by ID. Use a stable, page-unique string like
  `"header-dark-mode-toggle"`.

* `class` (`:string`) - Additional CSS classes for the button element. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the button element (e.g. `aria-label`, `data-*`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
