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:
tokens.css— the source of truth. Always emitted alongside the page CSS.- Tailwind v4
@theme— for projects on Tailwind. Emitted intodesign.md's Exports section on multi-page projects. - DTCG
tokens.json— for projects using a token pipeline (Style Dictionary, Token Studio, Cobalt). Emitted intodesign.md. - 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:
@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-*(withing) for the spacing utilities. We mirror Hallmark's--space-*to--spacing-*so both names work. --text-*works as-is (Tailwind v4 picks them up fortext-mdetc.).--font-*becomesfont-display/font-body/font-outlierutilities.- The user's
tailwind.config.{ts,js}may need@sourcedirectives but notheme.extend— v4 reads@themedirectly.
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 tocolor(oklch ...)(CSS Color 4). - The
text-displayvalue uses a fixed6remfor portability — the responsiveclamp(...)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:
- Always write
tokens.cssnext to the page CSS (or in the project root for multi-file projects). Format 1. - On
design.md-managed projects (multi-page), embed all four formats inline indesign.md's Exports section. The user copies whichever they need. - On Tailwind projects (detected at pre-flight), additionally surface the Tailwind
@themeblock in the build output so the user knows where to paste it (typically intoapp/globals.cssor the equivalent). - Merge into an existing entry stylesheet; never overwrite it. When the target project already has
app/globals.css(orsrc/index.css,src/styles/global.css): keep its existing@import "tailwindcss"/@tailwind base|components|utilitiesdirectives exactly as they are, append Hallmark's:roottokens and base rules after them, and keep any@import "tokens.css"at the very top of the file (CSS parses@importonly 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.