The one theme-controller script — the behavior behind every element the
PhoenixKitWeb.Components.Core.ThemeController picker renders.
Before this module the same behavior existed three times: a hand-written
script in the dashboard layout, a near-copy inside the admin layout
wrapper, and the deleted static phoenix_kit_themes.js. They drifted —
the dashboard copy carried its own hardcoded base map that missed newer
themes and could never know host-defined ones, and both resolved
"system" to hardcoded phoenix-* names, wrong for any host whose
configured pair uses other names (see PhoenixKit.ThemeConfig.system_pair/0).
Everything data-shaped is generated from PhoenixKit.ThemeConfig at
render time, so host :theme_definitions flow through with zero JS
changes. The script is idempotent per page (window.__pkThemeController
guard) and every DOM hook is optional — a page with no label element or
no toggle simply skips those branches.
Event contract: picker elements dispatch phx:set-theme themselves (a
JS.dispatch bubbling to window), so hosts hear each selection exactly
once whether or not this script is present. The script CONSUMES that
event to persist and reflect; it re-announces only changes that did not
arrive as a window event (the legacy phx:set-admin-theme translation).
Renders at the end of <body>; the pre-paint half of the story is
PhoenixKitWeb.Components.ThemeBootstrap in <head>.
Summary
Functions
The shared theme controller. Render once, at the end of <body>.