Skip to content
oks-ui

Theming

Dark mode

Every token ships a light and a dark value. Dark mode is a single attribute on the html element — no .dark class per component, no media query in the library.

The mechanism

Set data-theme="dark" (or "light") on <html>. The token stylesheet emits both a base block and a :where(:root[data-theme="dark"]) block, so flipping the attribute re-values every --oks-* property at once.

document.documentElement.setAttribute("data-theme", "dark");

Respecting the system preference

With no attribute set, fall back to the OS setting in your own CSS:

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* your dark overrides, if any */
  }
}

No flash of the wrong theme

Apply the stored / preferred theme before the page paints with a tiny blocking script in <head>. Without this, a user who prefers dark sees a white flash on every load.

<script>
  (function () {
    try {
      var stored = localStorage.getItem("theme");
      var dark = stored === "dark" ||
        (!stored && matchMedia("(prefers-color-scheme: dark)").matches);
      document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
    } catch (e) {}
  })();
</script>

Consumer-defined tokens

A few tokens are deliberately left to you and default to light values: --oks-color-surface and the --oks-form-field-* group. If your app has dark mode, set dark values for these under your dark selector so overlays and form fields match.