# Export formats Loaded by [`SKILL.md`](../SKILL.md) Step 6 when emitting the design system as portable tokens. Defines the four canonical formats Hallmark always writes: 1. **`tokens.css`** — the source of truth. Always emitted alongside the page CSS. 2. **Tailwind v4 `@theme`** — for projects on Tailwind. Emitted into `design.md`'s Exports section on multi-page projects. 3. **DTCG `tokens.json`** — for projects using a token pipeline (Style Dictionary, Token Studio, Cobalt). Emitted into `design.md`. 4. **shadcn/ui CSS variables** — for projects using shadcn/ui's component library. Emitted into `design.md`. The output rule: `tokens.css` is always written. The other three live inline in `design.md` so the user copies whichever they need into a new project. **No new verb.** This is a side effect of every build. --- ## Token taxonomy — Hallmark's source of truth Every Hallmark output writes these tokens (or a subset, if the page doesn't use one). The names are the source; every other format is a translation. | Hallmark token | Type | Example value | | --- | --- | --- | | `--color-paper` | colour | `oklch(96% 0.018 80)` | | `--color-paper-2` | colour | `oklch(94% 0.020 80)` | | `--color-paper-3` | colour | `oklch(91% 0.022 80)` | | `--color-ink` | colour | `oklch(15% 0.012 80)` | | `--color-ink-2` | colour | `oklch(28% 0.014 80)` | | `--color-rule` | colour | `oklch(86% 0.018 80)` | | `--color-rule-2` | colour | `oklch(72% 0.020 80)` | | `--color-muted` | colour | `oklch(50% 0.014 80)` | | `--color-neutral` | colour | `oklch(38% 0.012 80)` | | `--color-accent` | colour | `oklch(58% 0.16 70)` | | `--color-accent-ink` | colour | (text on accent fill, ≥ APCA 7:1) | | `--color-focus` | colour | (often = accent at higher chroma) | | `--font-display` | font | `"Fraunces", ui-serif, Georgia, serif` | | `--font-body` | font | `"Geist", ui-sans-serif, system-ui, sans-serif` | | `--font-outlier` | font | `"Geist Mono", ui-monospace, monospace` | | `--space-3xs` … `--space-5xl` | length | 4-pt scale: `0.25rem` … `8rem` | | `--text-xs` … `--text-display` | length | 1.25 (major-third) ratio scale | | `--ease-out` / `--ease-in` / `--ease-in-out` | easing | `cubic-bezier(0.16, 1, 0.3, 1)` etc. | | `--dur-micro` / `--dur-short` / `--dur-long` | time | `120ms` / `220ms` / `420ms` | | `--rule-hair` / `--rule-fine` | length | `1px` / `2px` | | `--radius-card` / `--radius-pill` / `--radius-input` | length | varies per theme | | `--shadow-card` | shadow | varies per theme | If the page introduces *additional* tokens, name them by role and add to `tokens.css`. Don't make up token names downstream that aren't in `tokens.css` — the source of truth is the source of truth. --- ## Format 1 — `tokens.css` The source. Plain CSS custom properties at `:root`. Every Hallmark page CSS imports this file at the top: ```css @import "tokens.css"; /* page CSS continues — uses var(--color-paper), never raw values */ ``` Or, if the project uses a CSS bundler / framework that doesn't honour bare `@import`, the project's existing entry-point imports `tokens.css` before the page CSS that consumes it (keep it among the other top-of-file `@import`s, never above or in place of `@import "tailwindcss"`). The page CSS still references tokens by name, never by raw value. **Worked example — editorial theme (Specimen-like):** ```css :root { --color-paper: oklch(96% 0.018 80); --color-paper-2: oklch(94% 0.020 80); --color-paper-3: oklch(91% 0.022 80); --color-rule: oklch(86% 0.020 78); --color-rule-2: oklch(72% 0.022 78); --color-muted: oklch(50% 0.018 78); --color-neutral: oklch(38% 0.014 76); --color-ink-2: oklch(28% 0.012 75); --color-ink: oklch(15% 0.010 75); --color-accent: oklch(58% 0.16 60); --color-accent-ink: oklch(98% 0.012 75); --color-focus: oklch(58% 0.16 60); --font-display: "Fraunces", "Cardo", ui-serif, Georgia, serif; --font-body: "Geist", "Söhne", ui-sans-serif, system-ui, sans-serif; --font-outlier: "Geist Mono", "JetBrains Mono", ui-monospace, monospace; --display-weight: 400; --display-style: italic; --tracking-display: -0.02em; --tracking-label: 0.10em; --text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1.125rem; --text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem; --text-display: clamp(3rem, 6vw + 1rem, 6.5rem); --space-3xs: 0.25rem; --space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem; --space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem; --space-2xl: 4.5rem; --space-3xl: 7rem; --space-4xl: 11rem; --rule-hair: 0.5px; --rule-fine: 1px; --radius-card: 0; --radius-pill: 0; --radius-input: 0; --ease-out: cubic-bezier(0.16, 1, 0.3, 1); --ease-in: cubic-bezier(0.7, 0, 0.84, 0); --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); --dur-micro: 120ms; --dur-short: 220ms; --dur-long: 420ms; } ``` **Worked example — modern-minimal theme (austere, pure-white):** ```css :root { --color-paper: oklch(100% 0 0); --color-paper-2: oklch(98.5% 0 0); --color-paper-3: oklch(96% 0 0); --color-rule: oklch(91% 0 0); --color-rule-2: oklch(82% 0 0); --color-muted: oklch(55% 0 0); --color-neutral: oklch(40% 0 0); --color-ink-2: oklch(28% 0 0); --color-ink: oklch(15% 0 0); --color-accent: oklch(15% 0 0); --color-accent-ink: oklch(100% 0 0); --color-focus: oklch(60% 0.10 240); --font-display: "Geist", "Inter", ui-sans-serif, system-ui, sans-serif; --font-body: "Geist", "Inter", ui-sans-serif, system-ui, sans-serif; --font-outlier: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; --display-weight: 600; --display-style: normal; --tracking-display: -0.025em; --radius-card: 8px; --radius-pill: 999px; --radius-input: 8px; } ``` --- ## Format 2 — Tailwind v4 `@theme` Tailwind v4 reads CSS variables inside `@theme` and generates utilities (`bg-paper`, `text-ink`, etc.) automatically. The translation from Hallmark tokens is direct — same variable names, same OKLCH values: ```css @theme { /* Colours */ --color-paper: oklch(96% 0.018 80); --color-paper-2: oklch(94% 0.020 80); --color-paper-3: oklch(91% 0.022 80); --color-rule: oklch(86% 0.020 78); --color-rule-2: oklch(72% 0.022 78); --color-muted: oklch(50% 0.018 78); --color-neutral: oklch(38% 0.014 76); --color-ink-2: oklch(28% 0.012 75); --color-ink: oklch(15% 0.010 75); --color-accent: oklch(58% 0.16 60); --color-focus: oklch(58% 0.16 60); /* Fonts */ --font-display: "Fraunces", "Cardo", ui-serif, Georgia, serif; --font-body: "Geist", ui-sans-serif, system-ui, sans-serif; --font-outlier: "Geist Mono", ui-monospace, monospace; /* Spacing — Tailwind reads --spacing-* by default; we keep Hallmark's --space-* names alongside */ --spacing-3xs: 0.25rem; --spacing-2xs: 0.5rem; --spacing-xs: 0.75rem; --spacing-sm: 1rem; --spacing-md: 1.5rem; --spacing-lg: 2rem; --spacing-xl: 3rem; --spacing-2xl: 4.5rem; /* Type scale — Tailwind reads --text-* */ --text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1.125rem; --text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem; /* Radius */ --radius-card: 0; --radius-pill: 0; --radius-input: 0; /* Easings — Tailwind reads --ease-* */ --ease-out: cubic-bezier(0.16, 1, 0.3, 1); --ease-in: cubic-bezier(0.7, 0, 0.84, 0); --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); } ``` Notes: - Tailwind v4 expects `--spacing-*` (with `ing`) for the spacing utilities. We mirror Hallmark's `--space-*` to `--spacing-*` so both names work. - `--text-*` works as-is (Tailwind v4 picks them up for `text-md` etc.). - `--font-*` becomes `font-display` / `font-body` / `font-outlier` utilities. - The user's `tailwind.config.{ts,js}` may need `@source` directives but no `theme.extend` — v4 reads `@theme` directly. --- ## Format 3 — DTCG `tokens.json` W3C Design Tokens Community Group format. For projects using Style Dictionary, Cobalt, or Token Studio. Path-based; OKLCH stays as a string value; `$type` is required. ```json { "$schema": "https://design-tokens.github.io/community-group/format/", "color": { "paper": { "$value": "oklch(96% 0.018 80)", "$type": "color" }, "paper-2": { "$value": "oklch(94% 0.020 80)", "$type": "color" }, "paper-3": { "$value": "oklch(91% 0.022 80)", "$type": "color" }, "rule": { "$value": "oklch(86% 0.020 78)", "$type": "color" }, "rule-2": { "$value": "oklch(72% 0.022 78)", "$type": "color" }, "muted": { "$value": "oklch(50% 0.018 78)", "$type": "color" }, "neutral": { "$value": "oklch(38% 0.014 76)", "$type": "color" }, "ink-2": { "$value": "oklch(28% 0.012 75)", "$type": "color" }, "ink": { "$value": "oklch(15% 0.010 75)", "$type": "color" }, "accent": { "$value": "oklch(58% 0.16 60)", "$type": "color" }, "focus": { "$value": "oklch(58% 0.16 60)", "$type": "color" } }, "font": { "display": { "$value": "Fraunces, Cardo, ui-serif, Georgia, serif", "$type": "fontFamily" }, "body": { "$value": "Geist, ui-sans-serif, system-ui, sans-serif", "$type": "fontFamily" }, "outlier": { "$value": "Geist Mono, ui-monospace, monospace", "$type": "fontFamily" } }, "size": { "text-xs": { "$value": "0.75rem", "$type": "dimension" }, "text-sm": { "$value": "0.875rem", "$type": "dimension" }, "text-md": { "$value": "1.125rem", "$type": "dimension" }, "text-lg": { "$value": "1.375rem", "$type": "dimension" }, "text-xl": { "$value": "1.75rem", "$type": "dimension" }, "text-2xl": { "$value": "2.25rem", "$type": "dimension" }, "text-display": { "$value": "6rem", "$type": "dimension" } }, "space": { "3xs": { "$value": "0.25rem", "$type": "dimension" }, "2xs": { "$value": "0.5rem", "$type": "dimension" }, "xs": { "$value": "0.75rem", "$type": "dimension" }, "sm": { "$value": "1rem", "$type": "dimension" }, "md": { "$value": "1.5rem", "$type": "dimension" }, "lg": { "$value": "2rem", "$type": "dimension" }, "xl": { "$value": "3rem", "$type": "dimension" }, "2xl": { "$value": "4.5rem", "$type": "dimension" }, "3xl": { "$value": "7rem", "$type": "dimension" }, "4xl": { "$value": "11rem", "$type": "dimension" } }, "duration": { "micro": { "$value": "120ms", "$type": "duration" }, "short": { "$value": "220ms", "$type": "duration" }, "long": { "$value": "420ms", "$type": "duration" } } } ``` Notes: - DTCG accepts `oklch(...)` strings on modern tooling. If the user's pipeline doesn't, convert to hex (lossy — flag this) or to `color(oklch ...)` (CSS Color 4). - The `text-display` value uses a fixed `6rem` for portability — the responsive `clamp(...)` belongs in CSS, not in token JSON. --- ## Format 4 — shadcn/ui CSS variables shadcn/ui's component library reads CSS custom properties with specific names, in a specific shape. The values are **space-separated** triples (no `oklch()` wrapper, no commas) so shadcn can compose them with `oklch( / )`. The translation from Hallmark tokens: ```css :root { --background: 96% 0.018 80; /* paper */ --foreground: 15% 0.010 75; /* ink */ --card: 94% 0.020 80; /* paper-2 */ --card-foreground: 15% 0.010 75; /* ink */ --popover: 94% 0.020 80; /* paper-2 */ --popover-foreground: 15% 0.010 75; /* ink */ --primary: 58% 0.16 60; /* accent */ --primary-foreground: 98% 0.012 75; /* accent-ink */ --secondary: 91% 0.022 80; /* paper-3 */ --secondary-foreground: 28% 0.012 75; /* ink-2 */ --muted: 86% 0.020 78; /* rule */ --muted-foreground: 50% 0.018 78; /* muted */ --accent: 58% 0.16 60; /* accent (same as primary in Hallmark) */ --accent-foreground: 98% 0.012 75; --destructive: 58% 0.20 25; /* warm red, fixed */ --destructive-foreground: 98% 0.012 75; --border: 86% 0.020 78; /* rule */ --input: 86% 0.020 78; /* rule */ --ring: 58% 0.16 60; /* focus */ --radius: 0; /* radius-card; shadcn uses one value */ } ``` Then the user's `tailwind.config.ts` (or v4 `@theme`) reads them with `oklch(var(--background))`. Hallmark's accent → shadcn's primary is the canonical mapping; secondary → paper-3. If the user wants a dark variant, mirror with the dark theme tokens under a `.dark` selector (Hallmark's Midnight / Bloom / Terminal supply the values). --- ## Output rule When SKILL.md Step 6 emits exports: 1. **Always** write `tokens.css` next to the page CSS (or in the project root for multi-file projects). Format 1. 2. **On `design.md`-managed projects** (multi-page), embed all four formats inline in `design.md`'s Exports section. The user copies whichever they need. 3. **On Tailwind projects** (detected at pre-flight), additionally surface the Tailwind `@theme` block in the build output so the user knows where to paste it (typically into `app/globals.css` or the equivalent). 4. **Merge into an existing entry stylesheet; never overwrite it.** When the target project already has `app/globals.css` (or `src/index.css`, `src/styles/global.css`): keep its existing `@import "tailwindcss"` / `@tailwind base|components|utilities` directives exactly as they are, append Hallmark's `:root` tokens and base rules *after* them, and keep any `@import "tokens.css"` at the very top of the file (CSS parses `@import` only before other rules, so a misplaced one is silently dropped and your tokens vanish). If the project already defines brand tokens (`--background`, `--foreground`, a Tailwind `@theme`), map Hallmark's roles onto those names rather than adding a parallel set, or scope Hallmark's tokens under a wrapper class. Replace the file outright only when the user has asked for a full takeover. Don't blanket-emit tokens.json or shadcn variables on single-page projects — the user can copy them out of `design.md` if they upgrade to a multi-page system.