1. Automatic Detection of System Theme
If no user preference is yet defined, the component automatically detects the system's preferred theme using the CSS rule:
@media (prefers-color-scheme: dark)
BrowserUX Theme Switcher is a lightweight and accessible Web Component that makes it easy to switch between light and dark themes, with system detection, persistence, and CSS customization.
The browserux-theme-switcher
component dynamically applies a light or dark theme
to an element on your page (html
by default, or another element using the target
attribute).
It follows a three-step logic:
If no user preference is yet defined, the component automatically detects the system's preferred theme using the CSS rule:
@media (prefers-color-scheme: dark)
When the user clicks the button to toggle the theme, their preference
(light
or dark
) is saved in localStorage
.
This preference will:
The component dynamically sets or updates the data-theme
attribute
on the targeted element, for example:
<html data-theme="dark">...</html>
This allows you to:
.has-dark
)theme-change
)
npm install browserux-theme-switcher
Or via CDN:
<script type="module" src="https://unpkg.com/browserux-theme-switcher/dist/browserux-theme-switcher.min.js"></script>
Use the .esm.js
version if you're integrating the component via a bundler (React, Vue, etc.),
and the .min.js
version for direct HTML integration via CDN.
browserux-theme-switcher
Web Component1. Simply import the component into your code:
import 'browserux-theme-switcher';
2. Then use it in your HTML:
<browserux-theme-switcher></browserux-theme-switcher>
1. Add this to your React component (often inside a useEffect
):
import { useEffect } from 'react';
useEffect(() => {
import('browserux-theme-switcher');
}, []);
2. Then in your JSX:
<browserux-theme-switcher></browserux-theme-switcher>
Add the types/browserux-theme-switcher.d.ts
file for better TypeScript support with JSX.
1. Add this in main.js
or main.ts
:
import 'browserux-theme-switcher';
2. Use it like a native component:
<browserux-theme-switcher></browserux-theme-switcher>
1. Import it in main.ts
:
import 'browserux-theme-switcher';
2. Add CUSTOM_ELEMENTS_SCHEMA
to your AppModule
:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
1. Add the component directly via a CDN:
<script type="module" src="https://unpkg.com/browserux-theme-switcher/dist/browserux-theme-switcher.min.js"></script>
2. Then:
<browserux-theme-switcher></browserux-theme-switcher>
To apply the light or dark theme to your page, you need to define your colors using CSS variables.
The <browserux-theme-switcher>
component automatically sets a data-theme="dark"
or "light"
attribute on the targeted element (default is html
), which allows your interface to be styled dynamically.
:root {
--bux-page-bg: #eaeaea;
--bux-page-color: #121212;
--bux-color-primary: #f05e0e;
--bux-color-secondary: #0e93f0;
--bux-white: #fff;
}
/** Mode sombre automatique selon les préférences système */
@media (prefers-color-scheme: dark) {
:root {
--bux-page-bg: #333;
--bux-page-color: #eaeaea;
--bux-color-primary: #eb8a55;
--bux-color-secondary: #58aae3;
--bux-white: #444;
}
}
/** Mode sombre forcé via browserux-theme-switcher */
[data-theme="dark"] {
--bux-page-bg: #333;
--bux-page-color: #eaeaea;
--bux-color-primary: #eb8a55;
--bux-color-secondary: #58aae3;
--bux-white: #444;
}
:root
defines the default colors (light mode).@media (prefers-color-scheme: dark)
accounts for system preferences if the user hasn't selected a theme yet.[data-theme="dark"]
forces dark mode when the user clicks the browserux-theme-switcher button.
The switcher applies data-theme="dark"
or data-theme="light"
to the targeted element
(html
by default, or a container via the target
attribute).
You must therefore apply your CSS variables on the same element or a common parent.
The browserux-theme-switcher
component automatically supports theme-adaptive images (light or dark)
using the special has-dark
class.
When you add the has-dark
class to an image in your HTML:
<img src="logo.png" class="has-dark" alt="Logo">
The component will automatically replace the src
attribute with a -dark
version
when dark mode is active, and revert to the original image when switching back to light mode.
name.ext
(e.g., logo.png
).name-dark.ext
(e.g., logo-dark.png
).
<img src="logo.png" class="has-dark">
→ Displays logo.png
→ Automatically replaced with logo-dark.png
The change is reversible and instantaneous with each theme toggle, without page reload or additional JavaScript.
browserux-theme-switcher
Parametersbrowserux-theme-switcher
offers a wide range of customization options:
Feature | Type | Name | Description |
---|---|---|---|
Custom Target | Attribute | target |
Applies the theme to a specific element |
Internationalization | Attribute | lang |
Language selection |
ARIA Accessibility | Attribute | data-label-* |
Customizable accessible labels |
Optional Shadow DOM | Attribute | no-shadow |
Disable encapsulation |
CSS Customization | Attribute | style |
Customization via CSS variables |
Custom Event | Event | theme-change |
Event emitted on every theme change |
Icon Slots | Slot | *-icon |
Icon customization |
target
)
By default, the browserux-theme-switcher
component applies the theme
(data-theme="light"
or "dark"
) to the html
element.
However, you can customize this target using the target
attribute.
target
string
(valid CSS selector)html
data-theme
attribute to the matching element
<browserux-theme-switcher
target="#app"
></browserux-theme-switcher>
<div id="app">
<!-- The theme is applied here -->
</div>
In this example, it’s the #app
element (not html
) that will receive the data-theme
attribute.
This allows you to scope the theme to a specific container within your application—useful for
micro-frontends, app shells, or embedded widgets.
Make sure your CSS styles rely on [data-theme="dark"]
or
[data-theme="light"]
applied to the correct selector:
#app[data-theme="dark"] {
--page-bg: #333;
/* etc. */
}
If the selector provided in the target
attribute doesn't match any element at render time,
the fallback will be html
.
lang
)
The browserux-theme-switcher
component supports multiple languages for its
accessible labels (e.g., “Activer sombre”, “Switch to light mode”, etc.).
lang
string
("en
", "fr
", "es
", "de
",
"ja
", "ru
", "pt
", "it
", "nl
")aria-label
) of the switch
<browserux-theme-switcher
lang="es"
></browserux-theme-switcher>
The ARIA label of the button will automatically be in French:
aria-label="Activer le mode sombre"
or aria-label="Activer le mode clair"
lang
is not setIf you don’t specify the lang
attribute, the component uses the following logic:
lang
attribute on the browserux-theme-switcher
taglang
value on the html lang="..."
tagen
" (English)The component supports the following languages for accessible labels (aria-label
):
data-label-light
/ data-label-dark
)
The browserux-theme-switcher
component is designed to be accessible for screen readers,
using dynamic aria-label
attributes that describe the button’s action (e.g., switch to light or dark mode).
By default, these labels are generated automatically based on the language (via the lang
attribute).
However, you can override them with your own custom text using the following attributes:
Attribute | Purpose |
---|---|
data-label-light |
Label when the current theme is dark and the button switches to light mode |
data-label-dark |
Label when the current theme is light and the button switches to dark mode |
<browserux-theme-switcher
data-label-light="Activate light theme"
data-label-dark="Switch to dark mode"
></browserux-theme-switcher>
Result:
aria-label="Switch to dark mode"
aria-label="Activate light theme"
These attributes take precedence over automatic language detection (lang
).
no-shadow
)
By default, the browserux-theme-switcher
component uses the Shadow DOM to encapsulate its HTML and CSS.
This ensures that its internal styles don't affect the rest of the page—and vice versa.
However, in certain cases—such as applying global styles or dealing with specific framework limitations— it may be useful to disable this encapsulation.
no-shadow
boolean
(presence-only)
<browserux-theme-switcher no-shadow></browserux-theme-switcher>
This component:
shadowRoot
)no-shadow
?Caution: Without Shadow DOM, the component becomes more sensitive to global style conflicts. Use with care in large applications.
style
)
The browserux-theme-switcher
component exposes several customizable CSS variables
that allow you to modify its appearance without overriding internal styles.
Variable | Default | Description |
---|---|---|
--bux-switch-width | 40px |
Toggle button width |
--bux-switch-height | 24px |
Toggle button height |
--bux-switch-bg-color | #888 |
Switch background color |
--bux-switch-thumb-color | #fff |
Thumb color |
--bux-switch-emoji-size | inherit |
Emoji icon size |
<browserux-theme-switcher
style="
--bux-switch-width: 60px;
--bux-switch-height: 32px;
--bux-switch-bg-color: #222;
--bux-switch-thumb-color: orange;
--bux-switch-emoji-size: 1.5rem;"
></browserux-theme-switcher>
[data-theme="dark"]
) or breakpoints (media queries).Shadow DOM
is enabled, thanks to the use of CSS custom properties
.theme-change
)
The browserux-theme-switcher
component dispatches a custom event named
theme-change
every time the theme changes (e.g., due to a user click,
an initial load with localStorage
, etc.).
This event allows your application to respond dynamically to theme changes (layout updates, analytics, etc.).
theme-change
CustomEvent
where e.detail.theme
contains the new theme value ("light"
or "dark"
).
const switcher = document.querySelector('browserux-theme-switcher');
switcher?.addEventListener('theme-change', (e) => {
console.log('Thème sélectionné :', e.detail.theme);
});
body
The event is available as soon as the component initializes and works in all contexts (framework or plain HTML).
light-icon
/ dark-icon
)
The browserux-theme-switcher
component allows you to customize the appearance of its button
by replacing the default icons using HTML slots.
Slot | Displayed when theme is... | Example usage |
---|---|---|
light-icon |
active = dark (icon to switch to light mode) | ☀️, sun, light.svg |
dark-icon |
active = light (icon to switch to dark mode) | 🌙, moon, moon.svg |
<browserux-theme-switcher>
<span slot="light-icon">🔆</span>
<span slot="dark-icon">🌑</span>
</browserux-theme-switcher>
Or with images:
<browserux-theme-switcher>
<img slot="light-icon" src="sun.svg" width="20" height="20" alt="Theme clair">
<img slot="dark-icon" src="moon.svg" width="20" height="20" alt="Theme sombre">
</browserux-theme-switcher>
If no slots are provided, default icons are used (☀️ / 🌙).
npm install
npm run build
Use TypeScript + Rollup to build:
dist/browserux-theme-switcher.esm.js
dist/browserux-theme-switcher.umd.js
dist/browserux-theme-switcher.d.ts
MIT License, free to use, modify, and distribute.