OUDS Web supports color modes. Explore our default light color mode and the dark mode.

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.

Note

A note on naming

We talk about color modes instead of themes as this could refer to other concepts. The attribute is named [data-bs-theme] for Bootstrap compatibility reasons but it might change in future versions.

[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.

Heads up!

Usage with background utilities

[data-bs-theme] is often used in conjunction with the .bg-* utilities because changing the background color may require adjusting the color mode for descendant elements. If you set up a container with a .bg-* class, you must use the color mode attribute on a single child element otherwise the background-color property will conform to the attribute value instead of the inherited color mode. More information in the background utilities page.

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>
html

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.

Heads up!

By definition these modes can not be used on the root element.

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>
html

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.

Note

Sometimes you won’t need to set the mode since it will be the same as the current one. Please check out our background utilities to know more about this specific topic.

Wanted local text color
inside main light mode
Wanted local text color
inside main dark mode
Mode that should be used
DarkDark"light"
LightLight"dark"
LightDark"root-inverted"
DarkLight"root"

Heads up!

root mode should be used only in very specific occasions, most of the time it will cascade from the main mode.

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>
html

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@1.2.0/dist/css/ouds-web.min.css" rel="stylesheet" integrity="sha384-FjWWODMGdMNagvhRmdsoutq6pd0LshOZcVcRBX8vElCfmXq7iyp8KG4uFS/Faxg/" crossorigin="anonymous" />
  </head>
  <body>
    <h1>Hello, world!</h1>
    <script src="https://cdn.jsdelivr.net/npm/@ouds/web-common@1.2.0/dist/js/ouds-web.bundle.min.js" integrity="sha384-3PMgD761aEY5iliNp5d90Rle7y3mk3Yx1MtMN7wvmDUeoxvS1C2ZDApXL9CSDOCU" 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/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)
        })
      })
  })
})()