Theming & Dark Mode
AdminLTE 4 uses Bootstrap 5.3's native color modes: the theme is the data-bs-theme attribute on <html>. A light / dark / auto toggle persists the choice to localStorage, and a small no-flash script applies it before first paint. Deeper theming is done with CSS custom properties or SCSS variables.
How dark mode works
Bootstrap 5.3 recolours every component based on data-bs-theme="light" or data-bs-theme="dark" on the root element. AdminLTE simply manages that attribute. There are three pieces:
- A no-flash init script in
<head>that sets the attribute synchronously before the page renders. - A toggle in the topbar with Light / Dark / Auto options.
- A controller script at the end of
<body>that wires the toggle and persists the choice.
The chosen theme is stored under the localStorage key lte-theme (values light, dark, or auto).
No-flash init (head)
Place this inline script as the first thing in <head>. Running synchronously, it prevents a flash of the wrong theme on load. Explicit dark/light win; otherwise it falls back to the OS preference:
<script>
(() => {
'use strict';
const STORAGE_KEY = 'lte-theme';
let stored = null;
try { stored = localStorage.getItem(STORAGE_KEY); } catch {}
const prefersDark = globalThis.matchMedia('(prefers-color-scheme: dark)').matches;
let resolved = 'light';
if (stored === 'dark' || stored === 'light') {
resolved = stored;
} else if (prefersDark) {
resolved = 'dark';
}
document.documentElement.setAttribute('data-bs-theme', resolved);
document.documentElement.style.colorScheme = resolved;
})();
</script>
The toggle
A Bootstrap dropdown whose options carry data-bs-theme-value. The controller reads that value, applies it, and stores it:
<li class="nav-item dropdown">
<button class="btn btn-link nav-link dropdown-toggle" id="bd-theme"
type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="theme-icon-active"><i class="my-1"></i></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><button type="button" class="dropdown-item" data-bs-theme-value="light">
<i class="bi bi-sun-fill me-2"></i>Light
<i class="bi bi-check-lg ms-auto d-none"></i></button></li>
<li><button type="button" class="dropdown-item" data-bs-theme-value="dark">
<i class="bi bi-moon-fill me-2"></i>Dark
<i class="bi bi-check-lg ms-auto d-none"></i></button></li>
<li><button type="button" class="dropdown-item" data-bs-theme-value="auto">
<i class="bi bi-circle-half me-2"></i>Auto
<i class="bi bi-check-lg ms-auto d-none"></i></button></li>
</ul>
</li>
The controller (end of body)
The controller applies the preferred theme, resolves auto against the OS setting, persists clicks to lte-theme, and re-resolves auto when the OS preference changes:
(() => {
'use strict';
const STORAGE_KEY = 'lte-theme';
const prefersDark = () => matchMedia('(prefers-color-scheme: dark)').matches;
const getPreferredTheme = () => localStorage.getItem(STORAGE_KEY)
|| (prefersDark() ? 'dark' : 'light');
const setTheme = (theme) => {
const resolved = theme === 'auto' ? (prefersDark() ? 'dark' : 'light') : theme;
document.documentElement.setAttribute('data-bs-theme', resolved);
};
setTheme(getPreferredTheme());
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const stored = localStorage.getItem(STORAGE_KEY);
if (!stored || stored === 'auto') setTheme(getPreferredTheme());
});
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('[data-bs-theme-value]').forEach((toggle) => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value');
localStorage.setItem(STORAGE_KEY, theme);
setTheme(theme);
});
});
});
})();
The demo applies data-bs-theme="dark" directly on .app-sidebar to get the dark-sidebar-on-light-page look — data-bs-theme can be scoped to any element, not just <html>. Drop it for a sidebar that follows the page theme.
SCSS / CSS theming
For quick retheming with no build step, override Bootstrap's CSS custom properties in your own stylesheet:
:root, [data-bs-theme="light"] {
--bs-primary: #6610f2;
--bs-primary-rgb: 102, 16, 242;
--bs-body-bg: #f3f4f6;
}
[data-bs-theme="dark"] {
--bs-body-bg: #14171c;
--bs-body-color: #e9ecef;
}
Most of AdminLTE's chrome is built on var(--bs-primary) and friends, so it picks these up automatically. For structural changes (sidebar width, breakpoints, spacing scale) that CSS variables don't cover, override SCSS variables and recompile — see Configuration.