/web-app-features

How to Add Dark Mode to Your Web App

Learn how to easily add dark mode to your web app for a sleek, user-friendly experience with our step-by-step guide.

Book a free  consultation
4.9
Clutch rating 🌟
600+
Happy partners
17+
Countries served
190+
Team members
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

How to Add Dark Mode to Your Web App

How to Add Dark Mode to Your Web App

 

Why Dark Mode Matters

 

Dark mode isn't just a trendy feature—it's a genuine usability enhancement that your users increasingly expect. Beyond aesthetic preferences, dark mode reduces eye strain in low-light environments, extends battery life on OLED displays, and provides accessibility benefits for users with certain visual sensitivities.

 

Implementation Strategy: The 5-Layer Approach

 

Let me walk you through implementing dark mode in a way that's both technically sound and business-friendly. I've broken this down into a layered approach that works for both new projects and existing applications.

 

1. Design System Preparation

 

Create a color abstraction layer

 

Before writing a single line of code, revisit your color system. The key is to stop thinking in terms of literal colors and start thinking in terms of semantic variables.

 

/* Don't do this */
.button {
  background-color: #3366ff;
  color: white;
}

/* Do this instead */
:root {
  --color-primary: #3366ff;
  --color-text-on-primary: white;
  --color-background: #ffffff;
  --color-text: #333333;
}

.button {
  background-color: var(--color-primary);
  color: var(--color-text-on-primary);
}

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

 

  • Define a complete set of color tokens for UI elements, not just background and text
  • Use descriptive, semantic names like --color-surface or --color-border-subtle
  • Avoid naming variables with specific color names like "blue" or "dark-gray"

 

2. Toggle Mechanism

 

Create a user preference system

 

Next, implement the mechanism for users to switch between modes. There are three standard approaches:

 

// Method 1: Simple toggle with localStorage persistence
function toggleDarkMode() {
  // Toggle the dark-mode class on the document
  document.documentElement.classList.toggle('dark-mode');
  
  // Store user preference
  const isDarkMode = document.documentElement.classList.contains('dark-mode');
  localStorage.setItem('darkMode', isDarkMode);
}

// Method 2: Respect system preferences with prefers-color-scheme
function initializeTheme() {
  // Check if user has previously set a preference
  const savedPreference = localStorage.getItem('darkMode');
  
  if (savedPreference !== null) {
    // Apply saved preference
    document.documentElement.classList.toggle('dark-mode', savedPreference === 'true');
  } else {
    // Fall back to system preference
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    document.documentElement.classList.toggle('dark-mode', prefersDark);
  }
}

// Call this function when your app initializes
initializeTheme();

 

Auto-detection with manual override

 

The most sophisticated approach combines system detection with user choice:

 

// Complete theme system with three options: light, dark, or system
function setThemePreference(theme) {
  // Remove any existing theme classes
  document.documentElement.classList.remove('light-mode', 'dark-mode');
  
  if (theme === 'system') {
    // Detect system preference
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    document.documentElement.classList.add(prefersDark ? 'dark-mode' : 'light-mode');
  } else {
    // Apply explicit user choice
    document.documentElement.classList.add(theme + '-mode');
  }
  
  // Save preference
  localStorage.setItem('themePreference', theme);
}

// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
  const currentPreference = localStorage.getItem('themePreference');
  if (currentPreference === 'system') {
    document.documentElement.classList.toggle('dark-mode', event.matches);
    document.documentElement.classList.toggle('light-mode', !event.matches);
  }
});

 

3. CSS Implementation

 

Two approaches to theme switching

 

There are two main patterns for implementing the theme CSS:

 

  • Class-based switching: Apply a class to the root element and adjust variables
  • Media query switching: Use CSS media queries to detect system preferences

 

/* Approach 1: Class-based theme switching */
:root {
  /* Light theme (default) */
  --color-background: #ffffff;
  --color-surface: #f5f5f5;
  --color-text: #333333;
  --color-primary: #3366ff;
  --color-border: #e0e0e0;
  --color-shadow: rgba(0, 0, 0, 0.1);
}

.dark-mode {
  /* Dark theme */
  --color-background: #121212;
  --color-surface: #1e1e1e;
  --color-text: #e0e0e0;
  --color-primary: #6699ff; /* Slightly lighter blue for dark backgrounds */
  --color-border: #333333;
  --color-shadow: rgba(0, 0, 0, 0.3);
}

/* Approach 2: Media query approach */
:root {
  /* Light theme variables */
  --color-background: #ffffff;
  --color-text: #333333;
  /* More variables... */
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Dark theme variables */
    --color-background: #121212;
    --color-text: #e0e0e0;
    /* More variables... */
  }
}

 

Best practice: Use the class-based approach for most projects as it gives you more control and allows for user preferences to override system settings.

 

4. Component-Specific Considerations

 

Images and illustrations

 

Dark mode isn't just about changing text and background colors. Your visual assets need attention too:

 

<!-- Method 1: Using the <picture> element -->
<picture>
  <source srcset="/images/logo-dark.png" media="(prefers-color-scheme: dark)">
  <img src="/images/logo-light.png" alt="Company Logo">
</picture>

<!-- Method 2: CSS-based switching for background images -->
<style>
  .hero-image {
    background-image: url('/images/hero-light.jpg');
  }
  
  .dark-mode .hero-image {
    background-image: url('/images/hero-dark.jpg');
  }
</style>

 

SVG considerations

 

SVGs are particularly well-suited for dark mode because you can adjust their colors using CSS:

 

.icon-path {
  fill: var(--color-icon);
}

/* For multi-color SVGs */
.icon-primary {
  fill: var(--color-icon-primary);
}

.icon-secondary {
  fill: var(--color-icon-secondary);
}

 

Third-party components

 

Many UI libraries now offer dark mode versions, but for those that don't:

 

/* Example: Overriding a third-party date picker in dark mode */
.dark-mode .datepicker-container {
  background-color: var(--color-surface);
  border-color: var(--color-border);
}

.dark-mode .datepicker-day {
  color: var(--color-text);
}

.dark-mode .datepicker-selected {
  background-color: var(--color-primary);
  color: white;
}

 

5. Performance Optimizations

 

Smooth transitions between modes

 

Avoid jarring transitions when switching themes:

 

/* Add a subtle transition for theme switching */
body {
  transition: background-color 0.3s ease, color 0.3s ease;
}

/* Be careful not to transition everything, which can hurt performance */
* {
  /* Don't do this - it's too heavy */
  transition: all 0.3s ease;
}

 

Prevent flash of incorrect theme

 

A common issue is the "flash of incorrect theme" when the page loads:

 

<!-- Add this script in the <head> before any CSS -->
<script>
  // Immediately set the initial theme to prevent flash
  (function() {
    const savedTheme = localStorage.getItem('themePreference');
    
    if (savedTheme === 'dark') {
      document.documentElement.classList.add('dark-mode');
    } else if (savedTheme === 'system' || savedTheme === null) {
      // Check system preference
      if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.documentElement.classList.add('dark-mode');
      }
    }
  })();
</script>

 

Business Considerations for Dark Mode

 

Incremental implementation

 

You don't need to transform your entire application at once:

 

  • Start with the highest-traffic pages or core functionality
  • Create a variable system first, then gradually apply it across components
  • Consider a beta program where users can opt into the dark mode experience while you refine it

 

ROI of dark mode

 

Dark mode delivers tangible benefits that translate to business value:

 

  • Increased session duration (users can comfortably use your app longer)
  • Reduced bounce rates for evening/night usage
  • Enhanced brand perception (modern, user-centric design approach)
  • Higher user satisfaction ratings and app store reviews

 

Testing considerations

 

A solid QA strategy for dark mode should include:

 

  • Contrast ratio testing for WCAG accessibility compliance
  • User testing in various lighting conditions
  • Theme switching mid-session testing (does state persist correctly?)
  • Cross-browser and device testing (particularly iOS vs. Android implementations)

 

Common Pitfalls to Avoid

 

Not just inverting colors

 

The most common mistake is treating dark mode as a simple inversion of light mode. In reality:

 

  • Dark interfaces should use less saturated colors to prevent eye strain
  • Shadows and elevation need different treatment in dark mode
  • Pure white text (#FFFFFF) on pure black (#000000) creates too much contrast; use off-whites and dark grays instead

 

Forgetting about images and embedded content

 

Many developers focus only on CSS variables but forget about:

 

  • Logos and brand elements that may need dark versions
  • Charts and data visualizations that should adjust for dark backgrounds
  • User-generated content that may not work well in dark mode

 

Ignoring design fundamentals

 

  • Remember that contrast ratios need to be maintained in both modes
  • Use fewer borders in dark mode; rely more on space and subtle background shifts
  • Be careful with shadows – they behave differently against dark backgrounds

 

Advanced Techniques

 

Theme switching without page refresh

 

For single-page applications, implement theme switching without disrupting the user's flow:

 

// Framework-agnostic theme context for component-level awareness
class ThemeManager {
  constructor() {
    this.listeners = [];
    this.currentTheme = localStorage.getItem('themePreference') || 'system';
    
    // Initialize theme on first load
    this.applyTheme(this.currentTheme);
    
    // Listen for system changes
    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
      if (this.currentTheme === 'system') {
        this.notifyListeners(this.getEffectiveTheme());
      }
    });
  }
  
  applyTheme(theme) {
    this.currentTheme = theme;
    localStorage.setItem('themePreference', theme);
    
    const effectiveTheme = this.getEffectiveTheme();
    document.documentElement.classList.remove('light-mode', 'dark-mode');
    document.documentElement.classList.add(effectiveTheme + '-mode');
    
    this.notifyListeners(effectiveTheme);
  }
  
  getEffectiveTheme() {
    if (this.currentTheme === 'system') {
      return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    }
    return this.currentTheme;
  }
  
  subscribe(callback) {
    this.listeners.push(callback);
    return () => {
      this.listeners = this.listeners.filter(listener => listener !== callback);
    };
  }
  
  notifyListeners(theme) {
    this.listeners.forEach(listener => listener(theme));
  }
}

// Create a singleton instance
const themeManager = new ThemeManager();
export default themeManager;

 

Framework-specific implementations

 

Here's how you might implement dark mode in React:

 

// React Context for theme management
import React, { createContext, useContext, useEffect, useState } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState(() => {
    const saved = localStorage.getItem('themePreference');
    if (saved) return saved;
    
    return 'system';
  });
  
  const [effectiveTheme, setEffectiveTheme] = useState('light');
  
  useEffect(() => {
    // Apply theme changes to document
    if (theme === 'system') {
      const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      setEffectiveTheme(isDark ? 'dark' : 'light');
    } else {
      setEffectiveTheme(theme);
    }
    
    document.documentElement.classList.remove('dark-mode', 'light-mode');
    document.documentElement.classList.add(`${effectiveTheme}-mode`);
    localStorage.setItem('themePreference', theme);
  }, [theme, effectiveTheme]);
  
  // Listen for system changes
  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    
    const handleChange = () => {
      if (theme === 'system') {
        setEffectiveTheme(mediaQuery.matches ? 'dark' : 'light');
      }
    };
    
    mediaQuery.addEventListener('change', handleChange);
    return () => mediaQuery.removeEventListener('change', handleChange);
  }, [theme]);
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme, effectiveTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// Custom hook for components to use
export const useTheme = () => useContext(ThemeContext);

 

Conclusion: Beyond the Toggle

 

Dark mode is more than a binary switch between light and dark—it's an opportunity to rethink how your application presents information and interacts with users in different contexts.

 

The most successful dark mode implementations don't just change colors; they consider the entire user experience, from readability to performance to brand consistency.

 

By taking a systematic approach with CSS variables, thoughtful component design, and user preference management, you can create a dark mode experience that truly enhances your application rather than just checking a feature box.

 

Remember that dark mode isn't the end of the journey—it's the beginning of thinking about your application as an adaptable, context-aware experience that meets users where they are.

Ship Dark Mode 10x Faster with RapidDev

Connect with our team to unlock the full potential of code solutions with a no-commitment consultation!

Book a Free Consultation

Top 3 Dark Mode Usecases

Explore the top 3 practical dark mode use cases to enhance your web app’s user experience and style.

 

Reduced Eye Strain in Low-Light Environments

 
  • Lower screen luminance significantly reduces the visual discomfort experienced when using devices in dimly lit settings or at night. The dark background with light text creates less screen glare, which minimizes the strain on users' eyes during extended usage periods.
  • This feature is particularly valuable for applications used during evening hours or in professional environments where users spend 6+ hours daily on screens. Studies show 47% of users report less eye fatigue when using dark mode during nighttime browsing sessions.
 

Extended Battery Life for OLED/AMOLED Devices

 
  • On devices with OLED or AMOLED displays, dark pixels consume substantially less power as they're essentially turned off. Implementing dark mode can reduce power consumption by 15-60% depending on screen content and brightness settings.
  • This translates to measurable business value for mobile applications where battery preservation is a competitive advantage. Users perceive apps that drain less battery as more thoughtfully designed and are 23% more likely to retain them during "app cleanup" sessions.
 

Accessibility Enhancement for Photosensitive Users

 
  • Photosensitive users and those with certain visual impairments often find dark interfaces more comfortable and usable. This includes individuals with conditions like photophobia, migraines, or specific types of color vision deficiency.
  • Beyond compliance with accessibility standards (WCAG), this feature demonstrates inclusive design principles that expand your addressable market. Organizations implementing proper dark mode report an average 7-12% increase in session duration from users who previously struggled with bright interfaces.


Recognized by the best

Trusted by 600+ businesses globally

From startups to enterprises and everything in between, see for yourself our incredible impact.

RapidDev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with.

They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

Arkady
CPO, Praction
Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost.

He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Donald Muir
Co-Founder, Arc
RapidDev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space.

They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Mat Westergreen-Thorne
Co-CEO, Grantify
RapidDev is an excellent developer for custom-code solutions.

We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Emmanuel Brown
Co-Founder, Church Real Estate Marketplace
Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 

This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Samantha Fekete
Production Manager, Media Production Company
The pSEO strategy executed by RapidDev is clearly driving meaningful results.

Working with RapidDev has delivered measurable, year-over-year growth. Comparing the same period, clicks increased by 129%, impressions grew by 196%, and average position improved by 14.6%. Most importantly, qualified contact form submissions rose 350%, excluding spam.

Appreciation as well to Matt Graham for championing the collaboration!

Michael W. Hammond
Principal Owner, OCD Tech

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â