Color modes
The theme script switches between light, dark and auto color modes, plus the base gray, font, primary color, corner radius and the page layout, without a page reload.
Overview
Tabler includes a small script, tabler-theme.js, that controls the settings below. Each setting is read from a URL query parameter or localStorage, then applied as a data-bs-* attribute on <html>. Tabler's CSS reads these attributes to switch styles instantly, with no page reload.
| Setting | Attribute | Default | Allowed values |
|---|---|---|---|
| Color mode | data-bs-theme |
auto |
light, dark, auto |
| Base gray shade | data-bs-theme-base |
gray |
slate, gray, zinc, neutral, stone |
| Font family | data-bs-theme-font |
sans-serif |
sans-serif, serif, monospace, comic |
| Primary color | data-bs-theme-primary |
blue |
blue, azure, indigo, purple, pink, red, orange, yellow, lime, green, teal, cyan, inverted |
| Corner radius | data-bs-theme-radius |
1 |
0, 0.5, 1, 1.5, 2 |
| Navigation position | data-bs-navbar-position |
horizontal |
horizontal, vertical |
| Container width | data-bs-layout |
default |
default, fluid, boxed |
| Navbar behavior | data-bs-navbar |
default |
default, sticky |
| Navigation theme | data-bs-navbar-theme |
default |
default, dark, primary |
| Sidebar | data-bs-sidebar |
default |
default, folded, folded-hover |
The data-bs-theme-base colors are the gray palettes that back every other color mode. See Theme base colors for every shade of every palette; this page only covers the script that switches between them.
The four data-bs-theme-font values are also available as utilities - .font-sans-serif, .font-serif, .font-monospace and .font-comic - to set a font family on a single element regardless of the page-wide setting.
Each setting only appears on <html> when it differs from its default. For example, data-bs-theme-primary="blue" is never written because blue is already the default - the browser falls back to Tabler's built-in styles instead.
Layout settings
The last five settings change the page layout rather than its colors, and they need the markup described in Page layouts:
data-bs-navbar-positiononly does something on a page that renders both navigations - a verticalaside.navbar-verticaland a horizontal navbar, both direct children of.page. The CSS shows one and hides the other. A page with a single navbar keeps it, whatever the stored value says, so a stale choice can never leave a page with no navigation.data-bs-layoutis the attribute form of the.layout-fluidand.layout-boxedbody classes. Use one route or the other, not both.data-bs-navbar="sticky"makes the top navbar stick to the top of the viewport while the page scrolls. On the two-row navbar only the top row sticks.data-bs-navbar-themecolors whichever navigation the page shows, since the vertical sidebar is a navbar too.defaultmeans "follow the page color mode", not "always light".darkgives it the dark palette on a light page - the same result as puttingdata-bs-theme="dark"on the element in your markup.primarypaints it in the primary color with the dark text palette.data-bs-sidebar="folded"folds every vertical navbar on the page into a narrow, icon-only rail.folded-hoverstarts folded too, but unfolds when the user hovers over it or moves keyboard focus into it. See Folded sidebar for the markup and thetabler:sidebar-foldedevent.
Setup
Load the theme script right after the opening <body> tag, and do not add defer or async to it:
<body>
<script src="https://cdn.jsdelivr.net/npm/@tabler/[email protected]/dist/js/tabler-theme.min.js"></script>
...
</body>
The script has to run and set data-bs-theme on <html> before the browser paints anything. If you defer it, or place it in <head>, the page paints with the light theme first and then flips to dark a moment later - a visible flash of the wrong theme (FOUC). Loading it inline, first thing in <body>, is what prevents that flash.
This is separate from Tabler's main tabler.min.js bundle, which still loads with defer near the end of <body> as usual.
Color mode: light, dark, and auto
auto is the default: with no stored choice, Tabler follows the visitor's operating system setting. Set theme to light or dark to force a mode instead:
<!-- Force dark mode -->
<html data-bs-theme="dark">
<!-- Follow the OS setting -->
<html data-bs-theme="auto">
With auto, the script checks the prefers-color-scheme media query once on load, and resolves it to light or dark right away - data-bs-theme is set to the resolved value, never to the literal string auto. It also keeps listening for OS-level changes: if the visitor switches their system between light and dark while your page is open, the script updates data-bs-theme on the fly, with no reload needed.
Setting values
You can set any of these keys in three ways, and they all agree with each other:
- URL query parameter - add
?theme=dark(or any other key) to the page URL. The script reads it, applies it, and saves it tolocalStorageso it persists on the next visit. localStorage- each key is stored undertabler-<key>, for exampletabler-themeortabler-theme-primary. This is what makes the choice persist across page loads once it has been set once, by either method here.- Server-rendered attribute - if you already know the visitor's preference (from a cookie or account setting), render the
data-bs-*attributes on<html>yourself. The script keeps them as the starting value and only replaces them with a stored choice or a URL parameter.
A query parameter always wins over what's already stored, and updates the stored value for next time:
https://example.com/?theme=dark&theme-primary=azure&navbar-position=vertical&layout=boxed
Building your own theme switcher
Tabler's own demo pages ship two working examples you can copy:
-
shared/components/navbar/NavbarSideTheme.astrois a simple light/dark toggle in the navbar. It's just two links:<a href="?theme=dark">Enable dark mode</a> <a href="?theme=light">Enable light mode</a>Since the theme script reads
themestraight from the query string, a plain link is enough to switch modes - no JavaScript of your own required..hide-theme-darkand.hide-theme-lightare used to show only the relevant link for the current mode. -
shared/components/demo/ThemeSettings.astrois a full settings panel, opened from a floating button, with radios and color swatches for every key. It only builds the form markup - the behavior lives in a page-level script inshared/layouts/BaseLayout.astrothat, on everychangeevent:document.documentElement.setAttribute('data-bs-' + key, value) window.localStorage.setItem('tabler-' + key, value) url.searchParams.set(key, value) window.history.pushState({}, '', url)It sets the attribute immediately, saves it to
localStorage, and pushes it into the URL without a reload, so a shared link reproduces the same look. Its "Color scheme" tiles are presets: one tile setsthemeandnavbar-themetogether, and reads as selected only while both keys hold the tile's values. The script knows nothing about presets - they are a convenience of the panel, built on the keys above. Use this as a starting point for your own settings UI - it's meant to be copied and adjusted, not used as-is in production.
Resetting the choice
To reset a key back to its default, remove its localStorage entry and its data-bs-* attribute:
document.documentElement.removeAttribute('data-bs-theme')
window.localStorage.removeItem('tabler-theme')
ThemeSettings.astro's "Reset changes" button does this for every key at once, and also strips them from the URL's query string.
Accessibility
auto mode respects the visitor's OS-level prefers-color-scheme setting instead of forcing a choice on them, which is the accessible default when you don't have a stronger reason to pick one. If you do force light or dark, still offer a visible way to switch modes rather than only reading the OS preference once - some visitors change their preference based on time of day or lighting conditions.
Switching theme-primary, theme-base, or theme-radius can change color contrast across the page. Re-check contrast for any custom combination you offer, since some primary colors don't clear WCAG AA contrast with white text at their default shade.
