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-position only does something on a page that renders both navigations - a vertical aside.navbar-vertical and 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-layout is the attribute form of the .layout-fluid and .layout-boxed body 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-theme colors whichever navigation the page shows, since the vertical sidebar is a navbar too. default means "follow the page color mode", not "always light". dark gives it the dark palette on a light page - the same result as putting data-bs-theme="dark" on the element in your markup. primary paints 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-hover starts folded too, but unfolds when the user hovers over it or moves keyboard focus into it. See Folded sidebar for the markup and the tabler:sidebar-folded event.

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:

  1. URL query parameter - add ?theme=dark (or any other key) to the page URL. The script reads it, applies it, and saves it to localStorage so it persists on the next visit.
  2. localStorage - each key is stored under tabler-<key>, for example tabler-theme or tabler-theme-primary. This is what makes the choice persist across page loads once it has been set once, by either method here.
  3. 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.astro is 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 theme straight from the query string, a plain link is enough to switch modes - no JavaScript of your own required. .hide-theme-dark and .hide-theme-light are used to show only the relevant link for the current mode.

  • shared/components/demo/ThemeSettings.astro is 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 in shared/layouts/BaseLayout.astro that, on every change event:

    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 sets theme and navbar-theme together, 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.

On this page

130 sleek illustrations for your startup's visual identity.