Files
training/.codex/skills/hallmark/references/export-formats.md
2026-07-28 19:31:07 +08:00

14 KiB

Export formats

Loaded by 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.25rem8rem
--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:

@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 @imports, 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):

: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):

: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:

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

{
  "$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(<value> / <alpha>).

The translation from Hallmark tokens:

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