Bootstrap Advanced — Dark Mode, Customization & JavaScript API Explained
Bootstrap advanced techniques let you customize the framework to match your brand, enable dark mode, build from Sass source, and control interactive components via JavaScript.
What You’ll Learn
- Enable dark mode globally or on specific components
- Override Bootstrap’s CSS variables for runtime theming
- Customize Bootstrap with Sass variable overrides
- Build Bootstrap from source with npm and sass
- Control interactive components with the JavaScript plugin API
- Implement RTL support for right-to-left languages
Why Advanced Customization Matters
Out-of-the-box Bootstrap works, but every brand has its own colors, spacing, and visual identity. Hardcoding overrides on top of Bootstrap creates maintenance headaches. Using Bootstrap’s customization system keeps your theme clean and upgrade-safe.
At DodaTech, Durga Antivirus Pro uses a custom Bootstrap build with brand colors and dark mode for its security dashboard. Doda Browser implements system-preference-based theme switching using Bootstrap 5.3+’s CSS variables. Both benefit from the same framework with different themes.
Your Learning Path
flowchart LR
A[Bootstrap Utilities] --> B[Bootstrap Advanced]
B --> C[Bootstrap Reference]
B:::current
classDef current fill:#0d6efd,color:#fff,stroke:#0a58ca,stroke-width:2px
Dark Mode (Bootstrap 5.3+)
Bootstrap 5.3 introduced built-in dark mode using CSS custom properties and the data-bs-theme attribute. You don’t need a separate dark CSS file.
Global Dark Mode
<!-- Add data-bs-theme="dark" to the <html> element.
Every component on the page automatically uses dark colors. -->
<html lang="en" data-bs-theme="dark">Per-Component Dark Mode
<!-- You can mix themes on the same page.
This card uses dark mode while the rest of the page uses light. -->
<div class="card" data-bs-theme="dark">
<div class="card-body">
<h5 class="card-title">Dark Card</h5>
<p class="card-text">This card is always in dark mode.</p>
</div>
</div>JavaScript Theme Toggle
<button id="themeToggle" class="btn btn-outline-primary">Toggle Dark Mode</button>
<script>
function toggleTheme() {
var html = document.querySelector("html");
var current = html.getAttribute("data-bs-theme") || "light";
html.setAttribute("data-bs-theme", current === "dark" ? "light" : "dark");
}
document.getElementById("themeToggle").addEventListener("click", toggleTheme);
</script>Detecting System Preference
// Check if the user's OS is set to dark mode
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.querySelector("html").setAttribute("data-bs-theme", "dark");
}CSS Variables — Runtime Theming
Bootstrap 5.3+ generates CSS custom properties for every component. You can override them in your stylesheet without recompiling Sass:
/* Override primary color on a specific section */
.my-section {
--bs-primary: #e91e63;
--bs-primary-rgb: 233, 30, 99; /* RGB needed for rgba() operations */
}
/* Override global border radius */
:root {
--bs-border-radius: 0.5rem;
--bs-border-radius-lg: 1rem;
}
/* Custom card header background */
.my-card {
--bs-card-cap-bg: #f8f9fa;
--bs-card-border-color: #dee2e6;
}Key CSS Variables
:root {
/* Colors */
--bs-primary: #0d6efd;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary: #6c757d;
--bs-success: #198754;
/* Body */
--bs-body-font-family: system-ui;
--bs-body-font-size: 1rem;
--bs-body-color: #212529;
--bs-body-bg: #fff;
/* Borders */
--bs-border-radius: 0.375rem;
--bs-border-color: #dee2e6;
}Why both --bs-primary and --bs-primary-rgb? Bootstrap functions like rgba(var(--bs-primary-rgb), 0.5) need the RGB values separated. Always set both when overriding a color.
Color Modes — Custom Themes
You can create entirely custom color modes beyond light and dark:
<html data-bs-theme="custom">[data-bs-theme="custom"] {
--bs-body-bg: #1a1a2e;
--bs-body-color: #e0e0e0;
--bs-primary: #e94560;
--bs-primary-rgb: 233, 69, 96;
/* Override any other variable... */
}Customization with Sass
For full control, customize Bootstrap’s Sass variables before compilation.
Setup
npm install bootstrap sassVariable Overrides
Create a file src/custom.scss:
// 1. OVERRIDE variables BEFORE importing Bootstrap.
// These values replace Bootstrap's defaults.
$primary: #e91e63;
$secondary: #9c27b0;
$border-radius: 0.5rem;
$enable-shadows: true;
// 2. Import Bootstrap AFTER overrides
@import "bootstrap/scss/bootstrap";
// 3. Your custom styles
.my-custom-class {
color: $primary;
}Build Configuration
{
"scripts": {
"build": "sass src/custom.scss dist/custom.css",
"watch": "sass --watch src/custom.scss dist/custom.css"
}
}npm run buildCommon Variables to Override
// Colors
$primary: #0d6efd;
$secondary: #6c757d;
// Typography
$font-family-sans-serif: system-ui, -apple-system, sans-serif;
$font-size-base: 1rem;
// Grid
$grid-breakpoints: (
xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px
);
$grid-columns: 12;
$grid-gutter-width: 1.5rem;
// Components
$border-radius: 0.375rem;
$enable-shadows: false;
$enable-gradients: false;Why override variables before importing? Bootstrap uses Sass’s !default flag on its variables. Your overrides (without !default) take priority because they’re defined first.
RTL Support — Right-to-Left Languages
Bootstrap 5 includes built-in RTL support for Arabic, Hebrew, Urdu, and other RTL scripts:
<!-- Use the RTL-specific Bootstrap CSS file -->
<html lang="ar" dir="rtl">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">How it works: Bootstrap uses logical properties (start/end instead of left/right). In RTL mode, start becomes right and end becomes left automatically. No class changes needed.
JavaScript Plugin API
Every interactive Bootstrap component can be controlled programmatically.
Constructor Pattern
// All components follow the same pattern:
// new bootstrap.ComponentName(element, options)
var modal = new bootstrap.Modal(element, { backdrop: "static" });
var tooltip = new bootstrap.Tooltip(element, { placement: "right" });
var popover = new bootstrap.Popover(element, { trigger: "hover" });
var toast = new bootstrap.Toast(element, { delay: 3000 });
var carousel = new bootstrap.Carousel(element, { interval: 4000 });
var offcanvas = new bootstrap.Offcanvas(element, { scroll: true });
var collapse = new bootstrap.Collapse(element, { toggle: false });
var tab = new bootstrap.Tab(element);
var dropdown = new bootstrap.Dropdown(element);
var scrollspy = new bootstrap.ScrollSpy(element, { target: "#nav" });Static Methods — Getting Instances
// Get an existing instance (returns null if not initialized)
var modalInstance = bootstrap.Modal.getInstance(element);
// Get or create (returns existing or creates new)
var modalInstance = bootstrap.Modal.getOrCreateInstance(element, options);
// Dispose — clean up when removing elements
modalInstance.dispose();Event Handling
Every component emits events with the .bs. namespace:
// Modal events
element.addEventListener("show.bs.modal", function(e) { /* before show */ });
element.addEventListener("shown.bs.modal", function(e) { /* after animation */ });
element.addEventListener("hide.bs.modal", function(e) { /* before hide */ });
element.addEventListener("hidden.bs.modal", function(e) { /* after close anim */ });
// Tooltip/Popover: show, shown, hide, hidden, inserted
// Toast: show, shown, hide, hidden
// Offcanvas: show, shown, hide, hidden
// Collapse: show, shown, hide, hidden
// Carousel: slide, slid
// Dropdown: show, shown, hide, hidden
// Tab: show, shown, hide, hidden
Re-initializing After Dynamic Content
When you add new elements via AJAX, tooltips/popovers/dropdowns on those elements won’t work until you initialize them:
function initDynamicComponents(container) {
container.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el) {
new bootstrap.Tooltip(el);
});
container.querySelectorAll('[data-bs-toggle="popover"]').forEach(function(el) {
new bootstrap.Popover(el);
});
}Comparison: CSS Variables vs Sass Variables
| Feature | CSS Variables | Sass Variables |
|---|---|---|
| When they exist | Runtime (in browser) | Build time (in .css file) |
| Can change dynamically? | Yes (via JS) | No (fixed after compile) |
| Browser support | Modern browsers | All (compiled to static values) |
| Theming | Runtime theme switching | Pre-built theme files |
| Performance | Slightly slower (browser resolves) | Faster (pre-computed) |
Common Mistakes
1. Not re-initializing components after AJAX
Dynamically loaded content with data-bs-toggle="tooltip" won’t work unless you call new bootstrap.Tooltip(el) after insertion.
2. Forgetting the -rgb variant when overriding colors
Setting --bs-primary: #e91e63 without --bs-primary-rgb: 233, 30, 99 breaks components that use rgba(var(--bs-primary-rgb), 0.5) for transparency.
3. Using data attributes and JS API on the same component
If you initialize a modal with data-bs-toggle and also with new bootstrap.Modal(), they can conflict. Use one approach.
4. Not escaping Sass variables in custom selectors
// WRONG — Sass tries to interpret $btn-color as a variable
.btn-custom { color: $btn-color; }
// RIGHT — use interpolation
.btn-custom { color: #{$btn-color}; }5. Using standard bootstrap.min.css for RTL layouts
The standard CSS is LTR-only. RTL layouts need bootstrap.rtl.min.css and dir="rtl" on <html>.
Practice Questions
How does
data-bs-theme="dark"work at the component level? Answer: It overrides the CSS variables for that component and its children only. Components outside it use the page’s theme (or default to light).What’s the difference between Bootstrap 5’s CSS variables and Sass variables? Answer: CSS variables exist at runtime and can be changed dynamically via JavaScript. Sass variables are compiled to static values in the .css file and can’t change at runtime.
Why do you override Sass variables BEFORE importing Bootstrap? Answer: Bootstrap uses
!defaulton its variables. Your override (without!default) takes precedence because it’s defined first. If you import first, your override becomes the default and Bootstrap’s value wins.How do you dispose of a tooltip before removing its trigger element from the DOM? Answer: Call
bootstrap.Tooltip.getInstance(element).dispose()to remove event listeners and prevent memory leaks.What does the
slide.bs.carouselevent indicate? Answer: It fires immediately when a slide transition starts (before the new slide is visible). Use it to pause video players or analytics tracking.
Challenge: Create a theme switcher page with 3 theme buttons (Light, Dark, Custom) that change data-bs-theme on the <html>. The “Custom” theme should use a purple color scheme with a dark background. Include cards, buttons, and a table that all respond to theme changes.
FAQ
Try It Yourself
<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Theme Switcher</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Theme Switcher Demo</h1>
<div class="d-flex align-items-center gap-2">
<span class="form-check-label" id="themeLabel">Light</span>
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch" id="darkSwitch" style="cursor:pointer;">
</div>
</div>
</div>
<div class="row g-4">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">Responsive Card</h5>
<p class="card-text">This card responds to theme changes.</p>
<button class="btn btn-primary">Primary Button</button>
<button class="btn btn-outline-secondary">Secondary</button>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card" data-bs-theme="dark">
<div class="card-body">
<h5 class="card-title">Always Dark</h5>
<p class="card-text">This card ignores global theme.</p>
<button class="btn btn-primary">Primary</button>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card" data-bs-theme="light">
<div class="card-body">
<h5 class="card-title">Always Light</h5>
<p class="card-text">This card ignores global theme.</p>
<button class="btn btn-primary">Primary</button>
</div>
</div>
</div>
</div>
<div class="mt-4">
<div class="alert alert-info">
<strong>Demo:</strong> Toggle the switch to change the page theme.
The "Always Dark" and "Always Light" cards ignore the global theme.
</div>
</div>
<div class="table-responsive mt-4">
<table class="table table-striped">
<thead class="table-dark">
<tr><th>Feature</th><th>Value</th></tr>
</thead>
<tbody>
<tr><td>Framework</td><td>Bootstrap 5.3+</td></tr>
<tr><td>Color Mode</td><td id="currentMode">Light</td></tr>
</tbody>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
var html = document.querySelector("html");
var toggle = document.getElementById("darkSwitch");
var label = document.getElementById("themeLabel");
var modeDisplay = document.getElementById("currentMode");
toggle.addEventListener("change", function() {
var isDark = this.checked;
html.setAttribute("data-bs-theme", isDark ? "dark" : "light");
label.textContent = isDark ? "Dark" : "Light";
modeDisplay.textContent = isDark ? "Dark" : "Light";
});
</script>
</body>
</html>This sandbox demonstrates: dark mode toggle, per-component theme override, responsive layout, and JavaScript-driven theme switching.
What’s Next
| Topic | Description |
|---|---|
| Bootstrap Reference | Complete cheatsheet of all classes |
| Sass | Learn the CSS preprocessor used by Bootstrap |
| JavaScript API | Deep dive into JavaScript event handling |
| CSS Custom Properties | Understanding CSS variables in depth |
What’s Next
Congratulations on completing this Bootstrap Advanced tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro