A Senior Developer’s Guide to Building Dark Mode in Modern Web Applications with Angular.
Dark mode has evolved from a niche preference to an expected feature in modern web and mobile applications. In 2025, users increasingly expect interfaces that can adapt to low-light environments or personal preferences, while enterprises want consistency across products.
For senior developers, implementing dark mode is not just about flipping colors. It requires careful planning, architecture-level decisions, performance optimisation, accessibility compliance, and user experience considerations. This article explores how to implement dark mode effectively in modern web applications, with a focus on Angular-based projects and real-world best practices.
1. Why Dark Mode Matters
1.1 User Experience
Dark mode improves usability in low-light conditions, reduces eye strain, and offers a modern, visually appealing interface. Users often prefer dark mode when using devices at night or in dimly lit environments.
1.2 Energy Efficiency
On OLED and AMOLED screens, dark mode reduces power consumption because black pixels are turned off, which can extend battery life for mobile devices.
1.3 Brand and Design Consistency
Many popular applications now offer dark mode, and users expect a consistent experience across platforms. Enterprises benefit from consistent theming across their web apps, mobile apps, and internal dashboards.
1.4 Accessibility
Dark mode can enhance readability for users with visual impairments, but it must be implemented thoughtfully to maintain contrast and avoid fatigue.
2. Approaches to Dark Mode Implementation
Dark mode implementation is not one-size-fits-all. The approach depends on application architecture, frameworks, and user experience goals.
2.1 CSS Variables (Recommended)
Using CSS custom properties (variables) is the most flexible approach. You define color variables in a global stylesheet and switch them dynamically for light and dark themes.
:root {
--primary-bg: #ffffff;
--primary-text: #111111;
}
[data-theme="dark"] {
--primary-bg: #121212;
--primary-text: #e0e0e0;
}
body {
background-color: var(--primary-bg);
color: var(--primary-text);
}
Benefits: Easy to maintain, supports dynamic switching, reduces duplication
Works well with Angular’s global styles and component styles
2.2 Class-Based Theming
Another approach is toggling a class on the root element (e.g., dark-mode) and styling components accordingly.
body.dark-mode {
background-color: #121212;
color: #e0e0e0;
}
2.3 Component-Level Theming
For complex Angular apps, component-level theming allows individual modules to define their own dark mode styles.
Example
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
host: { '[class.dark-mode]': 'isDarkMode' }
})
export class CardComponent {
@Input() isDarkMode = false;
}
2.4 Third-Party Libraries
Some libraries provide out-of-the-box dark mode support:
Angular Material: Built-in theming with light and dark palettes
TailwindCSS: Supports dark: variants with a single configuration flag
Ngx-dark-mode: Angular utility library for theme switching
3. Dark Mode in Angular
Angular provides multiple strategies to implement dark mode effectively:
3.1 Angular Material Theming
Angular Material supports dark mode natively through predefined palettes:
import { OverlayContainer } from '@angular/cdk/overlay';
...
toggleDarkMode(isDark: boolean) {
const overlayContainerClasses = this.overlayContainer.getContainerElement().classList;
if (isDark) overlayContainerClasses.add('dark-theme');
else overlayContainerClasses.remove('dark-theme');
}
Use SCSS theming for global and component-specific styles
Supports mat-toolbar, mat-card, mat-button theming out-of-the-box
3.2 Using CSS Variables in Angular
With Angular 15+ and component-scoped styles, you can implement theme switching at runtime:
@Injectable({ providedIn: 'root' })
export class ThemeService {
setTheme(isDark: boolean) {
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}
}
3.3 Persisting User Preferences
Use local storage, cookies, or backend user settings to remember the preferred theme:
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
3.4 Automatic Dark Mode Detection
You can detect system-level dark mode preferences using media queries:
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
4. Best Practices for Dark Mode
4.1 Avoid Pure Black Backgrounds
Use dark greys (#121212, #1e1e1e) instead of #000000
Pure black can increase eye strain and reduce readability
4.2 Maintain Proper Contrast
4.3 Handle Images and Icons
4.4 Smooth Transitions
body {
transition: background-color 0.3s ease, color 0.3s ease;
}
4.5 Respect System Preferences
4.6 Accessibility Considerations
Ensure form inputs, focus states, and buttons are clearly visible
Avoid using color alone to convey information
Test with screen readers and high-contrast modes
5. Dark Mode Architecture in Large Applications
For enterprise-grade applications, dark mode should be treated as a first-class feature, not an afterthought.
5.1 Centralised Theme Management
@Injectable({ providedIn: 'root' })
export class ThemeService {
private themeSubject = new BehaviorSubject<'light' | 'dark'>('light');
theme$ = this.themeSubject.asObservable();
toggleTheme() {
const next = this.themeSubject.value === 'dark' ? 'light' : 'dark';
this.themeSubject.next(next);
document.documentElement.setAttribute('data-theme', next);
}
}
5.2 Component-Level Theme Awareness
5.3 Theming for Third-Party Components
Angular Material, PrimeNG, or Kendo UI often support dark mode, but verify component overrides
Custom CSS may be needed for consistent experience
6. Performance Considerations
Preload critical CSS variables to avoid flash of unstyled content (FOUC)
Minimise recalculation of styles on theme switch
Avoid heavy DOM manipulations during theme toggling
Example: Using CSS variables avoids recalculating every element style:
:root {
--bg-color: #ffffff;
--text-color: #111111;
}
[data-theme="dark"] {
--bg-color: #121212;
--text-color: #e0e0e0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
7. Testing Dark Mode
Testing is critical to ensure a polished experience.
7.1 Manual Testing
Check readability, contrast, and UX
Test in various lighting conditions and devices
7.2 Automated Visual Regression Testing
Use tools like Percy, Chromatic, or Cypress
Compare screenshots in light and dark modes for unexpected changes
7.3 Accessibility Testing
8. Real-World Examples
8.1 Angular Material Dashboard
Light and dark palettes applied via OverlayContainer
Persistent theme using local storage
Dynamic component styling for charts, tables, and cards
8.2 E-Commerce Platform
Adaptive product images with dark/light versions
Dark mode toggle in user profile
CSS variable-based theming for performance
8.3 Marketing Websites
Pre-rendered dark mode via Angular Universal
Automatic detection of system preferences
Smooth transition between themes
9. Challenges in Dark Mode Implementation
Legacy Components – Older UI libraries may not support dark mode
Third-Party Widgets – May require overrides or custom theming
Content with Fixed Colors – Images, videos, and charts may clash
Performance – Switching themes on large DOM trees can cause lag
Accessibility – Incorrect contrast or focus styles can harm usability
10. Future of Dark Mode
AI-assisted theme adaptation: Automatically adjust brightness, contrast, and colors based on environment and user preferences
Dynamic dark mode: Changes according to ambient light sensor
Cross-platform consistency: Unified theme across web, mobile, and desktop applications
Smart content adaptation: Images, charts, and icons automatically switch versions for dark mode
In 2025, dark mode is no longer optional; it is expected by users and enterprises alike.
Best Practices for Senior Developers
Use CSS variables or Angular Material themes for maintainability
Respect user preferences and system settings
Ensure accessibility compliance
Optimize performance to prevent FOUC and DOM recalculations
Test thoroughly, including visual and accessibility testing
Consider component-level and app-wide architecture for scalability
Handle third-party libraries and custom assets correctly
Persist user preferences using local storage or backend settings
Offer smooth transitions between light and dark themes
Plan for future enhancements, like AI-based or dynamic themes
Conclusion
Dark mode has evolved into a critical feature of modern web applications. Its implementation requires a combination of technical skill, thoughtful UX design, and accessibility considerations.
For Angular developers, leveraging CSS variables, Angular Material theming, signals, and component-level architecture enables maintainable, scalable, and performant dark mode implementations.
Senior developers should approach dark mode as a first-class feature, integrating it into the application architecture, CI/CD pipeline, and user experience strategy. When done correctly, dark mode not only improves usability but also elevates the overall perception of the product.
Dark mode is not just a design choice. It is a responsibility for modern web developers.