How to Add a Dark Mode Toggle to Your Website With CSS and JavaScript
Why Every Website Needs a Dark Mode Toggle in 2026 Dark mode is no longer a nice-to-have feature. Users expect it. Operating systems, browsers, and apps all ship with dark themes, and visitors will leave your site if it blasts bright white light at them at midnight. In this tutorial you will learn how to add a dark mode toggle to a website using plain CSS custom properties and a small JavaScript function. No frameworks, no libraries, no dependencies. The technique works on any website, whether it runs on Express.js, WordPress, a static site generator, or anything else. Here is what we will cover: Planning a color scheme with CSS custom properties Building the toggle button in HTML Writing the JavaScript to switch themes Saving user preference in localStorage Respecting the prefers-color-scheme media query Avoiding common pitfalls By the end you will have a production-ready dark mode toggle you can drop into any project. Step 1: Plan Your Color Scheme With CSS Custom Properties The foundation of a maintainable dark mode is CSS custom properties (also called CSS variables). Instead of hard-coding color values throughout your stylesheet, you define them once on the :root selector and reference them everywhere else. Define Light Mode Colors :root { –color-bg: #ffffff; –color-text: #1a1a1a; –color-heading: #000000; –color-primary: #2563eb; –color-surface: #f3f4f6; –color-border: #d1d5db; } Define Dark Mode Colors We scope the dark palette to a .dark class on the <html> element. When that class is present, every variable is overridden automatically. html.dark { –color-bg: #0f172a; –color-text: #e2e8f0; –color-heading: #f8fafc; –color-primary: #60a5fa; –color-surface: #1e293b; –color-border: #334155; } Use the Variables in Your Styles body { background-color: var(–color-bg); color: var(–color-text); transition: background-color 0.3s ease, color 0.3s ease; } h1, h2, h3 { color: var(–color-heading); } a { color: var(–color-primary); } .card { background-color: var(–color-surface); border: 1px solid var(–color-border); } The transition property on the body gives users a smooth fade between themes instead of an abrupt flash. Quick Color Palette Reference Variable Light Value Dark Value Purpose –color-bg #ffffff #0f172a Page background –color-text #1a1a1a #e2e8f0 Body text –color-heading #000000 #f8fafc Headings –color-primary #2563eb #60a5fa Links, accents –color-surface #f3f4f6 #1e293b Cards, panels –color-border #d1d5db #334155 Borders, dividers Step 2: Build the Toggle Button in HTML Keep the markup simple. A single <button> element with an accessible aria-label is all you need. <button id=”theme-toggle” aria-label=”Toggle dark mode” title=”Toggle dark mode” > <span class=”icon-sun”>☀</span> <span class=”icon-moon”>☾</span> </button> We show the sun icon when dark mode is active (meaning “click to switch to light”) and the moon icon when light mode is active. Basic Toggle Button CSS #theme-toggle { background: none; border: 2px solid var(–color-border); border-radius: 8px; padding: 6px 10px; cursor: pointer; font-size: 1.2rem; color: var(–color-text); } /* In light mode, hide the sun icon */ .icon-sun { display: none; } /* In dark mode, hide the moon icon and show the sun */ html.dark .icon-moon { display: none; } html.dark .icon-sun { display: inline; } Step 3: Write the JavaScript to Switch Themes The JavaScript for a dark mode toggle is surprisingly short. All it does is add or remove the .dark class from the <html> element. const toggle = document.getElementById(‘theme-toggle’); toggle.addEventListener(‘click’, () => { document.documentElement.classList.toggle(‘dark’); }); That is a working toggle in three lines. But we still need to remember the user’s choice and respect their system preference. Let’s do that next. Step 4: Save User Preference in localStorage Without persistence, the theme resets on every page load. We use localStorage to remember the visitor’s choice across sessions. const toggle = document.getElementById(‘theme-toggle’); toggle.addEventListener(‘click’, () => { const isDark = document.documentElement.classList.toggle(‘dark’); localStorage.setItem(‘theme’, isDark ? ‘dark’ : ‘light’); }); Now we need to read that value when the page loads and apply it before the page renders. This is important because if you apply the class too late, the user will see a flash of the wrong theme. Inline Script in the <head> Place this script inside the <head> tag, before your stylesheets finish loading. Because it is synchronous and tiny, it blocks rendering for only a fraction of a millisecond and prevents the dreaded flash of incorrect theme (FOIT). <script> (function () { const saved = localStorage.getItem(‘theme’); if (saved === ‘dark’) { document.documentElement.classList.add(‘dark’); } })(); </script> Step 5: Respect the prefers-color-scheme Media Query Many users set a system-wide dark or light preference in their OS settings. A polite website should honor that preference when no explicit choice has been saved. We update the inline head script to check for the media query as a fallback: <script> (function () { const saved = localStorage.getItem(‘theme’); if (saved) { if (saved === ‘dark’) { document.documentElement.classList.add(‘dark’); } } else if (window.matchMedia(‘(prefers-color-scheme: dark)’).matches) { document.documentElement.classList.add(‘dark’); } })(); </script> The priority order is: Explicit user choice stored in localStorage (highest priority) System preference via prefers-color-scheme Light mode as the default fallback Optional: Listen for System Changes in Real Time If the user changes their OS theme while your page is open, you can react to it: window.matchMedia(‘(prefers-color-scheme: dark)’) .addEventListener(‘change’, (e) => { if (!localStorage.getItem(‘theme’)) { document.documentElement.classList.toggle(‘dark’, e.matches); } }); This listener only fires when the user has not explicitly chosen a theme on your site. Step 6: The Complete Code Here is every piece assembled into a single, copy-paste-ready example. HTML <!DOCTYPE html> <html lang=”en”> <head> <meta charset=”UTF-8″ /> <meta name=”viewport” content=”width=device-width, initial-scale=1.0″ /> <title>Dark Mode Toggle Demo</title> <!– Apply saved or system theme instantly –> <script> (function () { var saved = localStorage.getItem(‘theme’); if (saved === ‘dark’) { document.documentElement.classList.add(‘dark’); } else if (!saved && window.matchMedia(‘(prefers-color-scheme: dark)’).matches) { document.documentElement.classList.add(‘dark’); } })(); </script> <link rel=”stylesheet” href=”style.css” /> </head> <body> <header> <h1>My Website</h1> <button id=”theme-toggle” aria-label=”Toggle dark mode”> <span class=”icon-sun”>☀</span> <span class=”icon-moon”>☾</span> </button> </header> <main> <p>This page supports dark mode. Click the button to switch.</p> </main> <script src=”theme-toggle.js”></script> </body> </html> CSS (style.css) :root { –color-bg: #ffffff; –color-text: #1a1a1a; –color-heading: #000000; –color-primary: #2563eb; –color-surface: #f3f4f6; –color-border: #d1d5db; } html.dark { –color-bg: #0f172a; –color-text: #e2e8f0; –color-heading: #f8fafc; –color-primary: #60a5fa; –color-surface: #1e293b; –color-border: #334155; } body { margin: 0; font-family: system-ui, sans-serif; background-color: var(–color-bg); color: var(–color-text); transition: background-color 0.3s ease,
How to Add a Dark Mode Toggle to Your Website With CSS and JavaScript Read More »





