Managing Light/Dark Themes with HTML and CSS: Native Solution

This second article in the guide focuses on managing light and dark themes using HTML and CSS, without relying on JavaScript.
We’ll explore how to leverage native web features to automatically adapt a site’s appearance based on the user’s system preferences:
colors, images, favicons, and even the mobile navigation bar.
The goal: to deliver a consistent, performant, and accessible experience right from the initial page load.
I. HTML: Structuring and Optimizing for Light/Dark Themes
While HTML isn't directly responsible for visual appearance, it plays an important role in managing light and dark themes by laying the groundwork for CSS and JavaScript to build on effectively. Several specific tags help tailor the user experience based on system preferences or manual actions, while also improving accessibility and system integration.
1. Customizing the Mobile Navigation Bar Color
Adding <meta name="theme-color">
tags lets you define the top bar color of mobile browsers, especially on Android.
This color can even be dynamic, adapting to the user’s preferred theme:
<!-- Color for light theme -->
<meta name="theme-color" content="#0e93f0" media="(prefers-color-scheme: light)">
<!-- Color for dark theme -->
<meta name="theme-color" content="#121212" media="(prefers-color-scheme: dark)">
This approach enhances visual immersion by aligning the web interface with native system elements. It also contributes to stronger brand consistency on mobile, especially for Progressive Web Apps (PWAs).
2. Using a Dark Mode–Specific Favicon
For readability and aesthetic reasons, it's a good idea to provide an alternative version of your favicon for users in dark mode.
Light-colored icons can become hard to see, or even invisible, against dark browser tab backgrounds.
Fortunately, HTML allows you to define a conditional favicon using the media
attribute.
<!-- Default (light) favicon -->
<link rel="icon" href="/favicon-light.svg" type="image/svg+xml">
<!-- Favicon for dark theme -->
<link rel="icon" href="/favicon-dark.svg" type="image/svg+xml" media="(prefers-color-scheme: dark)">
There's no need to explicitly define a light theme version, the browser will load the default favicon unless a media
rule for dark mode is matched.
This keeps your code clean and improves compatibility.
3. Providing Alternative Images for Dark Mode
Some embedded images, such as logos, illustrations, or stylized screenshots, may become unreadable in dark mode, especially if they contain light elements or have a transparent background. To avoid this and maintain a visually consistent interface, it's a good idea to provide an alternative version for dark mode.
Thanks to the <picture>
element, HTML allows you to dynamically load a different image based on the user's theme preference.
<picture>
<source srcset="/images/logo-dark.png" media="(prefers-color-scheme: dark)">
<img src="/images/logo-light.png" alt="Site logo">
</picture>
The browser will automatically select the appropriate image based on context:
- If the user is in light mode, it will display the default image (
logo-light.png
). - If the system is set to dark mode, it will use the image specified by the
media
attribute.
There's no need to explicitly define a light mode image, just like with favicons, the image in the <img>
tag serves as a fallback when no media query applies.
This method ensures an optimal visual experience without JavaScript and aligns perfectly with the progressive enhancement approach to dark mode.
4. Accessibility and User Controls
When offering a button to manually switch themes, it’s essential to consider accessibility.
The button should use a semantic element like <button>
and include a clear label using the aria-label
attribute.
<button aria-label="Activate dark mode">🌓</button>
The aria-label
attribute is crucial for accessibility. It provides an invisible text description for screen readers and assistive technologies.
Without it, a visually impaired user would have no idea what the button does, especially if it only contains an icon.
By clearly describing the action (e.g., “Switch theme”), you ensure an inclusive experience for all users.
While adding such a button requires handling via JavaScript (which we’ll cover in Part 3 of this guide), the HTML should be designed from the start to be understandable, usable, and accessible.
II. CSS: Styling Light and Dark Themes Effectively
CSS is the cornerstone of managing light and dark themes. It allows you to dynamically adjust colors, contrast, backgrounds, and other visual properties based on the user’s preferences or system settings. This section explains how to build a clean, maintainable, and scalable theming system.
1. Defining Colors with CSS Variables (Custom Properties)
Instead of repeating color values in each rule, it’s best to centralize them using CSS variables. This makes theme switching much easier to manage and maintain:
:root {
--bg-color: #f0f0f0;
--text-color: #121212;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
2. Using prefers-color-scheme
to Detect the System Theme
The @media (prefers-color-scheme)
media query allows you to detect whether the user has set their operating system to use a dark or light theme.
This enables your site or application to automatically adapt its appearance, with no JavaScript required.
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #121212;
--text-color: #f0f0f0;
}
}
This is the simplest way to respect user preferences, and it's widely supported by modern browsers.
To do this, we'll use the CSS filter
property:
3. Adapting Images with CSS Filters
You can use CSS filters to adjust a single image for both light and dark themes, without needing multiple versions of the file:
<img src="image.jpg" class="theme-adaptive" alt="Image filtered based on theme">
.theme-adaptive {
transition: filter 0.3s ease;
}
@media (prefers-color-scheme: dark) {
.theme-adaptive {
filter: brightness(0.8) contrast(1.2);
}
}
Warning: This method is not recommended in most cases
While this trick can be helpful in a pinch, it comes with several important limitations:
- Limited control: results can vary significantly depending on the image’s colors and type (photo, illustration, logo, etc.).
- Often not visually appealing: images may look “washed out,” too dark, or poorly adjusted.
- Not accessible: images that are important for understanding or user experience shouldn’t rely solely on filters.
4. Smooth Transitions Between Themes
To avoid a harsh visual switch when toggling between themes, it's a good idea to add a transition:
body {
transition: background-color 0.3s ease, color 0.3s ease;
}
This significantly improves the user experience by making the theme switch feel smoother and more natural.
III. Conclusion
Thanks to the native capabilities of HTML and CSS, it’s now possible to offer an elegant, performant dark theme that respects the user’s system preferences, all without writing a single line of JavaScript. However, this approach is limited to automatic behavior: it doesn’t allow users to manually choose or switch themes.
That’s what we’ll cover in the next part of this guide, where we’ll explore how to dynamically manage themes using JavaScript:
toggle buttons, saving preferences, adapting visuals, and much more:
Managing Light/Dark Themes: Letting Users Choose with JavaScript
The Complete Guide
-
May 2025 HTML CSS JS 🌙 Dark Mode 🧩 UX ♿ Accessibility
0. Managing Light and Dark Themes with HTML, CSS, and JavaScript: Introduction
-
May 2025 🌙 Dark Mode 🧩 UX ♿ Accessibility
1. Managing Light/Dark Themes: Basics, Accessibility, and Best Practices
-
May 2025 HTML CSS 🌙 Dark Mode 🧩 UX ♿ Accessibility
2. Managing Light/Dark Themes with HTML and CSS: Native Solution
-
May 2025 HTML CSS JS 🌙 Dark Mode 🧩 UX ♿ Accessibility
3. Managing Light/Dark Themes with JavaScript: Giving Users Full Control

BrowserUX Theme Switcher is a lightweight, accessible, and customizable Web Component designed to easily add a theme switcher button to any website or application: BrowserUX Theme Switcher
Go Further
If you’d like to explore how to better account for user preferences in your interfaces, here are a few resources you may find helpful:
-
May 2025 CSS ♿ Accessibility 🧩 UX
CSS: Improve Accessibility by Respecting Users’ Contrast Preferences with
prefers-contrast
-
May 2025 CSS ♿ Accessibility 🧩 UX
CSS: Adapting Animations to User Preferences with
prefers-reduced-motion

browserux.css is a base CSS file designed as a modern alternative to classic resets and Normalize.css, focused on user experience and accessibility. It lays accessible, consistent foundations adapted to today's web usage: browserux.css
About
This blog was designed as a natural extension of the BrowserUX ecosystem projects.
Its goal is to provide complementary resources, focused tips, and detailed explanations around the technical choices, best practices, and accessibility principles that structure these tools.
Each article or tip sheds light on a specific aspect of modern front-end (CSS, accessibility, UX, performance…), with a clear intention: to explain the “why” behind each rule to encourage more thoughtful and sustainable integration in your projects.