Color modes
OUDS Web supports color modes. Explore our default light color mode and the dark mode.
On this page
Color modes let us change the color of text and components within a container without creating multiple variants and manually configuring each component. We provide four distinct modes: "light", "dark", "root", and "root-inverted".
The "light" and "dark" modes are static, while the "root" and "root-inverted" modes are dynamic.
These modes enable the implementation of a global dark/light mode toggle, as well as localized darker or lighter areas within the interface. Such areas can adapt dynamically when the overall mode switches, providing a flexible user interface design.
[data-bs-theme] attribute
Use the [data-bs-theme] attribute to set the color mode on a container. It can be applied on the <html> element (only light or dark values) to support the global color mode toggle. By default, our CSS behaves as if [data-bs-theme="light"] were set on <html>, even when the attribute is not present.
Static color modes
We offer two static color modes: "light" and "dark". These modes are called static because once the mode is set on an element, it remains fixed regardless of the root mode. The "light" mode will set the text color to black and the descendant components will adapt their colors accordingly. Conversely, the "dark" mode will set the text color to white, with descendant components adapting their colors to match.
To experiment with the following static modes examples, you can toggle the color mode picker of this documentation in the header.
Text and components in this container will always be in dark mode
Text and components in this container will always be in light mode
<div class="bg-always-black p-small">
<div data-bs-theme="dark">
<p>Text and components in this container will always be in dark mode</p>
<button class="btn btn-strong mb-small">Dark mode button</button>
<div class="bg-always-white p-small">
<div data-bs-theme="light">
<p>Text and components in this container will always be in light mode</p>
<button class="btn btn-strong">Light mode button</button>
</div>
</div>
</div>
</div> light mode
Light mode is the default state, it will use black text and light-mode components. To apply the light mode independently from the cascade, you can set [data-bs-theme="light"] on any element.
dark mode
Dark mode will use light gray text and dark-mode components. To apply the dark mode independently from the cascade, you can set [data-bs-theme="dark"] on any element.
Dynamic color modes
We offer two dynamic modes: "root" and "root-inverted". These modes are called dynamic because they depend only on the root element mode, so by changing the main mode, you’re changing all the dynamic areas using these modes as well. The "root" mode will reset the color mode to the root mode of your page, while the "root-inverted" mode will set the color mode to the opposite mode of your page.
To experiment with the following dynamic modes examples, you can toggle the color mode picker of this documentation in the header.
Text and components in here will always be inverted compared to the main mode
Text and components in here will always be reset to the main mode
<div class="bg-inverse-high p-small">
<div data-bs-theme="root-inverted">
<p>Text and components in here will always be inverted compared to the main mode</p>
<button class="btn btn-strong mb-small">Inverted mode button</button>
<div class="bg-inverse-high p-small">
<div data-bs-theme="root">
<p>Text and components in here will always be reset to the main mode</p>
<button class="btn btn-strong">Main mode button</button>
</div>
</div>
</div>
</div> root-inverted mode
This color mode inverts the main color mode. It will be useful for example inside a container with a darker background, like .bg-inverse-high in root light mode. To apply this mode add [data-bs-theme="root-inverted"] on any element but the main one.
root mode
This color mode reset to the main color mode. It will be useful inside a root-inverted context to switch back to the same behavior than the root color mode. To apply this mode add [data-bs-theme="root"] on any element but the main one.
How to use
You should apply a [data-bs-theme] attribute whenever you need to change the text color and the component modes inside a container.
Here is a recap table of when to use which contextual mode. Prefer to use light and dark modes as much as possible to avoid unexpected rendering. The mode to use strongly depends on the context of use and there is no manner to automate it unfortunately.
These four modes should be enough to deal with any use cases. If it’s not the case, you are probably using them wrong. Since the implementation might be quite hard to understand, don’t hesitate to contact us via our GitHub discussions please provide a reduced test case so we can help.
| Wanted local text color inside main light mode | Wanted local text color inside main dark mode | Mode that should be used |
|---|---|---|
| Dark | Dark | "light" |
| Light | Light | "dark" |
| Light | Dark | "root-inverted" |
| Dark | Light | "root" |
Examples
Here are some examples of how to use the different [data-bs-theme] attribute on different use cases.
<div class="bg-always-white">
<div class="dropdown" data-bs-theme="light">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenuButtonLight" data-bs-toggle="dropdown" aria-expanded="false">
Always light dropdown
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButtonLight">
<li><a class="dropdown-item active" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
</div>
</div>
<div class="bg-always-black">
<div class="dropdown" data-bs-theme="dark">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenuButtonDark" data-bs-toggle="dropdown" aria-expanded="false">
Always dark dropdown
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButtonDark">
<li><a class="dropdown-item active" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
</div>
</div>
<div class="bg-inverse-high">
<div data-bs-theme="root-inverted" class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenuButtonRootInverted" data-bs-toggle="dropdown" aria-expanded="false">
Main inverted mode dropdown
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButtonRootInverted">
<li><a class="dropdown-item active" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
</div>
</div>
<div class="bg-inverse-high">
<div data-bs-theme="root-inverted" class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenuButtonRoot" data-bs-toggle="dropdown" aria-expanded="false">
Main inverted mode dropdown and main mode dropdown menu
</button>
<ul data-bs-theme="root" class="dropdown-menu" aria-labelledby="dropdownMenuButtonRoot">
<li><a class="dropdown-item active" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Separated link</a></li>
</ul>
</div>
</div> Usage
Enable dark mode
Enable the built in dark color mode across your entire project by adding the [data-bs-theme="dark"] attribute to the <html> element. This will apply the dark color mode to all components and elements, other than those with a specific [data-bs-theme] attribute applied. Building on the quick start template:
<!doctype html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OUDS Web demo</title>
<link href="https://cdn.jsdelivr.net/npm/@ouds/web-orange-compact@1.1.0/dist/css/ouds-web.min.css" rel="stylesheet" integrity="sha384-N6Om8LeGFX49sYIRp4m/l0uBc8UUBGMJzGJatljQ6iOIrdNOfTFmaSFZC3Ghp45S" crossorigin="anonymous" />
</head>
<body>
<h1>Hello, world!</h1>
<script src="https://cdn.jsdelivr.net/npm/@ouds/web-common@1.1.0/dist/js/ouds-web.bundle.min.js" integrity="sha384-qPTEwuRMLbfwT0l3TtdD1vvupC5JbZopSu6JJJZu8IycZivMR0d57kv0IoLlKeW9" crossorigin="anonymous"></script>
</body>
</html>
OUDS Web do ship with a built-in color mode picker, you can use the one from our own documentation. Learn more in the JavaScript section.
Building with Sass
Our dark mode option is available to use for all users, but it’s controlled via data attributes instead of media queries and so does not automatically toggle your project’s color mode. You can disable our dark mode entirely via Sass by changing $enable-dark-mode to false.
Change default behavior
We use a custom Sass mixin, color-mode(), to help you control how color modes are applied. By default, we use a data attribute approach, allowing you to create more user-friendly experiences where your visitors can choose to have an automatic dark mode or control their preference (like in our own docs here).
In case you want to use media queries and only make color modes automatic, you can change the mixin’s default type via Sass variable. Consider the following snippet and its compiled CSS output.
$color-mode-type: data;
@include color-mode(dark) {
.element {
color: var(--bs-color-content-on-status--info-muted);
background-color: var(--bs-color-surface-status-info-muted);
}
}
Outputs to:
[data-bs-theme=dark] .element {
color: var(--bs-color-content-on-status-info-muted);
background-color: var(--bs-color-surface-status-info-muted);
}
And when setting to media-query:
$color-mode-type: media-query;
@include color-mode(dark) {
.element {
color: var(--bs-color-content-on-status-info-muted);
background-color: var(--bs-color-surface-status-info-muted);
}
}
Outputs to:
@media (prefers-color-scheme: dark) {
.element {
color: var(--bs-color-content-on-status-info-muted);
background-color: var(--bs-color-surface-status-info-muted);
}
}
Change the root selector
You can also change the root selector from where the mode variables are set. By default, it’s set to :root, but you can change it to any other selector. This is useful if you want to apply the mode to another element, for instance in Angular where you can’t access easily the <html> element.
$ouds-root-selector: "#app";
@import "@ouds/web-orange-compact/scss/ouds-web";
Outputs to:
#app,
[data-bs-theme="light"],
#app[data-bs-theme="light"] [data-bs-theme="root"],
#app[data-bs-theme="dark"] [data-bs-theme="root-inverted"] {
/* Your light mode variables definition */
}
[data-bs-theme="dark"],
#app[data-bs-theme="dark"] [data-bs-theme="root"],
#app[data-bs-theme="light"] [data-bs-theme="root-inverted"] {
/* Your dark mode variables definition */
}
/* Further OUDS Web CSS */
JavaScript
To allow visitors or users to toggle color modes, you’ll need to create a toggle element to control the [data-bs-theme] attribute on the root element, <html>. We’ve built a toggler in our documentation that initially defers to a user’s current system color mode, but provides an option to override that and pick a specific color mode.
Here’s a look at the JavaScript that powers it. Feel free to inspect our own documentation navbar to see how it’s implemented using HTML and CSS from our own components. It is suggested to include the JavaScript at the top of your page to reduce potential screen flickering during reloading of your site. Note that if you decide to use media queries for your color modes, your JavaScript may need to be modified or removed if you prefer an implicit control.
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2026 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
(() => {
'use strict'
const getStoredTheme = () => localStorage.getItem('theme')
const setStoredTheme = theme => localStorage.setItem('theme', theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const setTheme = theme => {
if (theme === 'auto') {
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}
setTheme(getPreferredTheme())
const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector('#bd-theme')
if (!themeSwitcher) {
return
}
const themeSwitcherText = document.querySelector('#bd-theme-text')
const activeThemeIcon = document.querySelector('.theme-icon-active use')
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
})
btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
if (focus) {
themeSwitcher.focus()
}
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const storedTheme = getStoredTheme()
if (storedTheme !== 'light' && storedTheme !== 'dark') {
setTheme(getPreferredTheme())
}
})
window.addEventListener('DOMContentLoaded', () => {
showActiveTheme(getPreferredTheme())
document.querySelectorAll('[data-bs-theme-value]')
.forEach(toggle => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})
})
})()