---
title: Color modes
-description: Bootstrap supports light and dark color modes. Dark mode is available globally by default, but can also be applied to specific components and elements via `data-bs-theme` attribute.
+description: Bootstrap supports light and dark color modes. The page follows the visitor’s system preference by default, and you can force a mode globally or per-component with the `data-bs-theme` attribute.
toc: true
---
## Dark mode
-**Bootstrap now supports color modes, starting with dark mode!** With v5.3.0 you can implement your own color mode toggler (see below for an example from Bootstrap’s docs) and apply the different color modes as you see fit. We support a light mode (default) and now dark mode. Color modes can be toggled globally on the `<html>` element, or on specific components and elements, thanks to the `data-bs-theme` attribute.
+**Bootstrap supports light and dark color modes out of the box.** They’re built on the native CSS [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) function and the [`color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) property, so our color tokens resolve to a light or dark value automatically—no recompiling and no duplicate stylesheets required.
-Alternatively, you can also switch to a media query implementation thanks to our color mode mixin—see [the usage section for details](#building-with-sass). Heads up though—this eliminates your ability to change themes on a per-component basis as shown below.
+This gives you the best of both worlds, with no flag to set:
+
+- **System preference by default.** Our `:root` is declared with `color-scheme: light dark`, so the page automatically follows the visitor’s operating system preference (`prefers-color-scheme`), just like a media query implementation.
+- **Override with a data attribute.** Set `data-bs-theme="light"` or `data-bs-theme="dark"` on the `<html>` element to force a mode for the whole page, or on any element or component to scope a mode to just that subtree.
+
+You don’t have to choose between media queries and a data attribute—because color modes are driven by `color-scheme`, the system preference and any `data-bs-theme` override work together.
## Example
## How it works
-- As shown above, color mode styles are controlled by the `data-bs-theme` attribute. This attribute can be applied to the `<html>` element, or to any other element or Bootstrap component. If applied to the `<html>` element, it will apply to everything. If applied to a component or element, it will be scoped to that specific component or element.
+- Our color tokens are defined once in `:root` using the CSS `light-dark()` function, e.g. `--bs-border-color: light-dark(var(--bs-gray-200), var(--bs-gray-700))`. Each token carries both its light and dark value, and the browser resolves the right one based on the element’s used color scheme.
-- For each color mode you wish to support, you’ll need to add new overrides for the shared global CSS variables. We do this already in our `_root.scss` stylesheet for dark mode, with light mode being the default values. In writing color mode specific styles, use the mixin:
+- The `:root` element is set to `color-scheme: light dark`, so the page honors the visitor’s system preference by default. There’s nothing else to wire up for automatic dark mode.
- ```scss
- // Color mode variables in _root.scss
- @include color-mode(dark) {
- // CSS variable overrides here...
- }
- ```
+- The `data-bs-theme` attribute overrides `color-scheme` for the element it’s set on and its descendants—`[data-bs-theme="dark"]` sets `color-scheme: dark` and `[data-bs-theme="light"]` sets `color-scheme: light`. That forces every `light-dark()` token in that subtree to resolve to the chosen mode, which is why you can flip the whole page from `<html>` or recolor just a single component (as shown above).
-- Dark mode overrides live in `_root.scss` (via the `color-mode()` mixin) and `_theme.scss`, which adjust global and theme color tokens. You don’t need these files for your own custom color modes, but they give Bootstrap a single place to reset colors for built-in dark mode.
+- For styles that can’t be expressed as a token—custom components, or additional color modes of your own—use the `color-mode()` Sass mixin to scope rules to a mode. See [Building with Sass](#building-with-sass).
## 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]([[docsref:/guides/quickstart/]]):
+By default Bootstrap already follows the visitor’s system preference, so you don’t need to do anything to get automatic dark mode. To **force** dark mode regardless of their system setting, add the `data-bs-theme="dark"` attribute to the `<html>` element. This applies the dark color mode to all components and elements, other than those with their own `data-bs-theme` attribute. Building on the [quick start template]([[docsref:/guides/quickstart/]]):
```html
<!doctype html>
### Building with Sass
-Our new dark mode option is available to use for all users of Bootstrap, but it’s controlled via data attributes instead of media queries and does not automatically toggle your project’s color mode.
+Our built-in light and dark tokens auto-toggle via `light-dark()`, so you don’t need Sass to get automatic dark mode. The `color-mode()` mixin is for the extra cases: styles that can’t be captured by a token, or additional custom color modes of your own.
+
+The mixin can emit its rules two ways, controlled by the `$color-mode-type` Sass variable:
-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). This is also an easy and scalable way to add different themes and more custom color modes beyond light and dark.
+- `media-query` (the default) wraps your styles in a `@media (prefers-color-scheme: …)` block, so they follow the system preference.
+- `data` scopes your styles to a `[data-bs-theme="…"]` selector, so they can be toggled per-page or per-component.
-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.
+For example, opting into the `data` attribute approach produces a scoped selector:
```scss
$color-mode-type: data;
}
```
-And when setting to `media-query`:
+And with the default `media-query` type, the same mixin output is wrapped in a media query instead:
```scss
$color-mode-type: media-query;
### Variables
-Dozens of root level CSS variables are repeated as overrides for dark mode. These are scoped to the color mode selector, which defaults to `data-bs-theme` but [can be configured](#building-with-sass) to use a `prefers-color-scheme` media query. Use these variables as a guideline for generating your own new color modes.
+Dozens of root-level CSS variables carry both a light and a dark value via `light-dark()`, so a single `:root` declaration covers both modes—no repeated dark mode overrides. Use these as a guideline when defining tokens for your own custom color modes.
{/* <ScssDocs name="root-dark-mode-vars" file="scss/_root.scss" /> */}
### Sass variables
-CSS variables for our dark color mode are generated from Sass in `_root.scss` and `_theme.scss`, including overrides for components that embed SVG data URIs in CSS—primarily via `mask-image` (carousel controls, close buttons, and similar icons), with select dropdown arrows as a remaining `background-image` case.
+Our light and dark color tokens are generated from Sass in `_root.scss` and `_theme.scss`, where most are emitted as `light-dark()` values so they resolve automatically. Components that paint their icons via CSS masks (carousel controls, close buttons, and similar) inherit the current color and adapt to the active mode without per-mode overrides.
{/* <ScssDocs name="sass-dark-mode-vars" file="scss/_config.scss" /> */}
## Theme variants
-Themeable components don’t hard-code their colors. Instead they read the generic `--bs-theme-*` tokens—`--bs-theme-bg`, `--bs-theme-fg`, `--bs-theme-border`, `--bs-theme-contrast`, and so on—each with a fallback to the component’s own default token. When no theme class is present the fallback applies; add a `.theme-*` class and the component picks up that theme’s colors instead.
+Themeable components support **theme variants**—a single family of composable `.theme-*` classes that recolor a component without hard-coded, per-component color classes. This replaces the previous pattern of baking color into each component (`.btn-primary`, `.alert-success`, `.bg-warning`, and so on) with one consistent API.
+
+Themeable components read a shared set of generic `--bs-theme-*` tokens for their background, foreground, border, and focus styles—each with a fallback to the component’s own default token. When no theme class is present, the fallback applies. Add a `.theme-*` class to the component (or to an ancestor) and it picks up that theme’s colors instead.
+
+<BsTable class="table">
+| Token | Description |
+| --- | --- |
+| `--bs-theme-base` | The raw base color the theme is derived from |
+| `--bs-theme-bg` | Solid background color, e.g., a solid button or badge |
+| `--bs-theme-bg-subtle` | Subtle background tint for low-emphasis surfaces |
+| `--bs-theme-bg-muted` | Muted background, between subtle and solid |
+| `--bs-theme-fg` | Foreground/text color on default surfaces |
+| `--bs-theme-fg-emphasis` | Higher-contrast foreground color |
+| `--bs-theme-border` | Border color |
+| `--bs-theme-contrast` | Contrasting color for text/icons sitting on a solid `--bs-theme-bg` |
+| `--bs-theme-focus-ring` | Focus ring color |
+</BsTable>
+
+The [`.theme-{name}` classes]([[docsref:/customize/theme#theme-utility-classes]]) mirror our semantic [theme colors]([[docsref:/customize/theme#theme-colors]])—`.theme-primary`, `.theme-secondary`, `.theme-success`, `.theme-danger`, etc. They’re generated from the `$theme-colors` Sass map, so adding or renaming a theme color automatically produces a matching `.theme-*` class. Each entry defines the tokens above for both light and dark mode via `light-dark()`:
-The [`.theme-{name}` classes]([[docsref:/customize/theme#theme-utility-classes]]) are generated from the `$theme-colors` map and simply remap the semantic role tokens onto the generic ones—`.theme-primary`, for example, sets `--bs-theme-bg: var(--bs-primary-bg)` and `--bs-theme-contrast: var(--bs-primary-contrast)`.
+```scss
+// …
+// One entry from $theme-colors in scss/_theme.scss
+"primary": (
+ "base": var(--blue-500),
+ "fg": light-dark(var(--blue-600), var(--blue-400)),
+ "fg-emphasis": light-dark(var(--blue-800), var(--blue-200)),
+ "bg": var(--blue-500),
+ "bg-subtle": light-dark(var(--blue-100), var(--blue-900)),
+ "bg-muted": light-dark(var(--blue-200), var(--blue-800)),
+ "border": light-dark(var(--blue-300), var(--blue-600)),
+ "focus-ring": light-dark(color-mix(in oklch, var(--blue-500) 50%, var(--bg-body)), color-mix(in oklch, var(--blue-500) 75%, var(--bg-body))),
+ "contrast": var(--white)
+),
+// …
+```
-Here’s how the badge consumes them in `scss/_badge.scss`. Its `color` and `background-color` each read a theme token first, falling back to the badge’s own default (CSS variables are written without the `--bs-` prefix in Sass source; PostCSS adds it at build):
+To see how a component consumes these tokens, here’s the badge in `scss/_badge.scss`. Its `color` and `background-color` each read a theme token first, falling back to the badge’s own default (CSS variables are written without the `--bs-` prefix in Sass source; PostCSS adds it at build):
```scss
.badge {
<Example code={`<span class="badge">Default badge</span>
<span class="badge theme-success">Success badge</span>`} />
+For components with variants, combine a variant class with a theme class to color it. For buttons, that means pairing a variant like `.btn-solid` or `.btn-outline` with a theme:
+
+<Example class="d-flex gap-2" code={`<button type="button" class="btn-solid theme-primary">Primary</button>
+<button type="button" class="btn-outline theme-success">Success</button>
+<button type="button" class="btn-subtle theme-danger">Danger</button>`} />
+
+Because the theme tokens are plain CSS variables, you can also scope a theme to a container and let everything inside inherit it, or override individual tokens for one-off styling—no Sass compilation required:
+
+```css
+.my-brand {
+ --bs-theme-bg: #6f42c1;
+ --bs-theme-contrast: #fff;
+}
+```
+
To make one of your own components themeable, follow the same pattern: read `var(--theme-{role}, var(--my-component-{role}))` for each color property, and it will respond to any `.theme-*` class set on it or an ancestor. See the [Theme page]([[docsref:/customize/theme]]) for the full list of role tokens and recommended pairings.
## Responsive
If you’re not using a component, comment it out or delete its `@forward` entry entirely. For example, if you’re not using the carousel, remove that line to save file size in your compiled CSS. Keep in mind there are some dependencies across Sass partials that may make it more difficult to omit a file.
+The cleanest way to do this is to maintain your own entry file that forwards only the parts of Bootstrap you need, rather than importing all of `bootstrap`. Copy the import stack above into your own stylesheet, then comment out the components you don’t use:
+
+```scss
+// custom-bootstrap.scss
+@forward "bootstrap/scss/colors";
+@forward "bootstrap/scss/root";
+
+// Layout & content
+@forward "bootstrap/scss/content";
+@forward "bootstrap/scss/layout";
+@forward "bootstrap/scss/forms";
+@forward "bootstrap/scss/buttons";
+
+// Components — keep only what you need
+@forward "bootstrap/scss/alert";
+@forward "bootstrap/scss/badge";
+@forward "bootstrap/scss/card";
+// @forward "bootstrap/scss/accordion"; // unused
+// @forward "bootstrap/scss/carousel"; // unused
+// @forward "bootstrap/scss/dialog"; // unused
+
+// Helpers & utilities
+@forward "bootstrap/scss/helpers";
+@forward "bootstrap/scss/utilities/api";
+```
+
+Then compile your `custom-bootstrap.scss` instead of Bootstrap’s default `bootstrap.scss`. Just remember to keep `colors` and `root` (and any partials your remaining components depend on), since the rest of the framework builds on the variables and tokens they define.
+
## Lean JavaScript
Bootstrap’s JavaScript includes every component in our primary dist files (`bootstrap.js` and `bootstrap.min.js`), and even our primary dependencies (Floating UI and Vanilla Calendar Pro) with our bundle files (`bootstrap.bundle.js` and `bootstrap.bundle.min.js`). While you’re customizing via Sass, be sure to remove related JavaScript.
```js
// Import just what we need
-// import 'bootstrap/js/dist/alert';
-// import 'bootstrap/js/dist/button';
-// import 'bootstrap/js/dist/carousel';
-// import 'bootstrap/js/dist/collapse';
-import 'bootstrap/js/dist/dialog';
-// import 'bootstrap/js/dist/menu';
-// import 'bootstrap/js/dist/drawer';
-// import 'bootstrap/js/dist/popover';
-// import 'bootstrap/js/dist/scrollspy';
-// import 'bootstrap/js/dist/tab';
-// import 'bootstrap/js/dist/toast';
-// import 'bootstrap/js/dist/tooltip';
+// import 'bootstrap/js/dist/alert'
+// import 'bootstrap/js/dist/button'
+// import 'bootstrap/js/dist/carousel'
+// import 'bootstrap/js/dist/collapse'
+import 'bootstrap/js/dist/dialog'
+// import 'bootstrap/js/dist/menu'
+// import 'bootstrap/js/dist/drawer'
+// import 'bootstrap/js/dist/popover'
+// import 'bootstrap/js/dist/scrollspy'
+// import 'bootstrap/js/dist/tab'
+// import 'bootstrap/js/dist/toast'
+// import 'bootstrap/js/dist/tooltip'
```
-This way, you’re not including any JavaScript you don’t intend to use for components like buttons, carousels, and tooltips. If you’re importing menus, tooltips or popovers, be sure to list the Floating UI dependency in your `package.json` file. If you’re using the datepicker, be sure to also list the Vanilla Calendar Pro dependency.
+Importing a file this way runs it for its side effects, which registers that component’s data attribute API—so `data-bs-toggle` and friends keep working without any extra code. This way, you’re not including any JavaScript you don’t intend to use for components like buttons, carousels, and tooltips. If you’re importing menus, tooltips or popovers, be sure to list the Floating UI dependency in your `package.json` file. If you’re using the datepicker, be sure to also list the Vanilla Calendar Pro dependency.
<Callout>
**Heads up!** Files in `bootstrap/js/dist` use the **default export**. To use them, do the following:
## Unused CSS
-_Help wanted with this section, please consider opening a PR. Thanks!_
+[PurgeCSS](https://github.com/FullHuman/purgecss) analyzes your markup and strips out any CSS selectors it can’t find in use, which can dramatically shrink Bootstrap’s compiled CSS. The setup below assumes a project of static HTML pages alongside Bootstrap’s JavaScript.
+
+First, install PurgeCSS as a dev dependency:
-While we don’t have a prebuilt example for using [PurgeCSS](https://github.com/FullHuman/purgecss) with Bootstrap, there are some helpful articles and walkthroughs that the community has written. Here are some options:
+```sh
+npm install --save-dev purgecss
+```
+
+Then add a `purgecss.config.js` to the root of your project:
+
+```js
+// purgecss.config.js
+export default {
+ // Files to scan for the class names actually in use
+ content: ['./**/*.html', './**/*.js'],
+ // Your compiled Bootstrap CSS (plus any of your own)
+ css: ['./css/bootstrap.css'],
+ output: './css/bootstrap.purged.css',
+ // Bootstrap 6 uses ":" in class names (e.g. `md:d-none`, `hover:opacity-50`),
+ // so the extractor must keep colons and slashes.
+ defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
+ safelist: {
+ // Classes added at runtime by Bootstrap's JS that never appear in your HTML
+ standard: ['show', 'showing', 'collapsing', 'active', 'fade', 'dialog-open'],
+ // Keep component families that are generated or toggled dynamically
+ greedy: [/^carousel/, /^menu/, /^drawer/, /^dialog/, /^tooltip/, /^popover/]
+ }
+}
+```
+
+Run it as part of your build, pointing PurgeCSS at the config:
+
+```sh
+npx purgecss --config purgecss.config.js
+```
+
+Then serve `bootstrap.purged.css` instead of the full file.
+
+<Callout type="warning">
+**Test thoroughly after purging.** Bootstrap toggles many classes with JavaScript (`.show`, `.active`, `.collapsing`, and more) and generates others dynamically (carousels, menus, dialogs). Anything PurgeCSS can’t find in the files it scans will be removed, so use the `safelist` option to protect runtime classes and verify every interactive component still works.
+</Callout>
-- https://medium.com/dwarves-foundation/remove-unused-css-styles-from-bootstrap-using-purgecss-88395a2c5772
-- https://lukelowrey.com/automatically-removeunused-css-from-bootstrap-or-other-frameworks/
+For deeper walkthroughs, the community has written some helpful guides:
-Lastly, this [CSS Tricks article on unused CSS](https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/) shows how to use PurgeCSS and other similar tools.
+- [Remove unused CSS styles from Bootstrap using PurgeCSS](https://medium.com/dwarves-foundation/remove-unused-css-styles-from-bootstrap-using-purgecss-88395a2c5772)
+- [Automatically remove unused CSS from Bootstrap or other frameworks](https://lukelowrey.com/automatically-removeunused-css-from-bootstrap-or-other-frameworks/)
+- [CSS-Tricks: How do you remove unused CSS from a site?](https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/)
## Minify and gzip
One line of changes to your HTML is all it takes to enable RTL. Bootstrap will then automatically adapt to the RTL direction using logical properties.
-```diff
-- <html lang="en">
-+ <html lang="ar" dir="rtl">
+```html
+<html lang="en">
+<html lang="ar" dir="rtl"><!-- [!code highlight] -->
```
### Live demo
Toggle between LTR and RTL on this page to see Bootstrap’s logical properties in action:
-<div class="bd-example">
- <div class="form-field">
- <div class="switch">
- <input type="checkbox" value="" id="rtlSwitch" role="switch" switch />
+<div class="bd-example-snippet bd-code-snippet not-prose">
+ <div class="bd-example">
+ <div class="form-field">
+ <div class="switch">
+ <input type="checkbox" value="" id="rtlSwitch" role="switch" switch />
+ </div>
+ <label for="rtlSwitch">Enable RTL mode</label>
</div>
- <label for="rtlSwitch">Enable RTL mode</label>
</div>
</div>
## Root variables
-Here are the variables we include (note that the `:root` is required) that can be accessed anywhere Bootstrap’s CSS is loaded. They’re located in our `_root.scss` file and included in our compiled dist files.
-
-### Default
-
-These CSS variables are available everywhere, regardless of color mode.
+Here are the variables we include (note that the `:root` is required) that can be accessed anywhere Bootstrap’s CSS is loaded. They’re located in our `_root.scss` file and included in our compiled dist files. Many of these resolve their light and dark values automatically via the CSS [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) function, so a single `:root` declaration covers both [color modes]([[docsref:/customize/color-modes]]).
<Code lang="css" filePath="dist/css/bootstrap.css" fileMatch=":(root {[^}]*})" />
-### Dark mode
-
-These variables are scoped to our built-in dark mode.
-
-<Code lang="css" filePath="dist/css/bootstrap.css" fileMatch="(\[data-bs-theme=dark\] {[^}]*})" />
-
## Component variables
Bootstrap makes use of custom properties as local variables for various components. This way we reduce our compiled CSS, ensure styles aren’t inherited in places like nested tables, and allow some basic restyling and extending of Bootstrap components after Sass compilation.
-Have a look at our table documentation for some [insight into how we’re using CSS variables]([[docsref:/content/tables#how-do-the-variants-and-accented-tables-work]]). Our [navbars also use CSS variables]([[docsref:/components/navbar#css]]) as of v5.2.0. We’re also using CSS variables across our grids—primarily for gutters the [new opt-in CSS grid]([[docsref:/layout/css-grid]])—with more component usage coming in the future.
+Nearly every component exposes its own set of CSS variables, scoped to the component’s base class. For example, the alert component reads a handful of `--bs-alert-*` variables that you can override in your own CSS—no Sass compilation required:
+
+```css
+.alert {
+ --bs-alert-padding-x: 1.5rem;
+ --bs-alert-padding-y: 1rem;
+ --bs-alert-border-radius: var(--bs-radius-5);
+}
+```
+
+Have a look at our table documentation for some [insight into how we’re using CSS variables]([[docsref:/content/tables#how-do-the-variants-and-accented-tables-work]]). Our [navbars also use CSS variables]([[docsref:/components/navbar#css]]), and we use CSS variables across our grids—primarily for gutters and the [opt-in CSS grid]([[docsref:/layout/css-grid]]).
Whenever possible, we’ll assign CSS variables at the base component level (e.g., `.navbar` for navbar and its sub-components). This reduces guessing on where and how to customize, and allows for easy modifications by our team in future updates.
A better alternative for those using this type of frameworks is to use a framework-specific package **instead of** the Bootstrap JavaScript. Here are some of the most popular options:
- React: [React Bootstrap](https://react-bootstrap.github.io/)
- <Callout>
- **Try it yourself!** Download the source code and working demo for using Bootstrap with React, Next.js, and React Bootstrap from the [twbs/examples repository](https://github.com/twbs/examples/tree/main/react-nextjs). You can also [open the example in StackBlitz](https://stackblitz.com/github/twbs/examples/tree/main/react-nextjs?file=src%2Fpages%2Findex.tsx).
- </Callout>
- Vue: [BootstrapVue](https://bootstrap-vue.org/) (Bootstrap 4)
- Vue 3: [BootstrapVueNext](https://bootstrap-vue-next.github.io/bootstrap-vue-next/) (Bootstrap 5)
- Angular: [ng-bootstrap](https://ng-bootstrap.github.io/) or [ngx-bootstrap](https://valor-software.com/ngx-bootstrap)
Bootstrap 6 is a major release with many breaking changes to modernize our codebase, adopt newer build tools, and improve customization. Keep reading for a guide on how to migrate from v5 to v6, and a full changelog of what’s new.
+<Callout type="info">
+**Migrating with an AI coding agent?** Bootstrap ships a [v5-to-v6 migration skill](https://github.com/twbs/bootstrap/tree/main/skills/bootstrap-v5-v6-migration) (`skills/bootstrap-v5-v6-migration`) that walks coding agents like Claude through the upgrade phase by phase—dependencies, class and attribute renames, component restructuring, JavaScript, and Sass. Point your agent at the `SKILL.md` file to automate much of the work below, then review its changes against this guide.
+</Callout>
+
1. Bump your Bootstrap dependency:
```json