# Theme > System/light/dark theming: provider, no-flash script, and toggle. Import from `@aspyn-io/kit`. Scaffold source: `npx shadcn@latest add https://design.aspyn.io/r/theme.json` ## Install ```tsx import { ThemeProvider, ThemeScript, ThemeToggle, useTheme } from "@aspyn-io/kit"; ``` ### Setup (Next.js) ThemeProvider manages both axes — theme (light/dark/system) and brand (the visual identity) — with persistence. ThemeScript prevents a flash of the wrong theme or brand. Developers write zero theme code. ```tsx // app/layout.tsx import { ThemeProvider, ThemeScript } from "@aspyn-io/kit"; export default function RootLayout({ children }) { return ( {children} ); } ``` ### Two axes: theme (mode) and brand theme is light/dark/system; brand is the palette (graphite, ember, …). Both come from one hook, both persist, both restore before paint. ```tsx const { theme, resolvedTheme, setTheme, // light/dark/system mode brand, setBrand, brands, // visual identity } = useTheme(); setTheme("dark"); // mode setBrand("ember"); // brand resolvedTheme; // "light" | "dark": what's applied ``` ### Bring your own theme A brand is just a `[data-brand]` CSS block overriding the --as-* tokens, then registered with the provider so it shows up in switchers. ```tsx /* app/globals.css — override only the tokens you want */ [data-brand="acme"] { --as-primary: oklch(0.55 0.2 265); --as-ring: oklch(0.55 0.2 265); --as-radius: 0.5rem; } .dark[data-brand="acme"], .dark [data-brand="acme"] { --as-primary: oklch(0.72 0.16 265); } // register it so the switcher offers it {children} // or set it imperatively anywhere const { setBrand } = useTheme(); setBrand("acme"); ``` ### Setup (Vite) ```tsx // index.html: inline the no-flash script // main.tsx import { ThemeProvider } from "@aspyn-io/kit"; createRoot(document.getElementById("root")!).render( ); ``` ## Notes - theme defaults to system (follows the OS live) and persists under "aspyn-theme"; brand defaults to aspyn and persists under "aspyn-brand". Both keys are configurable (storageKey / brandStorageKey). - First-party brands: graphite (default), ember, ocean, slate, violet, rose. Plus two corporate-brand showcases — aptive, google — real identities rebuilt on the token system. Each is `@import "@aspyn-io/tokens/themes/"`. Custom brands need no import beyond your own CSS block. - The default graphite brand is the bare :root, so it carries no data-brand attribute; every other brand sets data-brand on . - This very page is powered by these components: try the theme toggle and brand switcher in the header.