Qure UI

Dark mode

One attribute. There is no dark variant of any component.

Every token is defined twice — under :root and under [data-theme="dark"]. Components read tokens, so switching the attribute reprints the whole tree.

<html data-theme="dark">

That is the mechanism in full. No dark: utilities in component files, no second set of styles to keep in sync.

With Fumadocs or next-themes

Write both attributes, so Tailwind's dark: variant and the token layer agree:

<RootProvider theme={{ attribute: ['class', 'data-theme'], defaultTheme: 'light' }}>

Tell the browser too

Scrollbars, native controls and the default focus ring are painted by the browser, not by tokens:

html { color-scheme: light; }
html[data-theme='dark'] { color-scheme: dark; }

Without it you get dark panels with a bright white scrollbar down the side.

Theming a subtree

data-theme works anywhere, not only on the root — useful for a viewer that should stay dark whatever the surrounding application is doing:

<div data-theme="dark">
  {/* resolves dark tokens regardless of the page */}
</div>

Anything hardcoding a colour breaks this. A stray #fff is invisible in light mode and glaring in dark — by a wide margin the most common source of dark-mode bugs.

On this page