# 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.