Skip to content

Docs · Styling

Theming

Light and dark, plus custom colors, radius and font.

Two layers: light and dark, which everyone gets, and custom themes, which change the brand colors, corner radius and font. Both only change CSS variables, so every component follows without code changes.

Light and dark

The theme is set by data-theme on <html>. People choose light, dark or system; the choice is saved in localStorage.

  • ThemeProvider (in providers.tsx) holds the choice.
  • themeScript runs in <head> before paint, so the page never flashes the wrong theme.
  • ThemeToggle is a sun/moon button; ThemeSwitcher offers all three choices.
Reading and changing the theme
"use client"; import { useTheme } from "@/components/ui/theme-provider"; const { theme, resolvedTheme, setTheme } = useTheme();// theme: "light" | "dark" | "system"   resolvedTheme: what's shownsetTheme("dark");// Pass the click point to reveal the new theme in a growing circlesetTheme("dark", { x: event.clientX, y: event.clientY });

Custom themes

Try it on the Theme builder. Each color is one value; every shade (hover, pressed, tint, border, text on top) and the whole dark theme are worked out from it, following the Arise UI scale.

  • Primary, secondary, accent. The editor warns when a color reads below 4.5:1 in either theme.
  • Tint surfaces. Backgrounds, borders and text take a hint of the primary hue.
  • Corner radius. Buttons use the value; cards 1.5×, panels 2×, tags ½.
  • Font. A Google Fonts link, a font-file link, or an upload kept in the browser.

Ship a theme for everyone

Switch the Theme builder to Code to read the CSS as you edit, or press Copy CSS, and paste it at the end of globals.css. It only contains the variables that differ from Arise UI.

Let each person choose

Setup
// src/app/layout.tsx: in <head>, before anything paintsimport { customThemeScript, themeScript } from "@/lib/theme";<script dangerouslySetInnerHTML={{ __html: themeScript + customThemeScript }} /> // src/app/providers.tsx<ThemeProvider>  <CustomThemeProvider>{children}</CustomThemeProvider></ThemeProvider> // A settings pageimport { ThemeCustomizer } from "@/components/ui/theme-customizer";<ThemeCustomizer />
Changing it from code
"use client"; import { useCustomTheme } from "@/components/ui/custom-theme";import { DEFAULT_THEME } from "@/lib/theme"; const { theme, update, set, reset } = useCustomTheme(); update({ primary: "#0b63ce" });          // change some settingsset({ ...DEFAULT_THEME, radius: 12 });   // replace all of themreset();                                 // back to Arise UI

Making a component themeable

  • Use token utilities, never hex colors.
  • Derive inner radii from the tokens, e.g. rounded-[calc(var(--radius-sm)_-_2px)], not a fixed px value.
  • Read live colors with getComputedStyle only when you must (3D does, through useThemeColors).