# Tokens — single source of truth

This folder is **the canonical source** for the Urbino Design System. Everything else in the project (component CSS, preview HTML, Markdown specs) is reference material that consumes these tokens.

```
tokens/
  README.md             ← you are here
  primitive.css         ← Layer 1 · raw scales
  semantic.css          ← Layer 2 · component intents (+ legacy --mgr-* / --cit-* aliases)
  world-cit.css         ← Layer 3 · citizen-world overrides
  typography.css        ← font setup + per-world type presets

  tailwind.preset.cjs   ← export · Tailwind v4 / v3 — Angular (manager + citizen web)
  aura-preset.ts        ← export · PrimeNG v21 Aura full override — Angular (manager)
  urbino_theme.dart     ← export · Flutter ThemeData + ThemeExtension — citizen mobile
```

Convenience entry point at the repo root: `colors_and_type.css` `@import`s the four CSS files in the correct order.

---

## The architecture in one picture

```
┌──────────────────────────────────────────────────────────────────────┐
│  LAYER 1 — primitive.css                                             │
│                                                                       │
│    Raw scales. World-agnostic.                                       │
│    --u-blue-500, --u-slate-150,                                       │
│    --u-space-1..16, --u-radius-xs..pill, --u-shadow-xs..pop,         │
│    --u-status-folyamatban-bg, --u-font-display, --u-ease-out         │
│                                                                       │
│    Components NEVER reference Layer 1 directly.                      │
└──────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────────────┐
│  LAYER 2 — semantic.css                                              │
│                                                                       │
│    Component-intent tokens. Manager-world defaults at :root.         │
│    --u-bg, --u-surface, --u-fg-1, --u-primary, --u-focus-ring,       │
│    --u-card-radius, --u-card-padding,                                 │
│    --u-btn-h-md, --u-btn-radius-md, --u-btn-px-md,                   │
│    --u-input-h-md, --u-input-radius-md,                               │
│    --u-text-base, --u-h1-size, --u-tap-min, --u-page-px              │
│                                                                       │
│    This is the contract every implementation honours.                │
└──────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────────────┐
│  LAYER 3 — world-cit.css                                             │
│                                                                       │
│    .urbino-cit { ... } — citizen overrides.                          │
│    ONLY the values that differ from the manager default.             │
│    Pill buttons, 56px inputs, larger radii, 44px tap floor.          │
│                                                                       │
│    Add .urbino-cit-web for the public-spa variant.                   │
└──────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌──────────────────────────────────────────────────────────────────────┐
│  CONSUMERS                                                            │
│                                                                       │
│    Angular + PrimeNG  →  aura-preset.ts                              │
│    Angular + Tailwind →  tailwind.preset.cjs                         │
│    Flutter            →  urbino_theme.dart                           │
│    Reference CSS      →  components/*.css                            │
│                                                                       │
│    Every consumer maps the SAME semantic tokens onto its platform.   │
└──────────────────────────────────────────────────────────────────────┘
```

**Layer 1 is the vocabulary. Layer 2 is the contract. Layer 3 is the dialect.** A new design decision lands in Layer 2 (rename a token, change a default) or Layer 3 (override for one world). Components touched: zero, because they consume Layer 2 only.

---

## What lives where

### Layer 1 — primitives (`primitive.css`)

Raw, world-agnostic values. Never carry intent.

- Brand: `--u-blue-50..950`
- Neutrals: `--u-slate-0..1000` (cool slate scale)
- Accents: `--u-green-*`, `--u-amber-*`, `--u-red-*`, `--u-violet-*`, `--u-teal-*`
- Status: `--u-status-{uj|jovahagyva|folyamatban|megoldva|elutasitva}-{bg|fg|dot}`
- Spacing: `--u-space-1..16` (4 / 8 / 12 / 16 / 20 / 24 / 28 / 32 / 40 / 48 / 64)
- Radii: `--u-radius-{xs|sm|md|lg|xl|2xl|3xl|pill}` (6 / 8 / 10 / 14 / 18 / 22 / 28 / 999)
- Shadows: `--u-shadow-{xs|sm|md|lg|pop}`
- Type: `--u-font-{display|sans|mono}`, `--u-leading-*`, `--u-tracking-*`
- Motion: `--u-ease-*`, `--u-dur-*`
- Z-index: `--u-z-{base|sticky|fixed|dropdown|popover|drawer|modal|toast|tooltip}` (0 / 10 / 20 / 50 / 60 / 70 / 80 / 90 / 100)
- Border widths: `--u-border-w-{hairline|thin|thick}` (1 / 1.5 / 2 px)
- Breakpoints: `--u-bp-{sm|md|lg|xl|2xl}` (640 / 768 / 1024 / 1280 / 1536 px) — *documentation-only; CSS media queries can't use `var()`*

### Layer 2 — semantic (`semantic.css`)

Tokens named by **role**. These are what components read.

- **Surfaces:** `--u-bg`, `--u-surface`, `--u-surface-alt`, `--u-surface-sunk`
- **Borders:** `--u-border`, `--u-border-strong`, `--u-divider`
- **Foregrounds:** `--u-fg-1` (text) · `--u-fg-2` (secondary) · `--u-fg-3` (meta) · `--u-fg-4` (muted) · `--u-fg-inv`
- **Brand:** `--u-primary`, `--u-primary-hover`, `--u-primary-press`, `--u-primary-soft`
- **Focus:** `--u-focus-ring`, `--u-focus-ring-danger`
- **Card:** `--u-card-{bg|radius|border|shadow|padding|padding-sm|padding-lg|padding-xl|header-px|header-py|footer-px|footer-py}`
- **Button:** `--u-btn-{h-sm|h-md|h-lg|radius-sm|radius-md|radius-lg|px-sm|px-md|px-lg|gap|font-size-sm|font-size-md|font-size-lg}`
- **Input:** `--u-input-{h-sm|h-md|h-lg|radius-sm|radius-md|radius-lg|px-sm|px-md|px-lg|font-size-sm|font-size-md|font-size-lg}`
- **Chip:** `--u-chip-{radius-pill|radius-square|py-sm|py-md|py-lg|px-sm|px-md|px-lg}`
- **Tap floor:** `--u-tap-min`
- **Type:** `--u-text-{xs|sm|base|md|lg|xl|2xl|3xl}`, `--u-h1-size`..`--u-h4-size`
- **Layout:** `--u-page-px`, `--u-page-py`, `--u-page-max-w`, `--u-sidebar-w`

This file ALSO carries the **legacy aliases** (`--mgr-space-4`, `--cit-radius-xl`, …) that map back to the new semantic tokens. These exist for back-compat while components migrate; new code uses `--u-*` exclusively.

### Layer 3 — world overrides (`world-cit.css`)

Only what differs. The full diff between manager (default) and citizen:

| Token | Manager (default) | Citizen override |
|---|---|---|
| `--u-card-radius` | 10 | 22 |
| `--u-card-border` | `1px solid …` | `0` |
| `--u-card-padding` | 16 | 20 |
| `--u-btn-h-md` | 36 | 48 |
| `--u-btn-radius-md` | 8 | pill |
| `--u-input-h-md` | 36 | 56 |
| `--u-input-radius-md` | 8 | 14 |
| `--u-tap-min` | 28 | 44 |
| `--u-text-base` | 14 | 16 |
| `--u-page-px` | 24 | 16 (mobile) / 32 (cit-web) |

Everything not listed inherits the manager default.

### Typography (`typography.css`)

Webfont declarations + the `.urbino-mgr` / `.urbino-cit` typography preset rules. These reference Layer 2 tokens (`--u-h1-size`, `--u-text-base`, `--u-font-display`) so they pick up world overrides automatically.

---

## How each consumer maps the tokens

### PrimeNG Aura preset — manager (Angular)

`aura-preset.ts` is a **full override** of Aura targeting **PrimeNG v21+**. It reads the semantic Layer 2 values (manually kept in sync with `semantic.css`) and maps them to Aura's component-level token paths (`button.root.borderRadius`, `datatable.row.hoverBackground`, …).

> **PrimeNG v21 — import paths changed.** The theming utilities and built-in presets were extracted to the framework-agnostic **`@primeuix/themes`** package. `aura-preset.ts` imports from `@primeuix/themes` and `@primeuix/themes/aura`; the old `@primeng/themes/*` paths are gone. Add `@primeuix/themes` to your runtime deps; remove `@primeng/themes` if upgrading.

> **Structural tokens go under `root`.** For components with variant slots (button, togglebutton, …), Aura reads size/spacing/radius from the `root` slot. `definePreset`'s deep-merge will **not** lift flat tokens (e.g. `button.paddingY`) onto `button.root.paddingY` — they're silently dropped. Components without variant slots (inputtext, select, datepicker, …) accept flat tokens fine.

> **`formField.paddingY` must be non-zero.** The earlier `'0'` value collapsed the intrinsic height of every PrimeNG control. The working scale is `0.5rem` default / `0.375rem` sm / `0.625rem` lg (preserves the 32 / 36 / 40 px form-field targets at a 14 px line-height in a metrics-neutral font). **Plus Jakarta Sans caveat:** the manager display face has a larger intrinsic em-box; pilot implementation found it needs `paddingY: '0.375rem'` (sm `0.25rem` / lg `0.5rem`) to land the same target heights. Verify by stacking `<p-button>`, `<p-input>`, `<p-select>` in a row — they should share the same outer height.

> **Do NOT enable `cssLayer` in the bootstrap.** The earlier suggested `cssLayer: { name: 'primeng', order: 'tailwind-base, primeng, tailwind-utilities' }` option **breaks PrimeNG datatable internals** in practice: checkbox columns disappear and filter-popup buttons lose their styling. Tailwind utilities still override component styles without it because PrimeNG ships higher-specificity scoped selectors. Leave `cssLayer` out for the pilot.

```ts
import { providePrimeNG } from 'primeng/config';
import { UrbinoAura } from './tokens/aura-preset';

providePrimeNG({
  theme: {
    preset: UrbinoAura,
    options: {
      prefix: 'p',
      darkModeSelector: false,
      // cssLayer: intentionally omitted — see warning above.
    },
  },
})
```

`cssLayer` (when enabled in other PrimeNG projects) controls the cascade order between Tailwind base, PrimeNG, and Tailwind utilities. For Urbino's pilot the default cascade works.

### Tailwind preset — Angular (manager + citizen web)

`tailwind.preset.cjs` exposes the primitive scales (`--u-blue-*`, `--u-space-*`, etc.) as Tailwind theme tokens so utility classes (`bg-u-blue-500`, `p-4`, `rounded-md`) match the system. Two variants:

- **Variant A** — Tailwind v4 `@theme` block, pasted into `app.css`.
- **Variant B** — JS preset for legacy Tailwind v3 during migration.

### Flutter — citizen mobile

`urbino_theme.dart` defines `ThemeData` + a `UrbinoTokens` `ThemeExtension`. Materials defaults take `colorScheme.primary` from `--u-primary`; the `ThemeExtension` carries the rest (spacing, radius, status palette) for widget code.

### Reference CSS — `components/*.css`

The reference implementation lives in `../components/`. These files are the **vizuális truth** — when the Angular team builds a `<p-card>` Aura override or the Flutter team builds `UrbinoCard`, they compare the result against the matching preview in `../preview/comp-*-system.html`.

These reference CSS files consume Layer 2 semantic tokens; migration to that is tracked under *Wave 2 — component refactor* below.

---

## Editing the tokens

1. **Decide which layer.** New raw value → primitive. Rename or repurpose → semantic. Different in one world only → world override.
2. **Edit the CSS first.** That's the source of truth.
3. **Mirror into the exports.** Update `aura-preset.ts`, `tailwind.preset.cjs`, `urbino_theme.dart` to match.
4. **Verify previews.** `preview/*.html` reflects the change automatically since it consumes the CSS.

---

## Versioning

Semver. Bumps trigger regeneration of the three exports from the canonical CSS.

- **MAJOR** — a removed or renamed token (breaking).
- **MINOR** — a new token, a new component-level Aura override, a new `ThemeExtension` field.
- **PATCH** — a value adjusted within a semantically equivalent role.

Changelog lives in `../CHANGELOG.md` (next iteration).

---

## Migration plan

- **Wave 1 — Token architecture (this commit).** Primitives + semantic + world-cit split done. Legacy `--mgr-*` / `--cit-*` aliases preserved. `colors_and_type.css` is now a thin `@import` shim. Existing components keep working through the alias layer.
- **Wave 2 — Component refactor.** Rewrite `components/*.css` to consume `--u-*` semantic tokens; remove hardcoded px values; fix off-grid spacing (e.g. form-field 18px → 16 or 20).
- **Wave 3 — Spec realignment.** Update `docs/components/*.md` so its tables reference semantic token names rather than raw px values.

---

## What's NOT in this folder (yet)

- **Dark-mode tokens** — pilot is light-only. The Aura preset is structured so a `dark` color-scheme block can be added later in one place.
- **Field-worker app tokens** — `--u-teal-*` is reserved as a primitive; no world layer exists yet.
- **Iconography export** — Lucide is consumed from CDN today. If we vendor a subset locally, it lands here as `tokens/icons/`.
