Back to Dark-mode Guide

How to Add Dark Mode to Any Website: Complete Implementation Guide

Learn how to implement dark mode on your website with our comprehensive guide. Discover CSS and JavaScript techniques for adding dark theme functionality to any website.

Dark mode began its journey as a curious developer novelty, but has since taken an improbable leap across the digital universe to become something users not only expect but actively hunt for with the determination of a caffeine-deprived programmer on Monday morning. Whether you're motivated by accessibility concerns, preventing your visitors' retinas from being seared like overcooked steaks, or simply trying to keep pace with web design fashion, this guide will navigate you through the mostly harmless process of implementing dark mode on any website. From simple CSS tricks to JavaScript wizardry that would impress even the Magratheans, you'll discover everything needed to let your visitors experience your site in their preferred luminosity setting.

Understanding Dark Mode: More Than Just Inverted Colors

Dark mode isn't simply a matter of flipping white backgrounds to black like some sort of digital negative – a fact that might surprise the digital equivalent of the Vogon Constructor Fleet who believe good design is whatever requires the least effort.

Before we plunge headlong into the code (without even stopping for a quick cup of tea), wrap your brain around these fundamental principles:

Contrast Sensitivity: Text demands a 4.5:1 contrast ratio against backgrounds for WCAG AA compliance – a number seemingly pulled from the ether by accessibility experts who clearly enjoy precise decimals
Color Warmth: Pure black (#000000) creates contrast so excessive it's almost aggressive; dark grays (#121212, #1f1f1f) are infinitely more soothing, like the digital equivalent of a cup of hot cocoa
Element Hierarchy: Those clever shadows and borders establishing visual hierarchy in light mode need equally clever dark mode alternatives, lest your users become hopelessly lost in the void
Content Adaptation: Images, videos, and interactive elements might require adjustments, unless you fancy them looking like mysterious alien artifacts against your new dark background

Armed with these principles and perhaps a towel (always handy), let's explore the technical implementation approaches, starting with the wonderfully simple and marching bravely toward the magnificently complex.

Method 1: CSS-Only Dark Mode Implementation

For static websites or designs uncomplicated enough to be explained to a particularly dim goldfish, a CSS-only approach offers an elegant solution with minimal overhead – rather like making tea without having to build a fusion reactor first.

Step 1: Define your color variables

In your CSS file, create variables for your color scheme with the cheerful optimism of someone who hasn't yet realized how many edge cases they'll encounter:

:root {
/* Light theme colors (default) */
--background-primary: #ffffff;
--background-secondary: #f1f5f9;
--text-primary: #1a202c;
--text-secondary: #4a5568;
--accent-color: #3182ce;
}

@media (prefers-color-scheme: dark) {
:root {
/* Dark theme colors */
--background-primary: #1a202c;
--background-secondary: #2d3748;
--text-primary: #f7fafc;
--text-secondary: #e2e8f0;
--accent-color: #63b3ed;
}
}


Step 2: Apply variables throughout your CSS

Replace those hardcoded color values with your variables, feeling slightly smug about your newfound efficiency:

body {
background-color: var(--background-primary);
color: var(--text-primary);
}

header, footer {
background-color: var(--background-secondary);
}

a {
color: var(--accent-color);
}


Advantages of this approach:

• Zero JavaScript required, much to the relief of purists everywhere
• Automatic detection of user preferences, like a mind-reading trick that actually works
• Performance impact so minimal it wouldn't disturb a sleeping quantum particle

Limitations:

• No manual toggle option without JavaScript, leaving users at the mercy of their system settings
• No way to override system preferences, which is mildly dictatorial
• No persistence of user selection, forcing them to relive their choices like some sort of bizarre digital amnesia

This solution works splendidly for simple sites but lacks the user control that visitors increasingly expect, rather like offering someone a choice of vehicle but insisting they can only drive what their neighbor recommends.

Method 2: JavaScript Toggle with Local Storage

Adding JavaScript brings the gift of choice to your users – that most cherished of human experiences, ranking just above breathing and slightly below coffee.

Step 1: Modify your CSS approach

Instead of relying solely on media queries, adopt a class-based approach with a media query safety net (because belts and suspenders are always in fashion when it comes to web development):

:root {
/* Light theme variables */
--background-primary: #ffffff;
--text-primary: #1a202c;
/* ...other variables... */
}

html.dark-mode {
--background-primary: #1a202c;
--text-primary: #f7fafc;
/* ...other dark theme variables... */
}

@media (prefers-color-scheme: dark) {
:root:not(.light-mode) {
/* Same dark theme variables as above */
--background-primary: #1a202c;
--text-primary: #f7fafc;
/* ...other dark theme variables... */
}
}


Step 2: Add the HTML toggle

Create a toggle with all the excitement of someone installing a light switch that doesn't electrocute users:

<button id="dark-mode-toggle" aria-label="Toggle dark mode">
<span class="moon-icon">🌙</span>
<span class="sun-icon">☀️</span>
</button>


Step 3: Add the JavaScript

Now for the clever bit – the JavaScript that makes everything work with the silent efficiency of a well-oiled improbability drive:

// Check for saved theme preference or use system preference
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
const storedTheme = localStorage.getItem('theme');

// Apply the right theme class on load
if (storedTheme === 'dark' || (!storedTheme && prefersDarkScheme.matches)) {
document.documentElement.classList.add('dark-mode');
} else if (storedTheme === 'light') {
document.documentElement.classList.add('light-mode');
}

// Set up the toggle
const themeToggle = document.getElementById('dark-mode-toggle');
themeToggle.addEventListener('click', () => {
// If currently light → go dark
if (!document.documentElement.classList.contains('dark-mode')) {
document.documentElement.classList.add('dark-mode');
document.documentElement.classList.remove('light-mode');
localStorage.setItem('theme', 'dark');
} else {
// If currently dark → go light
document.documentElement.classList.remove('dark-mode');
document.documentElement.classList.add('light-mode');
localStorage.setItem('theme', 'light');
}
});


This approach strikes a delicate balance between simplicity and user control – like giving someone both a spoon and a fork when serving soup, just in case they're feeling adventurous.

Method 3: Dynamic CSS Injection for Complex Websites

For websites with more layers of complexity than a conspiracy theorist's bulletin board, dynamically injecting a separate stylesheet provides better isolation and control – much like keeping your most volatile chemicals in separate cabinets.

Step 1: Create separate stylesheets

Instead of wrestling with CSS variables, maintain two separate stylesheets with the organizational zeal of someone who color-codes their sock drawer:
light-theme.css - Your default styling
dark-theme.css - Dark mode specific overrides

Step 2: Add theme switching JavaScript

// Create link element for theme stylesheet
const themeStylesheet = document.createElement('link');
themeStylesheet.rel = 'stylesheet';
document.head.appendChild(themeStylesheet);

// Function to set the active theme
function setTheme(themeName) {
localStorage.setItem('theme', themeName);
themeStylesheet.href = `${themeName}-theme.css`;
}

// Initialize theme
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (storedTheme) {
setTheme(storedTheme);
} else {
setTheme(prefersDark ? 'dark' : 'light');
}

// Toggle button event handler
document.getElementById('theme-toggle').addEventListener('click', () => {
const currentTheme = localStorage.getItem('theme') || 'light';
setTheme(currentTheme === 'light' ? 'dark' : 'light');
});


This approach offers cleaner separation than a diplomatic treaty and works remarkably well with complex UI frameworks and third-party components that regard CSS variables with the suspicion normally reserved for emails from unknown princes.

Handling Images and Media in Dark Mode

Images and media require special consideration in dark mode, unless you enjoy the aesthetic of neon signs against a night sky – which, to be fair, some do find rather appealing.

Approach 1: CSS Filters

For images needing subtle adjustment, apply CSS filters with the delicate touch of a surgeon who's had exactly the right amount of coffee:

html.dark-mode img:not([src*="logo"]) {
filter: brightness(0.8) contrast(1.2);
}


This works brilliantly for photographs but may distort logos with all the artistic merit of a painting left out in the rain.

Approach 2: Different Image Sources

For critical images like logos, provide alternate versions designed specifically for the dark side (no Sith lordship required):

<picture>
<source srcset="logo-dark.png" media="(prefers-color-scheme: dark)">
<img src="logo-light.png" alt="Company Logo">
</picture>


For JavaScript-based theme switching, update image sources when the theme changes, like a digital chameleon with a particularly decisive nature:

// Update all theme-aware images
function updateImages(themeName) {
document.querySelectorAll('img[data-dark-src]').forEach(img => {
if (themeName === 'dark') {
img.src = img.getAttribute('data-dark-src');
} else {
img.src = img.getAttribute('data-light-src');
}
});
}


Approach 3: SVG with currentColor

For icons, use SVGs with currentColor to automatically adapt to text color, displaying the kind of intelligence we'd all appreciate in certain politicians:

<svg fill="currentColor" viewBox="0 0 24 24">...</svg>

With corresponding CSS:

.icon {
color: var(--text-primary);
}


These techniques ensure your visuals maintain there consistency across both themes, preventing the visual equivalent of showing up to a formal dinner in beach attire.

Dark Mode for Web Applications and Frameworks

Modern web application frameworks require slightly different approaches – rather like how one wouldn't use the same technique to open a bottle of fine wine as one would to open a jar of pickles.

React Implementation

Use a context-based theme provider, the React equivalent of a universal remote control:

// ThemeContext.js
import React, { createContext, useState, useEffect } from 'react';

export const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');

useEffect(() => {
// Initialize theme from localStorage or system preference
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (savedTheme) {
setTheme(savedTheme);
} else if (prefersDark) {
setTheme('dark');
}
}, []);

useEffect(() => {
// Apply theme class to document
document.documentElement.className = theme;
localStorage.setItem('theme', theme);
}, [theme]);

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};


Then consume the context in components with the enthusiasm of someone who's just discovered free refills:

// ThemeToggle.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

const ThemeToggle = () => {
const { theme, setTheme } = useContext(ThemeContext);

const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};

return (
<button onClick={toggleTheme}>
{theme === 'light' ? '🌙' : '☀️'}
</button>
);
};


Similar patterns can be applied to Vue, Angular, and other frameworks, adapting to their specific state management approaches like a particularly adaptable chameleon at a disco.

Preventing Flash of Incorrect Theme (FOIT)

A common issue with dark mode implementations is a brief flash of the wrong theme during page load – an experience about as pleasant as someone flicking the lights on at 3 AM while you're deeply asleep.

Solution: Inline Script in <head>

Place this script in your HTML <head> before any stylesheets, with the urgency of someone who knows the importance of first impressions:

<script>
// Immediately set the theme before the page renders
(function() {
var savedTheme = localStorage.getItem('theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
document.documentElement.classList.add('dark-mode');
} else if (savedTheme === 'light') {
document.documentElement.classList.add('light-mode');
}
})();
</script>


This script executes synchronously before the DOM starts rendering, preventing any flash of incorrect theme with the efficiency of a bouncer at an exclusive club.

For more complex applications, consider using server-side rendering to determine the theme on the server and include the appropriate classes in the initial HTML, as if you were sending a pre-decorated room rather than decorating it after the guest arrives.

Testing Your Dark Mode Implementation

Thorough testing is essential to ensure your dark mode implementation works correctly across devices and browsers – unless you enjoy the unique thrill of late-night bug reports from users in different time zones.

Test Cases to Cover:

1. System preference detection - Verify your site respects OS-level settings like a well-trained digital butler
2. Manual toggle functionality - Ensure users can override system preferences with the satisfaction of someone flipping a really satisfying switch
3. Preference persistence - Confirm selections persist across page navigations, unlike certain politicians' campaign promises
4. Contrast ratios - Validate WCAG compliance in both modes, because accessibility isn't just a nice-to-have, it's a must-have
5. Browser compatibility - Test in Chrome, Firefox, Safari, and Edge with the thoroughness of someone checking for monsters under every bed
6. Mobile responsiveness - Verify behavior on iOS and Android devices, where most night-time browsing happens anyway
7. Image/media adaptations - Check all visual elements for proper display, lest they appear like mysterious shadows
8. Third-party content - Assess embedded content, iframes, and plugins, which often behave with the predictability of cats

Testing Tools:

WebAIM Contrast Checker - Verify color contrast compliance with scientific precision
• Browser DevTools - Toggle prefers-color-scheme manually, like a god of light and darkness
BrowserStack - Test across multiple browsers and devices without needing to remortgage your home for a device lab

Address any issues before launching, as a poor dark mode implementation can be worse than none at all – rather like serving slightly spoiled fish instead of no fish.

Conclusion: Dark Mode as a Competitive Advantage

Implementing dark mode is no longer just a nice-to-have feature—it's an expectation for modern websites, much like indoor plumbing is for houses. A well-executed dark mode implementation can:

• Reduce eye strain for users browsing at night, saving them from looking like they've just watched a trilogy of tear-jerking movies
• Extend battery life on devices with OLED/AMOLED screens, a benefit that users appreciate more than they can articulate
• Demonstrate your commitment to user experience customization, earning you digital brownie points
• Modernize your website's appearance and functionality, preventing it from looking like a digital dinosaur

As you implement your solution, remember that dark mode is not merely an inverted color scheme but a thoughtfully designed alternative visual experience. Take the time to properly test and refine your implementation across devices and user scenarios, with the care of an artist preparing for a gallery opening.

With the techniques outlined in this guide, you now have the knowledge to implement dark mode on any website, from simple static pages to complex web applications. Your users' eyes will thank you, and your website will join the ranks of modern, user-focused digital experiences that don't make people reach for sunglasses at midnight.

Frequently Asked Questions

Can I add dark mode to my website without knowing CSS?

While basic CSS knowledge is helpful, there are no-code solutions available, rather like how one doesn't need to understand internal combustion to drive a car. Website builders like Wix, Squarespace, and WordPress with appropriate themes often include built-in dark mode toggles that work with the click of a button. Alternatively, you can use third-party services like Darkreader.org to add a client-side dark mode to any website. However, for the best visual results and performance, a custom implementation using the CSS and JavaScript techniques described in this guide is recommended – much like how a tailor-made suit fits better than one grabbed off the rack.

How do I handle forms and input fields in dark mode?

Forms require special attention in dark mode to maintain usability, rather like how a black cat needs a colorful collar to be visible at night. Use moderate background colors (not pure black) for input fields and ensure sufficient contrast with input text. Remember to style focus states prominently, as they can be less visible in dark themes than a ninja at midnight. For select menus and dropdowns, ensure the dropdown options maintain the dark theme, lest they appear like unexpected flashlights. System inputs like date pickers may use browser default styling, so test these carefully in dark mode to ensure compatibility, as they sometimes have a mind of there own.

Will adding dark mode affect my website's performance?

When properly implemented, dark mode should have minimal impact on performance – about as much as a butterfly landing on a freight train. CSS variables and media queries add negligible overhead. The JavaScript toggle approach adds only a tiny amount of code, barely enough to notice unless you're counting bytes like a digital miser. For optimal performance: (1) Avoid large duplicate stylesheets, which is like carrying two identical suitcases when one would do, (2) Use CSS variables over complete style duplication, (3) Place theme detection scripts inline in the head to prevent flashing, and (4) Lazy-load any alternate dark mode images to reduce initial page load time. Your users won't notice any performance difference, but they will notice the comfort of not having their retinas scorched.

Should I implement dark mode before launching my website?

While not absolutely essential for launch, implementing dark mode during initial development is significantly easier than retrofitting it later – rather like how it's easier to install plumbing before the walls go up. When dark mode is planned from the start, you can establish a color system with variables, properly structure your CSS, and test both themes simultaneously with the efficiency of someone who plans their vacation months in advance. If your website is already live, implementing dark mode makes an excellent feature update that can be promoted to show your commitment to improving user experience, much like a restaurant proudly announcing new vegetarian options.

How do I handle third-party widgets in dark mode?

Third-party widgets and embedded content present challenges for dark mode implementation as they often use their own styling with the stubbornness of a cat that refuses to be trained. For widgets within iframes, you generally cannot modify their appearance, much like you can't redecorate your neighbor's house. Options include: (1) Check if the widget provider offers a dark mode version you can conditionally load, (2) Apply a subtle background to the widget container to visually separate it from your dark-themed UI, like putting a frame around a mismatched painting, (3) Use CSS to invert the iframe contents as a last resort (this may cause visual issues similar to wearing sunglasses in a dark room), or (4) In extreme cases, consider excluding the widget container from dark mode styles entirely, accepting that perfect uniformity may be as elusive as a unicorn.

Ready to transform your dark-mode website?

Join thousands of users who are already using our visual editor to update their dark-mode sites without coding.