This episode covers Tailwind's dark mode strategies: media versus class, along with the pros and cons of each. You also learn how to structure themes with theme.extend, use CSS custom properties for dynamic theme switching, plus integration with prefers-color-scheme and accessibility considerations.

Dark mode has become a feature users expect. Tailwind supports it through the dark: variant with two strategies: following the system preference via media, or being manually controlled through a class. Episode 6 covers both, how to structure themes with theme.extend, and the modern approach of using CSS variables for dynamic theming.
The choice of strategy heavily affects your architecture. media is the simplest and purely follows the system; class gives you full control, including manual toggles and persisting user preference. Most modern applications choose class for its flexibility.
This is Tailwind's default. The dark: variant activates automatically when the user's system is in dark mode:
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
Konten menyesuaikan preferensi sistem.
</div>Its advantage is zero configuration. Its drawback: you can't offer a manual toggle, because the decision is entirely in the operating system's hands.
Enable it with darkMode: "class" in the config:
module.exports = {
darkMode: "class",
content: ["./src/**/*.{html,js,ts,jsx}"],
theme: { extend: {} },
plugins: [],
};Then add the dark class to a root element:
<html class="dark">
<body class="bg-white dark:bg-gray-900">...</body>
</html>darkMode: "class" makes dark: active only if there's an element with the dark class above it — usually html. Manual toggling is now possible by adding or removing that class with JavaScript.
For large projects, avoid scattering color values directly in markup. Define tokens in theme.extend:
theme: {
extend: {
colors: {
brand: {
50: "#eff6ff",
500: "#3b82f6",
600: "#2563eb",
900: "#1e3a8a",
},
},
},
},After that, bg-brand-500 and text-brand-600 become available as utilities. These tokens become the single source of truth for your design — changing one value here changes the whole application.
A more modern approach: define tokens as CSS variables in :root, then reference them from the config. This lets you swap themes at runtime without a rebuild:
:root {
--color-bg: #ffffff;
--color-text: #111827;
}
html.dark {
--color-bg: #111827;
--color-text: #f9fafb;
}theme: {
extend: {
colors: {
bg: "var(--color-bg)",
text: "var(--color-text)",
},
},
},Now bg-bg and text-text automatically follow the variable values, so switching themes is just a matter of swapping the values in CSS — Tailwind doesn't need rebuilding. This colors: { bg: "var(--color-bg)" } pattern is very useful for white-label or multi-tenant setups.
To persist the user's choice, save the preference and apply it before rendering:
const isDark = localStorage.getItem("theme") === "dark";
document.documentElement.classList.toggle("dark", isDark);Move this script into the head so you don't get a flash of the wrong theme when the page loads.
If you want to still respect the system preference while using the class strategy, combine the two:
<script>
if (localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark");
}
</script>Accessibility considerations: make sure text-to-background contrast still meets the minimum ratio in both modes, and don't rely on color as the only status indicator — add an icon or text label.
Warning
Sufficient contrast in light mode isn't necessarily sufficient in dark mode. A token like gray-700 on white can fall below the 4.5:1 ratio once flipped. Always audit contrast in both themes before release.
Episode 6 equipped you with theming: the media and class strategies, structuring tokens via theme.extend, dynamic theme switching with CSS variables, and prefers-color-scheme integration that respects the system preference while still offering manual control.
Key takeaways:
media strategy follows the system; the class strategy gives full control.class via darkMode: "class", then add the dark class on html.theme.extend.colors as the single source of truth.head to prevent flashes.Next, in episode 7, we'll cover composing utilities — when to use @apply versus inline utilities, creating component classes inside the components layer, and advanced composition with variants and plugins for reusable patterns.