# Theming & brands

A **brand** is one typed token object (colors, fonts, a chart palette), and the whole deck (slides, code, plugins) derives its look from it. Nothing is hardcoded in slides: a slide uses `text-primary`, not `text-blue-500`, so it's right under any brand.

## Use a built-in brand

The theme styles ship a few brands (`liebstoeckel`, `nocturne`, `acme`, `sunset`). Import the styles once in your entry and pick one by name:

```tsx title="main.tsx"
import "@liebstoeckel/theme/styles.css";
import { Present } from "@liebstoeckel/engine";

<Present brands={["nocturne"]} slides={[…]} />
```

## Define your own brand

Author it with `defineTheme` and hand it to `<Present>` via `brandThemes`. The engine injects it as a `[data-brand]` stylesheet, so you ship a brand **without touching the theme package**:

```tsx title="main.tsx"
import "@liebstoeckel/theme/styles.css";
import { Present } from "@liebstoeckel/engine";
import { defineTheme } from "@liebstoeckel/theme";

const acme = defineTheme({
  name: "acme",
  colors: {
    bg: "#0b0e14", surface: "#141925", border: "#222a3a",
    text: "#e6eaf2", muted: "#8a93a6",
    primary: "#3b82f6",   // brand
    accent: "#22d3ee",    // accent
    accent2: "#f0abfc",   // optional secondary accent
    onPrimary: "#ffffff", // text/icons on a primary fill
  },
  fonts: {
    heading: '"Inter", system-ui, sans-serif',
    body: '"Inter", system-ui, sans-serif',
    mono: '"JetBrains Mono", ui-monospace, monospace',
  },
  viz: ["#3b82f6", "#22d3ee", "#f0abfc", "#a3e635", "#fbbf24"], // optional chart palette
  glow: { a: "#10233f", b: "#0b2530" },                         // optional atmosphere gradient
});

<Present brands={["acme"]} brandThemes={[acme]} slides={[…]} />
```

That's it: `brands` names the active brand, `brandThemes` supplies its tokens. (Bring fonts via `@font-face` / a `<link>` in your `index.html`.)
**Semantic, not literal:** Name colors by role (`primary`, `accent`, `surface`), never by hue. A slide written once looks right under every brand you give it.

## Available tokens

| CSS variable | Tailwind | Meaning | Required |
| --- | --- | --- | --- |
| `--brand-bg` / `--brand-surface` | `bg-bg` / `bg-surface` | page + panel backgrounds | ✓ |
| `--brand-text` / `--brand-muted` | `text-text` / `text-muted` | primary + secondary text | ✓ |
| `--brand-primary` / `--brand-accent` | `text-primary` / `text-accent` | brand + accent | ✓ |
| `--brand-on-primary` | `text-on-primary` | content on a primary fill | ✓ |
| `--brand-font-heading/body/mono` | `font-heading/body/mono` | typefaces | ✓ |
| `--brand-border` | `border-border` | hairlines (falls back to a tint of text) | optional |
| `--brand-accent2` | `text-accent2` | secondary accent | optional |
| `--brand-viz-0…n` | (none) | chart-series palette (`theme.viz`) | optional |
| `--brand-glow-a/b` | (none) | atmosphere gradient stops (`theme.glow`) | optional |

## How it works (the bridge)

1. A brand is a `[data-brand="name"] { --brand-*: … }` block. Built-ins live in the theme styles; your `brandThemes` are injected as the same kind of block.

2. `@theme inline` in the theme CSS maps Tailwind utilities onto those variables, so `text-primary` resolves to `var(--brand-primary)` **at the point of use**, under whichever `data-brand` is active.

3. The deck sets `document.body.dataset.brand`, so the whole deck re-skins instantly, no rebuild.

**Code and plugins inherit automatically:** Shiki highlights against the brand tokens (see [Animated code](https://docs.liebstoeckel.app/guides/animated-code/)), and plugins style against `var(--brand-*)` (and read `theme.viz` for charts), so polls, Q&A, and reactions are on-brand for free.
**Multiple brands:** Pass several, e.g. `brands={["acme", "sunset"]}` (+ their `brandThemes`), and press <kbd>t</kbd> on the keyboard to cycle them. It's a desktop preview/showcase aid (handy while designing, or to show one deck in two skins); it isn't surfaced in the touch UI.

## Contributing a shared brand

To make a brand available to *every* deck by name (instead of per-deck `brandThemes`), add it to the theme package: drop a `defineTheme(...)` file in `packages/theme/src/brands/`, export it from `brands/index.ts`, and run `bun run gen` to regenerate `brands.generated.css`.

[Build & deploy](https://docs.liebstoeckel.app/guides/building/)