TRA SummitDesign System

Theming

Class-based dark mode with a no-flash inline script, system preference support, and a ready-made toggle.

Try it

Current theme

Preference: system · Resolved: light

Setup

Wrap your app once. ThemeScript applies the stored theme before first paint so there is no flash of the wrong mode.

// app/layout.tsx
import { ThemeProvider, ThemeScript } from '@tra/ui';

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeScript />
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

Reading and setting the theme

'use client';
import { useTheme, ThemeToggle } from '@tra/ui';

function Settings() {
  const { theme, resolvedTheme, setTheme } = useTheme();
  return (
    <>
      <ThemeToggle />              {/* or roll your own: */}
      <button onClick={() => setTheme('dark')}>Dark</button>
      <button onClick={() => setTheme('system')}>Match system</button>
    </>
  );
}

How it works

  • Dark mode is the .dark class on <html> — every semantic token swaps automatically.
  • The preference persists in localStorage under tra-theme.
  • system tracks the OS and updates live when the OS preference changes.
  • Components built with semantic tokens (bg-canvas, text-ink, border-line) never need dark: overrides.