# How to Add Dark Mode to Any Website in 5 Minutes (with localStorage)
Dark mode isn't just a trend anymore – it's an expected feature. Users want it for better readability and less eye strain.
The good news? You can add it to any existing website in about 5 minutes with pure CSS and a few lines of JavaScript.
No frameworks. No dependencies. Just copy, paste, and customize.
What You'll Build
A complete dark mode toggle that:
✅ Saves user preference in localStorage (persists across visits)
✅ Detects system theme automatically
✅ Lets users override system preference
✅ Works on any existing website
Step 1: CSS Variables (The Foundation)
CSS variables make theme switching effortless. Define your light theme as default, then override for dark mode.
/* Light theme (default) */
:root {
--bg-primary: #f8fafc;
--bg-secondary: #ffffff;
--text-primary: #0f172a;
--text-secondary: #475569;
--border: #e2e8f0;
--accent: #3b82f6;
}
/* Dark theme override */
[data-theme="dark"] {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--border: #334155;
--accent: #60a5fa;
}
/* Apply variables to your elements */
body {
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s, color 0.3s;
}
The transition property creates a smooth fade effect when switching themes.
Step 2: The Toggle Switch HTML
Add this toggle switch anywhere on your page. I put mine in the header.
And the CSS to make it look good:
.theme-switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.theme-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--accent);
}
input:checked + .slider:before {
transform: translateX(26px);
}
Step 3: The JavaScript (3 Simple Functions)
Here's the code logic. It's only 20 lines.
// Apply theme to the page
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
// Update toggle checkbox
const toggle = document.getElementById('darkModeToggle');
if (toggle) toggle.checked = (theme === 'dark');
}
// Detect system preference
function getSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
// Initialize on page load
const savedTheme = localStorage.getItem('theme');
setTheme(savedTheme || getSystemTheme());
// Listen to toggle clicks
document.getElementById('darkModeToggle').addEventListener('change', (e) => {
setTheme(e.target.checked ? 'dark' : 'light');
});
Step 4: Put It All Together
Here's complete working example you can copy into any HTML file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dark Mode Demo</title>
<style>
/* Light mode (default) */
:root {
--bg-primary: #f8fafc;
--text-primary: #0f172a;
--border: #e2e8f0;
}
/* Dark mode */
[data-theme="dark"] {
--bg-primary: #0f172a;
--text-primary: #f1f5f9;
--border: #334155;
}
body {
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s, color 0.3s;
font-family: system-ui, sans-serif;
padding: 2rem;
}
/* Toggle switch CSS here (from Step 2) */
</style>
</head>
<body>
<h1>Dark Mode Demo</h1>
<p>Click the toggle below. Your preference is saved automatically.</p>
<label class="theme-switch">
<input type="checkbox" id="darkModeToggle">
<span class="slider"></span>
</label>
<script>
// JavaScript from Step 3 here
</script>
</body>
</html>
How It Works (The 30-Second Explanantion)
- CSS Variables store all your colors in one place.
- [data-theme="dark"] overrides those variables when active.
- localStorage remembers the user's choice between visits.
- matchMedia detects if their OS is already in dark mode.
- One event listener toggles everything when clicked.
Pro Tips
Tip 1: Add more than colors
Dark mode can also adjust shadows, borders and even images.
[date-theme="dark"] img {
opacity: 0.8;
filter: brightness(0.8);
}
Tip 2: Listen for system changes
This updates the page if the user changes their OS theme while your site is open:
window.matchMedia('(prefers-color-scheme: dark).addEventListener('change', (e) => {
!localStorage.getItem('theme')) {
tTheme(e.matches ? 'dark' : 'light');
}
});
Tip 3: Use semantic HTML
The data-theme attribute is semantic and accessible. Screen readers understand it.
Live Demo
See the complete working example here:
👉 Live Dark Mode Demo with 3 Implementations
The live demo includes:
Toggle switch with smooth animation
"Match System" button to reset to OS preference
localStorage persistence
Want More UI Components?
I'm building a free library of copy-paste UI snippets. Here's what's available:
- Responsive navbar with mobile hamburger menu
- 4 CSS loaders (spinner, pulse dots, skeleton, progress bar)
- Login/ Signup forms with validation and password toggle
- Dark mode toggle (the one you just learned)
👉 All frees snippets here: UI Studio
Need Advanced Components?
I also offer a Pro Pack with 15 premium snippets:
- 5 navbar variations (mega menu, sticky, transparent, etc.)
- 5 advanced loaders (skeleton tables, infinite scroll, SVG)
- 5 form components (multi-step, OTP input, file upload)
👉 [Get the Pro Pack - $7 one-time] (https://mykhurram068-mak.github.io/ui-learning-platform/pro-pack.html)
Step What You Did Time
1 Added CSS variables 1 min
2 Added dark mode overrides 1 min
3 Added toggle HTML/CSS 2 min
4 Added JavaScript 1 min
Total Dark mode complete 5 minutes
Dark mode is one of those features that users notice and appreciate. It shows attention to detail and improves accessibility.
Now go add dark mode to your projects!
Found this helpful? Consider buying me a coffee to support more free tutorials.
☕ Buy me a Coffee
Want the full code + 3 implementation methods?
🔗 Live Demo & Code (https://mykhurram068-mak.github.io/ui-learning-platform)

Top comments (0)