Dark Mode
Maddex UI includes built-in support for dark mode using Tailwind CSS v4
and CSS variables. By toggling a single class on the
html
element, your entire application seamlessly transitions between color
schemes.
CSS Variables
We define semantic colors (like
--background) in
:root. When the
.dark class is applied, we override these
variables with their dark-mode counterparts.
Tailwind v4
Maddex is configured with a custom variant
@custom-variant dark (&:is(.dark *));. This
allows you to style elements specifically for dark mode using the
dark: prefix if needed.
1. CSS Configuration
Ensure your globals.css defines the
color palette for both light and dark modes. Maddex relies on
oklch colors for consistent
perceptual brightness.
@import
"tailwindcss";
/* 1. Define the custom variant */
@custom-variant dark (&:is(.dark
*));
/* 2. Light Mode Defaults */
:root {
--background:
oklch(1 0 0);
--foreground:
oklch(0.145 0 0);
/* ... other variables */
}
/* 3. Dark Mode Overrides */
.dark {
--background:
oklch(0.145 0 0);
--foreground:
oklch(0.985 0 0);
}
2. The Toggle Component
We use PulsePoint state to manage the theme. This ensures
the UI is reactive and the selection persists via
localStorage. The effect hook
automatically updates the
html class list.
<!-- Toggle Button UI -->
<button
class="rounded-full bg-muted p-2 hover:bg-accent
hover:text-accent-foreground"
aria-label="Toggle theme"
onclick="toggleThemeMode()"
>
<Sun
class="size-4"
hidden="{toggleTheme === 'dark'}"
/>
<Moon
class="size-4"
hidden="{toggleTheme === 'light'}"
/>
</button>
<!-- PulsePoint Logic -->
<script>
const [toggleTheme,
setToggleTheme] = pp.state(
localStorage.getItem("maddex_theme") || "dark"
);
// Sync DOM with State
pp.effect(() => {
if
(toggleTheme === "dark") {
document.documentElement.classList.add("dark");
}
else {
document.documentElement.classList.remove("dark");
}
}, [toggleTheme]);
// Toggle Handler
function
toggleThemeMode() {
if
(toggleTheme === "light") {
setToggleTheme("dark");
localStorage.setItem("maddex_theme", "dark");
}
else {
setToggleTheme("light");
localStorage.setItem("maddex_theme", "light");
}
}
</script>
Best Practices
-
Use Semantic Colors: Always use
bg-backgroundandtext-foreground. Avoid hardcoding generic colors likebg-whiteortext-black. -
Default State: Initialize the state with a fallback to
"system"preference if local storage is empty (optional enhancement). -
Avoid Flash of Unstyled Content: Ensure your base
template runs a small script to check
localStoragebefore the body renders to prevent flickering.