Bootstrap Colors: A Practical Guide
Learn Bootstrap’s color utilities, light and dark modes, Sass customization, and accessible contrast, with practical code examples for Bootstrap 5.3.
Bootstrap colors are easiest to use when you choose a utility by role: use .text-* for text, .bg-* for a surface, .link-* for links, and .text-bg-* when one helper should set both a background and a contrasting foreground. For site-wide theme changes, customize Bootstrap’s Sass color map; for light and dark modes in Bootstrap 5.3, use data-bs-theme and the mode-aware emphasis and subtle color variables.
This guide follows the official Bootstrap v5.3 documentation, which identifies its current documentation as v5.3.8. Check the docs and compiled behavior for the version installed in your project.
1. Understand Bootstrap’s color system
Bootstrap has a broad palette of named colors and a smaller semantic theme map. The palette includes blue, indigo, purple, pink, red, orange, yellow, green, teal, cyan, and neutral colors. The standard theme map uses semantic keys that components and generated utilities can share:
| Theme key | Typical role |
|---|---|
primary |
Main theme color for links, focus styles, and active component or form states |
secondary |
Supporting or less prominent theme color |
success |
Positive or successful actions and information |
info |
Neutral or informative content |
warning |
Warnings that do not indicate a destructive action |
danger |
Errors and dangerous actions |
light, dark |
Light and dark palette options |
The default palette values documented by Bootstrap include $blue: #0d6efd, $indigo: #6610f2, $purple: #6f42c1, $pink: #d63384, $red: #dc3545, $orange: #fd7e14, $yellow: #ffc107, $green: #198754, $teal: #20c997, and $cyan: #0dcaf0. These general palette tokens are distinct from the semantic theme names. See Bootstrap’s color customization guide for the version-specific values.
Not every palette shade has a default text or background utility. Theme colors are the main inputs used to generate many component variants and utility classes. If you need a utility for a palette color that Bootstrap does not generate, add it through Sass’s utility API.
2. Pick the utility that matches the job
Color text with .text-*
Use text utilities when the surface should remain unchanged:
<p class="text-primary">Primary text</p>
<p class="text-success">The operation completed.</p>
<p class="text-danger">The request failed.</p>
Bootstrap’s text utilities support opacity through RGB CSS variables, so an alpha value can be applied without recompiling Sass. Consult the color utilities documentation for the utility syntax supported by your installed version.
Set a surface with .bg-*
A background utility changes the background only. It does not choose a readable foreground for you:
<div class="bg-primary text-white p-3">
A primary surface with an explicit foreground.
</div>
Choose the foreground deliberately for the actual color and mode. A class combination that looks readable with the defaults can become inaccessible after customization.
Set both with .text-bg-*
For a compact badge or label, Bootstrap’s text-bg-* helpers set a background and use Sass’s color-contrast() helper to choose a contrasting foreground:
<span class="badge text-bg-warning">Review required</span>
This is convenient, but it is not a guarantee that every customized theme variable passes a contrast requirement. Check the final rendered pair.
Style links with .link-*
Link color helpers include hover and focus states. Keep links recognizable as links, and verify that keyboard focus remains visible when changing their colors:
<a class="link-primary" href="/docs">Read the documentation</a>
Use subtle and emphasis variants for surfaces and text
Bootstrap 5.3 adds theme-aware variables and utilities for emphasis text, subtle backgrounds, and subtle borders. For example:
<div class="p-3 text-primary-emphasis bg-primary-subtle border border-primary-subtle rounded-3">
A softly tinted primary message.
</div>
These variants are intended to adapt to color modes. The older .text-primary and .bg-primary utilities do not uniformly adapt; see the next section before relying on them in a theme switcher.
3. Use light and dark color modes in Bootstrap 5.3
Bootstrap 5.3 uses the data-bs-theme attribute and the Sass color-mode() mixin for color modes. Set the attribute on the root element for a page-wide mode:
<!doctype html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/path/to/bootstrap.min.css" rel="stylesheet">
<title>Dark mode example</title>
</head>
<body>
<main class="container py-4">
<h1>Color modes</h1>
<p class="text-body-secondary">This text uses a mode-aware variable.</p>
<div class="p-3 text-primary-emphasis bg-primary-subtle rounded-3">
A theme-aware surface.
</div>
</main>
</body>
</html>
You can scope the attribute to a component or container instead of the root. The scope changes variables inside it, but custom CSS and hard-coded colors can bypass Bootstrap’s variables. Inspect the actual computed foreground and background in each mode.
There is an important version 5.3 distinction: original theme-map .text-* utilities do not all respond to color modes, and original theme .bg-* utilities do not all respond either. The newer .text-*-emphasis and .bg-*-subtle variants use mode-adaptive variables. Do not assume every existing class will change when data-bs-theme changes. The color modes documentation explains the variable and Sass approaches.
Bootstrap also documents custom named modes, such as a blue mode, by overriding variables for that mode. Define the mode’s variable values in your Sass or CSS and apply the matching data-bs-theme value. Confirm that the elements in scope actually use those variables.
4. Change the palette with Sass
Use Sass when a color change should affect compiled theme tokens, generated utilities, or component variants across the site. Set existing Sass variables before importing Bootstrap so the theme map is built with the values you intend:
// custom.scss
$primary: #4b3fbb;
$danger: #b42318;
@import "bootstrap/scss/bootstrap";
Compile this file using the Sass build setup already used by your project. Bootstrap’s official guide shows how to install, import, and customize Sass in Sass customization.
Add a semantic theme color
To generate theme-based utilities and component variants for a new semantic key, merge it into $theme-colors before Bootstrap consumes the map:
// custom.scss
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
$theme-colors: map-merge(
$theme-colors,
(
"brand": #5b3cc4
)
);
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
// Import the remaining Bootstrap partials or components required by your build.
The precise import sequence depends on how much of Bootstrap your project imports. Use the official Sass documentation for the complete sequence rather than copying a partial import list into a production build unchanged. Keep the expected semantic keys unless you also change every component or partial that references them: removing a key can cause Sass compilation errors.
Generate utilities for other palette shades
Bootstrap avoids generating text and background classes for every palette shade by default, in part to limit CSS output. If you need classes such as .text-purple-500, extend the Sass utilities API. The API can generate the classes your design needs, but each addition contributes to the compiled stylesheet. Read the utilities API guide and keep the generated set focused.
5. Choose between utilities, CSS variables, and Sass
| Approach | Best fit | Things to check |
|---|---|---|
| Built-in utility class | One element or component using an existing Bootstrap token | Whether that class adapts to color modes and whether the foreground remains readable |
| CSS variable override | A runtime or mode-specific change supported by Bootstrap’s variable system | Scope the override correctly and confirm components actually consume that variable |
| Sass variable or map change | A compiled site-wide theme or new generated semantic utilities | Import order, keys required by components, build output, and contrast after compilation |
| Custom CSS | A one-off color or a color that does not belong in the theme map | Hard-coded values can bypass mode variables; document and test the pairing |
6. Check accessibility and contrast
Color can reinforce meaning, but it cannot carry meaning by itself. Bootstrap’s official guidance notes that a color-only indication is visual and is not conveyed to screen-reader users. Pair a red error with an error label or accessible text, and pair a green success state with a clear message or icon plus text.
Under WCAG 2.2 AA, normal text generally needs a contrast ratio of at least 4.5:1, and large text at least 3:1. WCAG AAA raises those figures to 7:1 for normal text and 4.5:1 for large text. The standard has exceptions, including inactive controls, purely decorative or invisible text, and text that is part of a logo or brand name.
- Check the actual foreground against its actual background in light and dark modes.
- Check links, focus indicators, borders, icons, and disabled or selected states separately.
- Keep a visible label or another accessible cue alongside status color.
- Recheck contrast after Sass or CSS variable changes; Bootstrap’s contrast helper does not certify the finished page.
Bootstrap’s customization documentation advises developers to monitor contrast ratios as they customize colors. The final rendered pairing is what matters.
7. Troubleshooting common color problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Text is hard to read on a colored panel | .bg-* changes the surface but not the text color |
Choose an explicit foreground or use a suitable .text-bg-* helper, then verify contrast |
| A color class does nothing | The class is not generated for that palette shade, Bootstrap CSS is missing, or the selector is overridden | Check the loaded stylesheet and computed styles; use a generated theme utility or extend the utilities API with Sass |
Old colors persist after changing data-bs-theme |
The class is an original .text-* or .bg-* utility that does not adapt to modes, or custom CSS hard-codes a value |
Use mode-aware emphasis/subtle variants or override the relevant variables and inspect computed styles |
| Sass reports a missing map key or variable | Import order is wrong, or a theme key used by a component was removed | Follow Bootstrap’s Sass import sequence and preserve keys expected by imported components |
| A custom color compiles but fails contrast | Sass compilation only produces CSS; it does not establish that every final pair meets WCAG | Measure the rendered foreground/background pair in each mode and adjust the token or foreground |
| Status is unclear to assistive technology | Color is the only sign of success, warning, or error | Add visible text or an accessible text alternative that identifies the state |
8. Capture color states for review
When reviewing a palette across pages or modes, capture the same page in light and dark mode at a consistent viewport. A screenshot helps compare layout and visible color treatment; it does not replace a contrast measurement or assistive technology review.
<!-- Example page setup: switch the mode before capture -->
<html lang="en" data-bs-theme="light">
...
</html>
Or skip the browser setup
For visual review, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. It can capture the page after you set its Bootstrap color mode; its clean-shot options remove cookie and consent banners, newsletter popups, and chat widgets before capture. Each step can be turned off.
Use your own API key and replace the target URL as needed. See the ScreenshotNeo API documentation for supported parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
- Cookie banners, popups, and chat widgets are removed before the shot.
- Bot checks, blank pages, and failed loads are never billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents, including Claude and Cursor, take screenshots.
- The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for 1,000 screenshots a month, with no card required.
9. Performance, reliability, and cost notes
- CSS output: Adding many utility variants increases compiled CSS. Generate only the shades and roles your interface needs.
- Runtime modes: CSS variable-based modes can change token values without rebuilding for each mode, but hard-coded colors will not follow those variables automatically.
- Reliability: Keep theme keys and import order consistent with the Bootstrap components in your build. Inspect the compiled stylesheet after Sass changes.
- Screenshot review: Capturing both modes at the same viewport makes visual comparison more consistent. A screenshot cannot establish WCAG contrast compliance by itself.
- API cost: ScreenshotNeo bills only clean shots; cache hits and bot checks, CAPTCHAs, blank pages, timeouts, and failed loads cost nothing. Plans include 1,000 free shots monthly, Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan.
10. Frequently asked questions
Does Bootstrap include a .text-purple class?
Not for every palette color and shade by default. Generate the utility through Sass’s utilities API or use a custom class.
Does .bg-primary choose a readable text color?
No. A background utility sets the background only. Set the foreground yourself or use an appropriate .text-bg-primary helper, then verify the result.
Are Bootstrap color utilities accessible by default?
A default token does not guarantee that every text, background, focus, or mode pairing meets your accessibility needs. Check the final pairing and provide non-color cues for meaning.
Should I change colors with CSS or Sass?
Use CSS variables for supported runtime or mode-specific overrides. Use Sass when you need to change compiled theme tokens or generate additional utility classes.
Can Bootstrap’s contrast helper replace an accessibility review?
No. It chooses a foreground based on color contrast calculations, but custom variables and surrounding UI states still need review against the applicable requirements.


