# Dark mode

AdminKit's light, dark and auto color modes: data-bs-theme on <html>, the stored mode, the toggle markup, adminkit:themechange and charts that recolor.

AdminKit uses [Bootstrap's color modes](https://getbootstrap.com/docs/5.3/customize/color-modes/) in the free version and in AdminKit PRO. One stylesheet, `css/app.css`, holds the light and the dark mode, and the `data-bs-theme` attribute on `<html>` picks one. Switching changes the page in place, without a reload.

## Set the mode

```html
<html lang="en" data-bs-theme="dark">
```

`data-bs-theme="light"` gives the light mode. The attribute also works on a single element, for example a card that stays dark on a light page:

```html
<div class="card mb-0" data-bs-theme="dark">
  <div class="card-body">This card is dark in both modes.</div>
</div>
```

## Light, dark and auto

The visitor's choice is stored in `localStorage` under `adminkit_config_colorMode`, as `light`, `dark` or `auto`. Auto, and no stored value, follow the operating system setting (`prefers-color-scheme`) and change with it while the page is open.

The template pages apply the stored mode with `js/head.js`, a small blocking script in `<head>`, before the stylesheet, so the page is painted in the right mode from the start. It is optional. To use it in your own layout, load it the same way, without `defer` or `type="module"`:

```html
<script src="js/head.js"></script>
<link href="css/app.css" rel="stylesheet">
```

From npm it is `@adminkit/core/head` (`dist/js/head.js`), from the CDN `https://unpkg.com/@adminkit/core/dist/js/head.js`. It also adds the `js-reveal` class to `<html>`, so cards fade in once `js/app.js` runs; if `app.js` has not started within 3 seconds, the cards show without the fade.

Without `head.js` and without a `data-bs-theme` attribute, `js/app.js` sets the attribute itself from the stored mode or the operating system setting when it starts. The only difference is a short flash of the other mode for visitors who picked the mode opposite to their operating system, and the cards show without the fade. The [framework guides](/docs/frameworks/html/) show where `head.js` goes in each framework. To keep a page light whatever the visitor chose, put `data-bs-theme="light"` on `<html>` and leave `head.js` out.

## Toggle

Any element with `data-color-mode-value="light"`, `"dark"` or `"auto"` switches to that mode when clicked and stores it. Buttons get the `active` class and `aria-pressed`, radio buttons get `checked`. Elements with `data-color-mode-icon="<mode>"` are shown only for the current mode, and the element with `data-color-mode-toggle` gets an `aria-label` that names it. The navbar of the template pages uses a dropdown:

```html
<li class="nav-item dropdown">
  <a class="nav-icon dropdown-toggle" href="#" id="colorModeDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Color mode" data-color-mode-toggle>
    <span data-color-mode-icon="light" hidden><i class="align-middle" data-lucide="sun"></i></span>
    <span data-color-mode-icon="dark" hidden><i class="align-middle" data-lucide="moon"></i></span>
    <span data-color-mode-icon="auto"><i class="align-middle" data-lucide="sun-moon"></i></span>
  </a>
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="colorModeDropdown">
    <li>
      <button type="button" class="dropdown-item" data-color-mode-value="light" aria-pressed="false">
        <i class="align-middle me-1" data-lucide="sun"></i> Light
      </button>
    </li>
    <li>
      <button type="button" class="dropdown-item" data-color-mode-value="dark" aria-pressed="false">
        <i class="align-middle me-1" data-lucide="moon"></i> Dark
      </button>
    </li>
    <li>
      <button type="button" class="dropdown-item" data-color-mode-value="auto" aria-pressed="false">
        <i class="align-middle me-1" data-lucide="sun-moon"></i> Auto
      </button>
    </li>
  </ul>
</li>
```

In AdminKit PRO, the settings panel has the same three modes, and demo links can preset one with `?colorMode=light`, `dark` or `auto`.

## Charts and maps

`window.theme` holds the colors of the current mode, read from the CSS custom properties: `primary`, `secondary`, `success`, `info`, `warning`, `danger`, `gray-100` to `gray-900`, `white` (the surface color), `black` (the strongest text color), `body-bg`, `body-color`, `border-color`, and `id` (`"light"` or `"dark"`). When the mode changes, `window.theme` is rebuilt and `document` fires an `adminkit:themechange` event with the new colors as `detail`.

Create charts and maps with the AdminKit helpers so they recolor with the mode. Each takes a function that receives the theme colors and returns the configuration; AdminKit calls it again with the new colors when the mode changes:

```js
adminkit.chartjs("#chartjs-line", function (theme) {
  return {
    type: "line",
    data: {
      labels: ["Jan", "Feb", "Mar"],
      datasets: [{ label: "Sales", borderColor: theme.primary, data: [21, 15, 29] }]
    }
  };
});
```

| Helper | Library | Edition |
| --- | --- | --- |
| `adminkit.chartjs(element, theme => config)` | Chart.js | Free and PRO |
| `adminkit.vectorMap(theme => options)` | jsVectorMap (`options.selector` names the element) | Free and PRO |
| `adminkit.apexcharts(element, theme => options)` | ApexCharts | PRO |

`element` is a selector or an element. The helpers return the chart or map instance. Read colors from the function's `theme` argument, not from a `window.theme` captured outside it, or the chart keeps the colors it was created with. Charts created with `new Chart()` only pick up Chart.js' default text and border colors when the mode changes.

For anything else, listen for the event:

```js
document.addEventListener("adminkit:themechange", function (event) {
  console.log(event.detail.id, event.detail.primary);
});
```

## Your own styles

Colors that follow the mode come from custom properties: Bootstrap's `--bs-*` variables, the gray scale `--bs-gray-100` to `--bs-gray-900` (which turns around in dark mode, so `--bs-gray-200` stays a subtle shade on either background) and `--ak-surface-bg`, the background of cards, dropdowns and inputs. The Sass values of the dark mode are the `$*-dark` variables in `src/scss/1-variables/_dark.scss`. For a rule that only applies in dark mode, use Bootstrap's mixin:

```scss
.my-panel {
  background: var(--ak-surface-bg);

  @include color-mode(dark) {
    box-shadow: none;
  }
}
```

## Coming from AdminKit 3.x

AdminKit PRO 3.x had `css/light.css` and `css/dark.css` and set dark mode with `data-theme="dark"` on `<body>`. Link `css/app.css` instead, add the script from [Light, dark and auto](#light-dark-and-auto) to `<head>`, and use `data-bs-theme="dark"` on `<html>`. `data-theme` on `<body>` now only picks the sidebar colors (`default`, `colored` or `light`). `data-theme="dark"` and `?theme=dark` still give dark mode with the default sidebar, but they are deprecated. Charts created with `new Chart()` or `new ApexCharts()` keep working; switch them to the helpers above to recolor them.

---

Canonical HTML page: https://adminkit.io/docs/getting-started/dark-mode/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/docs/ · Questions: support@adminkit.io
