Theming

The tokens every component is styled with, and how dark mode switches them.

The theme sets shadcn's standard color variables, so other shadcn components match. It lives in your CSS, and changing a token there changes every component that uses it.

Dark mode

Every token has a dark value, used inside any element with the dark class, where Tailwind's dark: variant applies too. The theme also sets color-scheme: dark there, so scrollbars and native form controls follow.

Put the class on <html> to switch the whole page. It has to be there before the page first paints, or a dark page flashes light as it loads, so a script at the top of <head> sets it from the reader's stored choice, or from the system setting until they make one. A provider then stores each new choice and, while it's System, follows the system setting as it changes.

Below are setups for Next.js and Vite. For other frameworks, such as Astro, Remix and TanStack Start, follow shadcn's dark mode guides, which set the same class.

Next.js

next-themes does both. Install it.

Wrap the root layout in its ThemeProvider, and add suppressHydrationWarning to <html>, since its script changes the class before React hydrates.

app/layout.tsx

disableTransitionOnChange turns transitions off for the moment the theme switches, so controls change color at once rather than fading one by one.

Vite

Add the script to <head> in index.html, before anything that renders. It falls back to the system setting when nothing valid is stored, and leaves the page alone if storage is blocked.

index.html

Then add a provider, and wrap the app in it.

components/theme-provider.tsx

It stores the choice under theme, the key next-themes uses too.

Toggle

A Dropdown Menu with a radio item for each choice lets readers pick one, and shows which is set. The trigger's icon follows the page with the dark: variant, so it's right before the provider knows the choice. Import useTheme from next-themes in Next.js, or from @/components/theme-provider with the Vite provider. Both return the theme and setTheme it uses.

components/mode-toggle.tsx

Tokens

Alongside shadcn's variables, the theme adds these.

Colors

Every color token in light and dark mode. Values such as grey-50 are steps of the palette. Hover a swatch to see the color it resolves to.

Surfaces

Text and icons

Borders

Controls

Overlays

Overlays are translucent. primary-hover and primary-pressed are shown over primary.

States

Syntax

Colors for code, such as the keys and values in JSON Viewer. They're GitHub Light's and GitHub Dark's, as in the code blocks on this site.

Charts

Palette

These are the same in both modes. Each step is a variable, such as --palette-blue-600.

Type scale

text-base stays at 16px, so inputs can use text-base md:text-sm to keep iOS from zooming in.