Dark Mode Done Right: A Complete Guide
Why Dark Mode Matters
Dark mode is no longer a nice-to-have. Users expect it, and for good reason: it reduces eye strain in low-light environments, saves battery on OLED screens, and many developers simply prefer it. Implementing it well, however, requires more thought than adding a .dark class.
The Architecture
A solid dark mode implementation has three layers:
- User preference detection (system setting via
prefers-color-scheme) - User choice persistence (localStorage or database)
- Theme application (CSS classes or variables)
Aura UI handles all three. Here is how it works under the hood.
Setting Up the Toggle
Aura UI ships with Alpine.js-powered dark mode support. The <html> element gets a .dark class, and Tailwind CSS 4 handles the rest:
<x-aura::dark-mode-toggle />
This renders a clean toggle button that:
- Detects the system preference on first visit
- Saves the user's choice to localStorage
- Applies the
.darkclass to<html>instantly (no flash)
Preventing the Flash of Wrong Theme
The most common dark mode bug is a flash of the light theme on page load. Aura UI prevents this with an inline script in the <head>:
<script>
if (localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
</script>
This runs before any CSS or content paints, so the correct theme is applied immediately.
Designing for Both Modes
The hardest part of dark mode is not the toggle; it is making your UI look good in both modes. Here are the principles Aura UI follows:
Use Semantic Colors
Instead of hardcoding colors, use Tailwind's dark mode modifier:
<x-aura::card class="bg-white dark:bg-gray-800">
<p class="text-gray-900 dark:text-gray-100">
Content that works in both modes.
</p>
</x-aura::card>
Aura UI components already include dark mode variants for every color, so you typically do not need to add these yourself.
Mind the Contrast
Dark mode is not just "invert everything." You need to maintain sufficient contrast ratios (WCAG AA requires 4.5:1 for normal text). Aura UI components are tested against these ratios in both modes.
Shadows and Elevation
In light mode, shadows create depth. In dark mode, shadows are invisible against dark backgrounds. Aura UI uses subtle border adjustments and lighter background shades to convey elevation in dark mode:
<x-aura::alert variant="info">
This alert looks correct in both light and dark mode,
with appropriate borders and background tints.
</x-aura::alert>
Testing Dark Mode
Always test your application in both modes. A quick tip: add a keyboard shortcut for toggling during development:
{{-- Add to your layout for dev convenience --}}
<div x-data @keydown.ctrl.d.window="$dispatch('toggle-dark-mode')">
<x-aura::dark-mode-toggle />
</div>
Summary
Dark mode is a design challenge, not just a CSS toggle. With Aura UI, every component is designed and tested for both light and dark modes. The Alpine.js integration handles preference detection and persistence, and the inline head script eliminates the dreaded flash. Use the <x-aura::dark-mode-toggle> component and focus on your application features instead.