# Tokens overview

> The @otfdashkit/tokens design system — CSS variables, palettes, and runtime theme switching.

`@otfdashkit/tokens` is the source-of-truth for every visual decision in OTF. It ships as
**CSS custom properties** (consumed by `@otfdashkit/ui`) and as a **JS object** (consumed by
`@otfdashkit/ui-native` via Tamagui). One source, two outputs.

## What's in it

| Token bucket          | Examples                                                                 |
| --------------------- | ------------------------------------------------------------------------ |
| **Color**             | `--background`, `--foreground`, `--primary`, `--card`, `--border`, `--ring`, `--chart-1..5` |
| **Typography**        | `--font-sans`, `--font-heading`, `--font-mono`, `--font-size-{xs..4xl}`, `--font-weight-*`, `--line-height-*`, `--letter-spacing-*` |
| **Border radius**     | `--radius`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`     |
| **Spacing scale**     | Implicit via Tailwind 4 + the OTF preset                                  |
| **Motion**            | Default ease curves + duration tokens                                     |

Every component reads exclusively from these variables. **Never hardcode hex outside the tokens package** — `@otfdashkit/eslint-plugin-otf-design` will flag it.

## Web setup

Import the CSS once in your app's globals, **before** the component CSS:

```css title="app/globals.css"
@import '@otfdashkit/tokens/web.css';
@import '@otfdashkit/ui/styles';
```

That's it. The tokens cascade onto `:root` and every component picks them up.

## Native setup

Import the JS export inside your Tamagui config:

```ts title="tamagui.config.ts"
import { tokens, themes } from '@otfdashkit/tokens/native'
import { createTamagui } from '@otfdashkit/ui-native'

export default createTamagui({
  tokens,
  themes,
})
```

## The four shipped themes

OTF ships four palettes. Each has a light + dark variant.

| Class            | Vibe                          | When to use                                        |
| ---------------- | ----------------------------- | -------------------------------------------------- |
| `theme-slate`    | Cool slate, neutral grays     | Default — works for almost any product              |
| `theme-warm`     | Warm purple, cream backgrounds | Lifestyle / wellness / creator products             |
| `theme-cosmic`   | Magenta-violet on near-black  | Editorial / showcase / portfolio                    |
| `theme-terminal` | Pure mono — black on green    | Developer tools / terminal-shaped apps              |

The fitness-kit defaults to its **own brand override** (warm orange) which is scoped to
`:root:not([class*="theme-"])` — i.e. it's the fallback when no `theme-*` class is set.
Switching to any of the four shipped themes overrides the orange brand.

## Switching themes at runtime

Set the class on `<html>`:

```ts
document.documentElement.className = 'theme-warm dark'
```

For zero-flicker first paint, do this **before React hydrates**:

```html title="app/layout.tsx"
<script dangerouslySetInnerHTML={{ __html: `
  const t = localStorage.getItem('otf-theme') || 'theme-slate'
  const s = localStorage.getItem('otf-color-scheme') || 'dark'
  document.documentElement.className = \`\${t} \${s}\`
` }} />
```

The kits use `<FloatingThemePicker />` (rendered globally) and `<ThemeSwitch />` inside Settings
to persist + apply the choice.

## Authoring a custom palette

Copy any of the four shipped palette blocks from
`packages/tokens/src/web.css` and rename:

```css
.theme-mybrand {
  --background:        0 0% 100%;
  --foreground:        220 8% 12%;
  --primary:           212 100% 47%;
  --primary-foreground: 0 0% 100%;
  --card:              0 0% 100%;
  --card-foreground:   220 8% 12%;
  --border:            220 8% 92%;
  --ring:              212 100% 47%;
  --chart-1:           212 100% 47%;
  --chart-2:           28 100% 56%;
  --chart-3:           160 70% 40%;
  --chart-4:           340 80% 56%;
  --chart-5:           260 60% 56%;
}

.theme-mybrand.dark,
.theme-mybrand .dark {
  --background:        220 12% 7%;
  --foreground:        0 0% 96%;
  --primary:           212 100% 60%;
  /* … */
}
```

Drop the file into your app's CSS, set
`document.documentElement.className = 'theme-mybrand dark'`, and every OTF component
re-paints to the new palette.

## Tailwind preset

For Tailwind 4 projects, import the OTF preset to get token-aware utility classes
(`bg-background`, `text-foreground`, `border-border`, `bg-card`, `bg-primary`, …):

```ts title="tailwind.config.ts"
import otfPreset from '@otfdashkit/tokens/tailwind-preset'

export default {
  presets: [otfPreset],
  content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
}
```

The kits use these utility classes exclusively — never inline `style={{ background: '#...' }}`.

## Charts

Charts always use chart tokens:

```tsx
<BarChart
  data={data}
  dataKey={['issues', 'completed']}
  colors={['hsl(var(--chart-1))', 'hsl(var(--chart-3))']}
  stacked
/>
```

Each palette ships a chart-1 through chart-5. They're tuned for legibility against the
palette's background, both light and dark.
