diff --git a/README.md b/README.md index ad2bae9..f804714 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **A design skill that studies what you admire — and rebuilds your content with it.** -→ Live demo: **[hallmark-murex.vercel.app](https://hallmark-murex.vercel.app)** · sixteen themes, eight worked tests, press `T` to cycle. +→ Live demo: **[hallmark-murex.vercel.app](https://hallmark-murex.vercel.app)** · seventeen themes, eight worked tests, press `T` to cycle. → Current version: **v0.7.0** — adds the `custom` theme route (catalog stays the default; custom fires only when the brief signals it). @@ -28,7 +28,7 @@ Paste this into Claude Code, Cursor, or Codex with the Hallmark skill installed: > *"Build me a landing page for Coffeebox — a small-batch coffee subscription. Roast on Sunday, ship on Monday, drink Tuesday. Audience: people who already buy good coffee. Tone: warm, hand-set, editorial."* -If Hallmark is wired correctly, you'll see Long Document · Linen (warm-paper roman-serif) · Tier-B hand-built SVG. Eight more worked briefs are documented in [`recipes.md`](skill/references/recipes.md) — copy/paste them to discover the skill before reading anything else. +If Hallmark is wired correctly, you'll see Long Document · Linen (warm-paper roman-serif) · Tier-B hand-built SVG. Eight more worked briefs are documented in [`docs/recipes.md`](docs/recipes.md) — copy/paste them to discover the skill before reading anything else. --- @@ -44,7 +44,7 @@ If Hallmark is wired correctly, you'll see Long Document · Linen (warm-paper ro | **Pages by archetype** | 21 named macrostructures, picked per brief | by brief | 5 deterministic directions | n/a | | **Verbs** | 5 (default · `audit` · `refine` · `redesign` · `study`) | 1 | 31 | 1 (CLI) | -Hallmark's edge is **`study`** — every other tool ships a preset menu or a scraper. Hallmark is the only one that takes a screenshot of a design you admire, names what it sees, refuses paid-template-marketplace listings, and rebuilds your content with the extracted DNA. Three worked study examples in [`study-examples.md`](skill/references/study-examples.md). +Hallmark's edge is **`study`** — every other tool ships a preset menu or a scraper. Hallmark is the only one that takes a screenshot of a design you admire, names what it sees, refuses paid-template-marketplace listings, and rebuilds your content with the extracted DNA. Three worked study examples in [`docs/study-examples.md`](docs/study-examples.md). --- @@ -98,14 +98,16 @@ Each page is its own self-contained HTML + CSS — no shared theme, no shared la ## What's inside - **[`SKILL.md`](skill/SKILL.md)** — the routing file. Six-step design flow (including `Step 2.5 · Check project memory` reading `.hallmark/log.json`), 38-question slop test, output contract. -- **[`references/`](skill/references/)** — eighteen short, opinionated rule files: typography, colour, layout, motion, microinteractions, interaction-and-states, responsive, copy, anti-patterns, the 21 named macrostructures, the 36 component archetypes with variation knobs, the 6 primitive structure axes, the vision-extraction protocol for `study`, hero enrichment, custom-craft (CSS art over Lottie), assets, plus the new **[`recipes.md`](skill/references/recipes.md)** (8 worked briefs + a canonical try-it prompt) and **[`study-examples.md`](skill/references/study-examples.md)** (3 worked DNA-extractions). -- **[`site/`](site/)** — a self-demonstrating landing page. Hand-written HTML + CSS + ES module, no framework, no build step. **Sixteen themes** balanced across the warm / cool / neutral spectrum: warm-paper (Specimen, Atelier, Newsprint, Salon, Riso), cool-paper (Linen-cool-slate, Studio-cool-grey, Garden, Almanac, Pastel, Sport), neutral (Brutal, Quiet), dark (Midnight, Terminal, Manifesto). Switching themes literally rebuilds the page — different hero archetype, different footer archetype. +- **[`references/`](skill/references/)** — short, opinionated rule files: typography, colour, layout, motion, microinteractions, interaction-and-states, responsive, copy, anti-patterns, the 21 named macrostructures, the 36 component archetypes with variation knobs, the 6 primitive structure axes, the vision-extraction protocol for `study`, hero enrichment, custom-craft (CSS art over Lottie), assets, the slop-test gates, four genre rule-overlays, and per-verb dispatchers. +- **[`docs/`](docs/)** — human-reading content: **[`recipes.md`](docs/recipes.md)** (8 worked briefs + a canonical try-it prompt) and **[`study-examples.md`](docs/study-examples.md)** (3 worked DNA-extractions). Not auto-loaded by the skill. +- **[`site/`](site/)** — a self-demonstrating landing page. Hand-written HTML + CSS + ES module, no framework, no build step. **Seventeen themes** balanced across the warm / cool / neutral / dark spectrum: warm-paper (Specimen, Atelier, Newsprint, Salon, Riso), cool-paper (Linen-cool-slate, Studio-cool-grey, Garden, Almanac, Pastel, Sport), polished-minimal (Quiet), neutral (Brutal), dark (Midnight, Terminal, Manifesto, Bloom). Switching themes literally rebuilds the page — different hero archetype, different footer archetype. --- ## What's distinct (the long list) - **One skill, five verbs.** Not eighteen commands. +- **Genres broaden the range.** Hallmark routes a brief through one of four genres before picking a theme: **editorial** (default · the canonical anti-slop voice), **modern-minimal** (Stripe / Linear / ElevenLabs school), **atmospheric** (Suno / Runway / dark-AI-tool school), **playful** (post-Linear soft school). Each genre is its own rule overlay — atmospheric allows radial blooms; modern-minimal allows pure white and pill CTAs; editorial bans both. Detection is signal-based, silent default to editorial. - **Tone is a first-class decision.** "Clean and modern" is rejected. Pick an extreme — *editorial · brutalist · soft · technical · luxury · playful · austere*. - **Macrostructures over axes.** Pick one of 21 named whole-page shapes wholesale; the macrostructure stamp lives in the CSS comment, so the next Hallmark run picks something different. - **Within-archetype variation.** Two Bento Grids should not be twins; each archetype has 2–3 picked-per-output knobs. @@ -118,7 +120,7 @@ Each page is its own self-contained HTML + CSS — no shared theme, no shared la - **Microinteractions default-on for SaaS-shaped archetypes.** Bento Grid, Stat-Led, Workbench, Marquee Hero pages ship with 2–3 purposeful microinteractions (number reveal, pricing lift, marquee, stagger) without the user having to ask. Editorial / Manifesto / Letter / Quote-Led pages stay still. - **SaaS page sequence.** Hero → social proof → features → testimonials → pricing → FAQ → CTA → footer. Real prices, not "contact sales for pricing." Specific testimonials with role + company. - **Wordmark may use a different display face.** A Geist-bodied SaaS page can set its wordmark in Fraunces. Same-family collapse on Bento / Stat-Led / Workbench / Marquee Hero is the new "un-branded" tell. -- **`study` extracts DNA, not pixels.** Refusal heuristics, type-role vocabulary (no font ID guessing), confirmation step before any code. Three worked examples in [`study-examples.md`](skill/references/study-examples.md). +- **`study` extracts DNA, not pixels.** Refusal heuristics, type-role vocabulary (no font ID guessing), confirmation step before any code. Three worked examples in [`docs/study-examples.md`](docs/study-examples.md). --- diff --git a/skill/references/recipes.md b/docs/recipes.md similarity index 100% rename from skill/references/recipes.md rename to docs/recipes.md diff --git a/skill/references/study-examples.md b/docs/study-examples.md similarity index 100% rename from skill/references/study-examples.md rename to docs/study-examples.md diff --git a/site/css/components.css b/site/css/components.css index 4814c22..9ffd8a7 100644 --- a/site/css/components.css +++ b/site/css/components.css @@ -69,6 +69,183 @@ letter-spacing: 0.14em; } +/* Sticky Install link — sits between the wordmark and the spacer. + Becomes the most prominent thing on the banner once the user + scrolls past the hero. */ +.banner__install { + margin-inline-start: var(--space-md); + padding: 0.18rem 0.65rem; + font-family: var(--font-label); + font-weight: 500; + font-size: 0.6875rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--color-ink); + text-decoration: none; + border: 1px solid var(--color-rule-2); + border-radius: var(--radius-pill, 999px); + transition: background-color var(--dur-micro) var(--ease-out), + color var(--dur-micro) var(--ease-out), + border-color var(--dur-micro) var(--ease-out); +} + +.banner__install:hover { + background: var(--color-ink); + color: var(--color-paper); + border-color: var(--color-ink); +} + +.banner__install:focus-visible { + outline: 2px solid var(--color-focus); + outline-offset: 2px; +} + +@media (max-width: 50rem) { + .banner__install { display: none; } +} + +/* Live stamp callout — small mono annotation showing what Hallmark + is currently rendering. Hover expands to the full stamp comment. + Sits between the install link and the spacer; hides on narrow. */ +.banner__stamp { + position: relative; + margin-inline-start: var(--space-sm); + padding: 0.18rem 0.55rem; + font-family: var(--font-mono); + font-size: 0.625rem; + letter-spacing: 0; + text-transform: lowercase; + color: var(--color-muted); + border: 1px dashed var(--color-rule); + border-radius: 2px; + user-select: text; + cursor: default; +} + +@media (hover: hover) and (pointer: fine) { + .banner__stamp:hover { color: var(--color-ink); border-color: var(--color-rule-2); } + .banner__stamp:hover .banner__stamp-detail { opacity: 1; pointer-events: auto; transform: translateY(0); } +} + +.banner__stamp-detail { + position: absolute; + top: calc(100% + 6px); + left: 0; + white-space: nowrap; + padding: 0.5rem 0.75rem; + font-family: var(--font-mono); + font-size: 0.6875rem; + color: var(--color-ink-2); + background: var(--color-paper); + border: 1px solid var(--color-rule-2); + border-radius: 2px; + box-shadow: 0 4px 12px -4px oklch(20% 0.02 60 / 0.16); + opacity: 0; + pointer-events: none; + transform: translateY(-4px); + transition: opacity 140ms var(--ease-out), transform 140ms var(--ease-out); + z-index: 10; +} + +@media (max-width: 64rem) { + .banner__stamp { display: none; } +} + +/* Genre label — small text after the theme name in the banner. */ +.banner__theme { + display: inline; +} + +.banner__genre { + display: inline; + margin-inline-start: 0.5em; + font-family: var(--font-mono); + font-size: 0.6875rem; + letter-spacing: 0.04em; + text-transform: lowercase; + color: var(--color-muted); + font-style: normal; + font-weight: 400; +} + +.banner__genre::before { + content: "· "; + color: var(--color-rule-2); + margin-inline-end: 0.1em; +} + +@media (max-width: 38rem) { + .banner__genre { display: none; } +} + +/* T-key onboarding tooltip — appears after 5 s idle, dismisses on + first T press. Anchored under the shuffle button. */ +.t-tooltip { + position: absolute; + top: calc(100% + 12px); + right: var(--page-gutter); + padding: 0.55rem 0.85rem; + background: var(--color-ink); + color: var(--color-paper); + border-radius: 6px; + font-family: var(--font-body); + font-size: 0.8125rem; + letter-spacing: 0; + text-transform: none; + line-height: 1.45; + box-shadow: 0 8px 24px -8px oklch(20% 0.02 60 / 0.32); + z-index: var(--z-sticky); + animation: t-tooltip-in 320ms var(--ease-out) both; +} + +.t-tooltip[hidden] { display: none; } + +.t-tooltip[data-state="closing"] { + animation: t-tooltip-out 240ms var(--ease-in) forwards; +} + +@keyframes t-tooltip-in { + from { opacity: 0; transform: translateY(-6px); } + to { opacity: 1; transform: none; } +} + +@keyframes t-tooltip-out { + from { opacity: 1; transform: none; } + to { opacity: 0; transform: translateY(-4px); } +} + +.t-tooltip__arrow { + position: absolute; + top: -5px; + right: 28px; + width: 10px; + height: 10px; + background: var(--color-ink); + transform: rotate(45deg); + border-radius: 1px; +} + +.t-tooltip__body kbd { + display: inline-block; + padding: 0.1em 0.45em; + margin-inline: 0.1em; + background: var(--color-paper); + color: var(--color-ink); + border-radius: 3px; + font-family: var(--font-mono); + font-size: 0.85em; + font-weight: 500; + letter-spacing: 0; +} + +.t-tooltip__body strong { + font-weight: 600; +} + +@media (prefers-reduced-motion: reduce) { + .t-tooltip { animation: none; } +} + /* The active theme name — sits to the LEFT of the theme picker so the page restructure registers as a deliberate event, not a passive repaint. Smaller text + closer to the picker on desktop; hidden on narrow viewports. */ @@ -713,6 +890,419 @@ border-radius: 0; } +/* Dashboard W/W variant — slop = glass widget, hallmark = considered. */ +.vs__panel--dash { + min-height: 18rem; +} + +/* Slop dashboard — glassmorphic, gradient, centered, badged. */ +.vs__panel--dash[data-variant="slop"] { + position: relative; + background: + linear-gradient(135deg, + color-mix(in srgb, #6366f1 14%, transparent) 0%, + color-mix(in srgb, #c14de0 14%, transparent) 60%, + color-mix(in srgb, #f97316 12%, transparent) 100%), + rgba(255, 255, 255, 0.65); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + border-color: rgba(0, 0, 0, 0.06); + border-radius: 16px; + text-align: center; + align-content: center; + justify-items: center; + box-shadow: 0 12px 32px -16px rgba(99, 102, 241, 0.35); + font-family: "Inter", system-ui, sans-serif; +} + +.vs__panel--dash[data-variant="slop"] .vs__panel-stat { + margin: 0; + font-family: inherit; + font-weight: 800; + font-size: clamp(2.75rem, 4vw + 1rem, 3.75rem); + letter-spacing: -0.03em; + line-height: 1; + background: linear-gradient(95deg, #6366f1, #a855f7 50%, #ec4899); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.vs__panel--dash[data-variant="slop"] .vs__panel-stat-label { + margin: 0.4rem 0 0; + font-family: inherit; + font-size: 0.875rem; + color: #6b6b76; +} + +.vs__panel--dash[data-variant="slop"] .vs__panel-badge { + display: inline-flex; + align-items: center; + margin: 0.6rem 0 0.4rem; + padding: 0.18rem 0.65rem; + font-family: inherit; + font-size: 0.75rem; + font-weight: 600; + color: #16a34a; + background: rgba(34, 197, 94, 0.12); + border-radius: 999px; + box-shadow: 0 0 12px -2px rgba(34, 197, 94, 0.5); +} + +/* Hallmark dashboard — hairline rule, mono numerals, asymmetric. */ +.vs__panel--dash[data-variant="hallmark"] { + text-align: left; + padding-top: var(--space-2xl); + border-top: 2px solid var(--color-ink); + border-left: var(--rule-hair) solid var(--color-rule-2); + border-right: var(--rule-hair) solid var(--color-rule-2); + border-bottom: var(--rule-hair) solid var(--color-rule-2); + background: var(--color-paper); + border-radius: 0; +} + +.vs__panel--dash[data-variant="hallmark"] .vs__panel-stat-line { + display: flex; + align-items: baseline; + gap: var(--space-md); + margin: 0; +} + +.vs__panel--dash[data-variant="hallmark"] .vs__panel-stat-num { + font-family: var(--font-mono); + font-feature-settings: "tnum"; + font-variant-numeric: tabular-nums; + font-size: clamp(2.75rem, 4vw + 1rem, 3.75rem); + font-weight: 500; + letter-spacing: -0.03em; + color: var(--color-ink); + line-height: 1; +} + +.vs__panel--dash[data-variant="hallmark"] .vs__panel-stat-delta { + display: inline-flex; + align-items: center; + gap: 0.4em; + font-family: var(--font-label); + font-size: var(--text-xs); + letter-spacing: var(--tracking-label); + text-transform: uppercase; + color: var(--color-muted); +} + +.vs__panel--dash[data-variant="hallmark"] .vs__panel-stat-dot { + display: inline-block; + width: 6px; + height: 6px; + background: var(--color-accent); + border-radius: 50%; +} + +.vs__panel--dash[data-variant="hallmark"] .vs__panel-stat-label { + margin: var(--space-md) 0 0; + font-family: var(--font-body); + font-size: 0.9375rem; + line-height: 1.5; + color: var(--color-ink-2); + max-width: 32ch; +} + +/* Pricing W/W variant — slop = popular badge + glow + emoji, + hallmark = editorial tier card with hairline rule and mono price. */ +.vs__panel--pricing { min-height: 22rem; } + +.vs__panel--pricing[data-variant="slop"] { + position: relative; + text-align: center; + align-content: start; + justify-items: center; + padding-top: 2.4rem; + background: + linear-gradient(135deg, + color-mix(in srgb, #6366f1 8%, transparent) 0%, + color-mix(in srgb, #ec4899 8%, transparent) 100%), + rgba(255, 255, 255, 0.85); + border: 1px solid rgba(99, 102, 241, 0.25); + border-radius: 18px; + box-shadow: 0 16px 36px -16px rgba(99, 102, 241, 0.4); + font-family: "Inter", system-ui, sans-serif; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-pop { + position: absolute; + top: -0.7rem; + left: 50%; + transform: translateX(-50%); + padding: 0.25rem 0.85rem; + background: linear-gradient(95deg, #6366f1, #a855f7, #ec4899); + color: #fff; + border-radius: 999px; + font-family: inherit; + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + box-shadow: 0 4px 12px -2px rgba(168, 85, 247, 0.5); + white-space: nowrap; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-eyebrow { + font-family: inherit; + font-weight: 600; + font-size: 0.75rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #6366f1; + margin: 0; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-price { + margin: 0.4rem 0; + font-family: inherit; + font-weight: 800; + font-size: 2.5rem; + letter-spacing: -0.025em; + line-height: 1; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-price-num { + background: linear-gradient(95deg, #6366f1, #a855f7 50%, #ec4899); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-price-unit { + font-size: 0.875rem; + font-weight: 500; + color: #6b6b76; + letter-spacing: 0; + margin-inline-start: 0.25em; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-sub { + margin: 0; + font-family: inherit; + font-size: 0.8125rem; + color: #6b6b76; + max-width: 28ch; +} + +.vs__panel--pricing[data-variant="slop"] .vs__panel-bullets { + list-style: none; + margin: 0.6rem 0 0.3rem; + padding: 0; + display: grid; + gap: 0.4rem; + font-family: inherit; + font-size: 0.8125rem; + color: #2a2a32; + text-align: left; +} + +/* Hallmark pricing — editorial tier card. */ +.vs__panel--pricing[data-variant="hallmark"] { + text-align: left; + align-content: start; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-tier { + margin: 0; + font-family: var(--font-display); + font-weight: var(--display-weight, 500); + font-style: var(--display-style, normal); + font-size: var(--text-xl); + letter-spacing: var(--tracking-tight); + color: var(--color-ink); + padding-block-end: var(--space-sm); + border-block-end: var(--rule-hair) solid var(--color-rule-2); + font-optical-sizing: auto; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-price { + display: flex; + align-items: baseline; + gap: 0.4rem; + margin: var(--space-md) 0 0; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-price-num { + font-family: var(--font-mono); + font-feature-settings: "tnum"; + font-variant-numeric: tabular-nums; + font-size: clamp(2.25rem, 3vw + 1rem, 3rem); + font-weight: 500; + letter-spacing: -0.025em; + color: var(--color-ink); + line-height: 1; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-price-unit { + font-family: var(--font-mono); + font-size: 0.875rem; + color: var(--color-muted); + letter-spacing: 0; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-sub { + margin: var(--space-sm) 0 0; + max-width: 30ch; +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-bullets { + list-style: none; + margin: var(--space-md) 0 0; + padding: 0; + display: grid; + gap: 0; + font-family: var(--font-body); + font-size: 0.9375rem; + color: var(--color-ink-2); +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-bullets li { + padding-block: var(--space-sm); + border-block-end: var(--rule-hair) solid var(--color-rule); +} + +.vs__panel--pricing[data-variant="hallmark"] .vs__panel-bullets li:last-child { + border-block-end: 0; +} + +/* Personal-site W/W variant — slop = avatar + gradient name + pills, + hallmark = letter format with italic name and text links. */ +.vs__panel--personal { min-height: 22rem; } + +.vs__panel--personal[data-variant="slop"] { + text-align: center; + align-content: center; + justify-items: center; + background: + linear-gradient(160deg, + color-mix(in srgb, #ec4899 6%, transparent) 0%, + color-mix(in srgb, #6366f1 8%, transparent) 100%), + rgba(255, 255, 255, 0.92); + border-color: rgba(0, 0, 0, 0.06); + border-radius: 16px; + font-family: "Inter", system-ui, sans-serif; +} + +.vs__panel--personal[data-variant="slop"] .vs__panel-avatar { + display: grid; + place-items: center; + width: 4.5rem; + height: 4.5rem; + border-radius: 50%; + background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899); + color: #fff; + font-family: inherit; + font-weight: 700; + font-size: 1.75rem; + letter-spacing: -0.02em; + box-shadow: 0 8px 22px -10px rgba(168, 85, 247, 0.5); + margin-bottom: 0.6rem; +} + +.vs__panel--personal[data-variant="slop"] .vs__panel-name { + margin: 0; + font-family: inherit; + font-weight: 800; + font-size: 1.875rem; + letter-spacing: -0.025em; + background: linear-gradient(95deg, #6366f1, #a855f7 50%, #ec4899); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.vs__panel--personal[data-variant="slop"] .vs__panel-sub { + margin: 0.4rem 0 0.9rem; + font-family: inherit; + font-size: 0.875rem; + color: #6b6b76; + max-width: 28ch; +} + +.vs__panel--personal[data-variant="slop"] .vs__panel-pills { + display: flex; + gap: 0.4rem; + flex-wrap: wrap; + justify-content: center; +} + +.vs__panel--personal[data-variant="slop"] .vs__panel-pill { + padding: 0.4rem 0.95rem; + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(99, 102, 241, 0.25); + border-radius: 999px; + font-family: inherit; + font-size: 0.8125rem; + font-weight: 500; + color: #6366f1; + box-shadow: 0 2px 6px -2px rgba(99, 102, 241, 0.2); +} + +/* Hallmark personal — letter format. */ +.vs__panel--personal[data-variant="hallmark"] { + text-align: left; + align-content: start; +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-name { + margin: var(--space-sm) 0 0; + font-family: var(--font-display); + font-weight: var(--display-weight, 500); + font-style: italic; + font-size: clamp(2rem, 3vw + 1rem, 2.875rem); + line-height: 1.05; + letter-spacing: -0.02em; + color: var(--color-ink); + font-optical-sizing: auto; +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-name em { + font-family: var(--font-display); + font-style: normal; + font-weight: var(--display-weight, 500); +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-bio { + margin: var(--space-md) 0 0; + font-family: var(--font-serif); + font-style: italic; + font-size: 1rem; + line-height: var(--lh-relaxed); + color: var(--color-ink-2); + max-width: 38ch; +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-links { + margin: var(--space-lg) 0 0; + font-family: var(--font-label); + font-size: var(--text-xs); + letter-spacing: var(--tracking-label); + text-transform: uppercase; + color: var(--color-muted); + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-linkitem { + color: var(--color-ink); + text-decoration: none; + background-image: linear-gradient(var(--color-accent), var(--color-accent)); + background-repeat: no-repeat; + background-position: 0 100%; + background-size: 100% 1px; + transition: background-size var(--dur-short) var(--ease-out); +} + +.vs__panel--personal[data-variant="hallmark"] .vs__panel-linkitem:hover { + background-size: 100% 2px; +} + /* — Install pane internals ——————————————————————————— */ /* Row label — "I · Run" */ @@ -2009,10 +2599,11 @@ .slot[data-populating] > * { animation: none; opacity: 1; } } -/* ───────────── QUIET — restraint as the design ───────────── - Override every "expressive" treatment on the page when Quiet is active. - No reveals, no per-theme display weight tricks, no shadow glows. - System fonts inherit naturally; just make sure nothing yells. */ +/* ───────────── QUIET — polished minimalism ───────────── + The "modern enterprise" theme: Geist sans, pure white, large + confident display, generous space, pill CTAs. Reads like the + ElevenLabs / Stripe school of restraint. Reveals stay off — the + page is composed, not animated in. */ [data-theme="quiet"] .reveal, [data-theme="quiet"] .reveal.is-in, @@ -2028,31 +2619,35 @@ [data-theme="quiet"] .section__title, [data-theme="quiet"] .foot__wordmark, [data-theme="quiet"] .spec__name, -[data-theme="quiet"] .tell__name { +[data-theme="quiet"] .ex-card__name, +[data-theme="quiet"] .harness__name { font-family: var(--font-display); - font-weight: 500; + font-weight: 600; font-style: normal; text-transform: none; - letter-spacing: -0.015em; - text-shadow: none; /* override Riso's off-register if a user toggled */ + letter-spacing: -0.025em; + text-shadow: none; } +[data-theme="quiet"] .hero__display { font-weight: 600; } + [data-theme="quiet"] .hero__lede, [data-theme="quiet"] .hero__letter-body, [data-theme="quiet"] .spec__rule, -[data-theme="quiet"] .tell__why, -[data-theme="quiet"] .foot__tagline { +[data-theme="quiet"] .foot__tagline, +[data-theme="quiet"] .install-pane__next { font-family: var(--font-body); font-style: normal; + font-weight: 400; color: var(--color-neutral); } [data-theme="quiet"] .hero__salutation { - font-family: var(--font-body); + font-family: var(--font-display); font-style: normal; - font-weight: 500; - font-size: var(--text-xl); - letter-spacing: 0; + font-weight: 600; + font-size: var(--text-2xl); + letter-spacing: -0.02em; } [data-theme="quiet"] .hero__sign, @@ -2061,16 +2656,66 @@ font-style: normal; } +[data-theme="quiet"] .hero__attrib, +[data-theme="quiet"] .section-label, +[data-theme="quiet"] .label, +[data-theme="quiet"] .install-pane__label, +[data-theme="quiet"] .vs__label, +[data-theme="quiet"] .ex-card__num { + font-family: var(--font-label); + font-weight: 500; +} + [data-theme="quiet"] .banner__center { font-family: var(--font-display); font-weight: 500; - font-size: var(--text-sm); - letter-spacing: 0; + font-size: 0.875rem; + letter-spacing: -0.005em; text-transform: none; } [data-theme="quiet"] .banner { - background: color-mix(in oklch, var(--color-paper) 92%, transparent); + background: color-mix(in oklch, var(--color-paper) 94%, transparent); + border-block-end-color: var(--color-rule); +} + +/* Pill CTAs — Quiet's signature button. Black-filled primary, white- + outlined secondary. Applied to anything that reads as a CTA. */ +[data-theme="quiet"] .install-pane__copy { + border-radius: 999px; + background: var(--color-ink); + color: var(--color-paper); + border-color: var(--color-ink); + font-weight: 600; +} +[data-theme="quiet"] .install-pane__copy:hover { + background: var(--color-ink-2); + color: var(--color-paper); + border-color: var(--color-ink-2); +} +[data-theme="quiet"] .install-pane__copy[data-state="copied"] { + background: var(--color-ink); + color: var(--color-paper); + border-color: var(--color-ink); +} + +/* Quiet's install command bar — refined card surface, clean typography. */ +[data-theme="quiet"] .install-pane { + background: var(--color-paper-2); + border-color: var(--color-rule); +} +[data-theme="quiet"] .install-pane__cmd { + background: var(--color-paper); + border-color: var(--color-rule); +} +[data-theme="quiet"] .install-pane__prompt { + color: var(--color-muted); +} + +/* The "without/with" demo on Quiet — soft, monochrome panels. */ +[data-theme="quiet"] .vs__panel[data-variant="hallmark"] { + border-left-color: var(--color-rule-2); + border-color: var(--color-rule); } /* No reveal animation on Quiet — the page is intentionally still. */ @@ -2083,6 +2728,172 @@ [data-theme="quiet"] .hero--clipped .hero__mock { display: none; } [data-theme="quiet"] .hero--clipped { grid-template-columns: 1fr; } +/* ───────────── BLOOM — atmospheric dark, warm bloom ───────────── + Suno-school: dark canvas with two soft radial colour blooms behind + the content, confident sans display, single warm accent on small + surfaces, pill CTAs. Reads like an AI-creative product page. */ + +html[data-theme="bloom"] { + background: var(--color-paper); +} + +html[data-theme="bloom"] body { + background: + radial-gradient(ellipse 80% 60% at 90% 0%, + color-mix(in oklch, var(--color-accent) 28%, transparent) 0%, + transparent 55%), + radial-gradient(ellipse 70% 55% at 5% 95%, + color-mix(in oklch, var(--color-accent-2) 26%, transparent) 0%, + transparent 55%), + var(--color-paper); + background-attachment: fixed; + min-height: 100dvh; +} + +[data-theme="bloom"] .hero__display, +[data-theme="bloom"] .hero__quote p, +[data-theme="bloom"] .hero__stat, +[data-theme="bloom"] .section__title, +[data-theme="bloom"] .foot__wordmark, +[data-theme="bloom"] .spec__name, +[data-theme="bloom"] .ex-card__name, +[data-theme="bloom"] .harness__name { + font-family: var(--font-display); + font-weight: 600; + font-style: normal; + text-transform: none; + letter-spacing: -0.03em; + text-shadow: none; +} + +[data-theme="bloom"] .hero__lede, +[data-theme="bloom"] .hero__letter-body, +[data-theme="bloom"] .spec__rule, +[data-theme="bloom"] .foot__tagline, +[data-theme="bloom"] .install-pane__next, +[data-theme="bloom"] .ex-card__brief { + font-family: var(--font-body); + font-style: normal; + color: var(--color-ink-2); +} + +[data-theme="bloom"] .hero__salutation { + font-family: var(--font-display); + font-weight: 600; + font-size: var(--text-2xl); + letter-spacing: -0.025em; + font-style: normal; +} + +/* Bloom's hero — centered, generous, with a glow behind the type. */ +[data-theme="bloom"] .hero--marquee, +[data-theme="bloom"] .hero--clipped { + text-align: center; + align-items: center; + justify-items: center; + padding-block: var(--space-3xl) var(--space-2xl); +} + +[data-theme="bloom"] .hero--marquee .hero__display, +[data-theme="bloom"] .hero--clipped .hero__display { + margin-inline: auto; + max-width: 22ch; +} + +[data-theme="bloom"] .hero--marquee .hero__lede, +[data-theme="bloom"] .hero--clipped .hero__lede { + margin-inline: auto; + text-align: center; + max-width: 52ch; +} + +[data-theme="bloom"] .hero__eyebrow { + margin-inline: auto; + justify-content: center; +} + +[data-theme="bloom"] .hero--marquee .hero__cta, +[data-theme="bloom"] .hero--clipped .hero__cta { + margin-inline: auto; + justify-self: center; +} + +[data-theme="bloom"] .hero--clipped .hero__mock { display: none; } +[data-theme="bloom"] .hero--clipped { grid-template-columns: 1fr; } + +/* Bloom's section heads — also centered for atmosphere. */ +[data-theme="bloom"] .section__head { + grid-template-columns: 1fr; + text-align: center; + border-bottom: 0; + padding-bottom: var(--space-md); +} +[data-theme="bloom"] .section-label { + justify-content: center; +} +[data-theme="bloom"] .section__title { + margin-inline: auto; + max-width: 26ch; +} + +/* Bloom's banner — tinted dark glass over the canvas. */ +[data-theme="bloom"] .banner { + background: color-mix(in oklch, var(--color-paper) 70%, transparent); + border-block-end-color: var(--color-rule); +} +[data-theme="bloom"] .banner__center { + font-family: var(--font-display); + font-weight: 500; + font-size: 0.875rem; + letter-spacing: -0.005em; + text-transform: none; +} + +/* Bloom's install command bar — feels like a Suno input field. */ +[data-theme="bloom"] .install-pane { + background: var(--color-paper-2); + border-color: var(--color-rule); +} +[data-theme="bloom"] .install-pane__cmd { + background: var(--color-paper-3); + border-color: var(--color-rule); +} +[data-theme="bloom"] .install-pane__copy { + background: var(--color-accent); + color: var(--color-paper); + border-color: var(--color-accent); + border-radius: 999px; + font-weight: 600; + text-transform: uppercase; +} +[data-theme="bloom"] .install-pane__copy:hover { + background: color-mix(in oklch, var(--color-accent) 88%, var(--color-paper)); + color: var(--color-paper); + border-color: transparent; +} + +/* Bloom's example rail tiles — elevated cards floating on the canvas. */ +[data-theme="bloom"] .ex-card { + background: var(--color-paper-2); + border-color: var(--color-rule); +} + +/* Without/With on Bloom — the slop panel still uses the white-AI + look against the dark canvas, which actually demonstrates "the AI + reaches for the same generic page even on a dark site." */ +[data-theme="bloom"] .vs__panel[data-variant="hallmark"] { + background: var(--color-paper-2); + border-left-color: var(--color-accent); + border-color: var(--color-rule); +} + +/* Bloom's reveal — fade only, no slide. The atmosphere does the work. */ +[data-theme="bloom"] .reveal { transform: none; } +[data-theme="bloom"] .reveal.is-in { + animation: reveal-fade calc(var(--dur-long) * 1.2) var(--ease-out) forwards; + animation-delay: calc(var(--i, 0) * 70ms); +} + /* ───────────── EXAMPLES RAIL — TILES ───────────── Each tile in the horizontal scroll-snap rail. Cards inherit the active theme's --radius-card, --rule-card, and --shadow-card so @@ -2219,6 +3030,75 @@ [data-theme="riso"] .ex-card__name { text-transform: lowercase; } +/* Decision-trail popover — appears after 600ms hover/focus on a tile. + Shows the routing chain Hallmark used to arrive at this output. + Pure CSS — no JS, just transition-delay on the show transition. */ +.ex-card__trail { + position: absolute; + top: var(--space-md); + left: var(--space-md); + right: var(--space-md); + padding: var(--space-md) var(--space-lg); + display: grid; + gap: 0.4rem; + background: var(--color-paper); + border: 1px solid var(--color-ink); + border-radius: var(--radius-input, 0); + box-shadow: 0 12px 28px -10px oklch(20% 0.02 60 / 0.28); + font-family: var(--font-mono); + font-size: 0.75rem; + line-height: 1.4; + color: var(--color-ink); + opacity: 0; + transform: translateY(6px); + pointer-events: none; + transition: opacity 200ms var(--ease-out), + transform 200ms var(--ease-out); + z-index: 5; +} + +@media (hover: hover) and (pointer: fine) { + .ex-card:hover .ex-card__trail, + .ex-card:focus-visible .ex-card__trail { + opacity: 1; + transform: none; + transition-delay: 600ms; + } +} + +.trail__row { + display: grid; + grid-template-columns: 4rem 1fr; + gap: var(--space-sm); + margin: 0; + align-items: baseline; +} + +.trail__k { + font-family: var(--font-label); + font-size: 0.625rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--color-muted); +} + +.trail__v { + font-family: var(--font-mono); + font-size: 0.8125rem; + color: var(--color-ink); + letter-spacing: 0; +} + +@media (prefers-reduced-motion: reduce) { + .ex-card__trail { transition-duration: 0ms; transition-delay: 600ms; } +} + +@media (hover: none) { + /* On touch devices the popover doesn't trigger — hide it entirely + so it doesn't clip badly. The trail is a desktop affordance. */ + .ex-card__trail { display: none; } +} + /* ───────────── FOUNDATION VIZ — illustrations ───────────── Eight different visual demos, one per spec card. Each one prefers real demonstration over decoration: real type for F/01, real space diff --git a/site/css/sections.css b/site/css/sections.css index 4d23a43..26cb326 100644 --- a/site/css/sections.css +++ b/site/css/sections.css @@ -202,13 +202,117 @@ .examples-rail::-webkit-scrollbar-track { background: transparent; } .examples-rail::-webkit-scrollbar-thumb { background: var(--color-rule-2); border-radius: 0; } -/* — 03 · Without / With (parity panels) ——————————————— */ +/* — 03 · Without / With (parity panels, four pairs) —————— + The section holds four W/W pairs that swap based on which toggle + radio is :checked. Pure CSS, no JS. + + Scroll-jump fix: the radio inputs sit in normal document flow + with zero size + opacity 0 (no position:absolute). When the user + clicks a label, the browser focuses the input — but because the + input is already in view (right above the toggle), no scroll + happens. Earlier versions used position:absolute which positioned + the input at top:0 of the viewport; clicking a label far below + would scroll the page back to the top. */ +.vs-toggle__input { + appearance: none; + -webkit-appearance: none; + width: 0; + height: 0; + margin: 0; + padding: 0; + border: 0; + outline: 0; + opacity: 0; + flex: 0 0 0; + pointer-events: none; +} + +.vs-toggle { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 0; + padding: 0.25rem; + background: var(--color-paper-2); + border: 1px solid var(--color-rule); + border-radius: var(--radius-pill, 999px); + margin-block-end: var(--space-xl); + align-self: start; + max-width: 100%; +} + +.vs-toggle__btn { + display: inline-flex; + align-items: center; + gap: 0.4em; + padding: 0.45rem 0.95rem; + font-family: var(--font-label); + font-size: var(--text-xs); + letter-spacing: var(--tracking-label); + text-transform: uppercase; + color: var(--color-muted); + cursor: pointer; + border-radius: var(--radius-pill, 999px); + transition: background-color var(--dur-micro) var(--ease-out), + color var(--dur-micro) var(--ease-out); + user-select: none; + white-space: nowrap; +} + +.vs-toggle__btn:hover { color: var(--color-ink); } + +.vs-toggle__num { + font-family: var(--font-mono); + font-size: 0.75em; + letter-spacing: 0.04em; + color: inherit; + opacity: 0.6; + font-feature-settings: "tnum"; +} + +.vs-toggle__label { + font-weight: 500; +} + +/* Focus ring on whichever button corresponds to the focused input. */ +#vs-notes:focus-visible ~ .vs-toggle .vs-toggle__btn[for="vs-notes"], +#vs-dashboard:focus-visible ~ .vs-toggle .vs-toggle__btn[for="vs-dashboard"], +#vs-pricing:focus-visible ~ .vs-toggle .vs-toggle__btn[for="vs-pricing"], +#vs-personal:focus-visible ~ .vs-toggle .vs-toggle__btn[for="vs-personal"] { + outline: 2px solid var(--color-focus); + outline-offset: 3px; +} + +#vs-notes:checked ~ .vs-toggle .vs-toggle__btn[for="vs-notes"], +#vs-dashboard:checked ~ .vs-toggle .vs-toggle__btn[for="vs-dashboard"], +#vs-pricing:checked ~ .vs-toggle .vs-toggle__btn[for="vs-pricing"], +#vs-personal:checked ~ .vs-toggle .vs-toggle__btn[for="vs-personal"] { + background: var(--color-ink); + color: var(--color-paper); +} + +#vs-notes:checked ~ .vs-toggle .vs-toggle__btn[for="vs-notes"] .vs-toggle__num, +#vs-dashboard:checked ~ .vs-toggle .vs-toggle__btn[for="vs-dashboard"] .vs-toggle__num, +#vs-pricing:checked ~ .vs-toggle .vs-toggle__btn[for="vs-pricing"] .vs-toggle__num, +#vs-personal:checked ~ .vs-toggle .vs-toggle__btn[for="vs-personal"] .vs-toggle__num { + opacity: 0.85; +} + +.vs-pairs { display: contents; } + .vs { - display: grid; + display: none; grid-template-columns: 1fr 1fr; gap: var(--space-lg); } +#vs-notes:checked ~ .vs-pairs .vs--notes, +#vs-dashboard:checked ~ .vs-pairs .vs--dashboard, +#vs-pricing:checked ~ .vs-pairs .vs--pricing, +#vs-personal:checked ~ .vs-pairs .vs--personal { + display: grid; +} + @media (max-width: 50rem) { .vs { grid-template-columns: 1fr; } } @@ -306,6 +410,136 @@ .step__body { display: grid; gap: var(--space-sm); } +/* — 05 · FAQ — typographic Q/A —————————————————————————— + Five numbered Q/A pairs in editorial format. Q in display face, + A in body serif. Tighter than a typical FAQ — meant to scan, not + loom. Re-typesets per-theme. */ +.faq { + display: grid; + gap: 0; + list-style: none; + margin: 0; + padding: 0; +} + +.faq__row { + display: grid; + grid-template-columns: 5rem 1fr; + gap: 0.4rem var(--space-xl); + align-items: baseline; + padding-block: var(--space-lg) var(--space-md); + padding-inline: 0; + border-block-start: var(--rule-hair) solid var(--color-rule); +} + +.faq__row:first-child { border-block-start: 0; padding-block-start: 0; } + +@media (max-width: 50rem) { + .faq__row { + grid-template-columns: 1fr; + gap: 0.4rem; + padding-block: var(--space-md); + } +} + +.faq__q-mark { + margin: 0; + font-family: var(--font-label); + font-size: var(--text-xs); + letter-spacing: var(--tracking-label); + text-transform: uppercase; + color: var(--color-muted); + font-feature-settings: "tnum"; + align-self: baseline; +} + +.faq__qletter { + font-family: var(--font-display); + font-style: var(--display-style, normal); + font-weight: var(--display-weight, 500); + font-size: 0.875rem; + letter-spacing: 0; + text-transform: none; + color: var(--color-ink); + margin-inline-end: 0.2em; +} + +[data-theme="riso"] .faq__qletter { text-transform: lowercase; } +[data-theme="brutal"] .faq__qletter, +[data-theme="manifesto"] .faq__qletter, +[data-theme="sport"] .faq__qletter { text-transform: uppercase; } + +.faq__q { + margin: 0; + font-family: var(--font-display); + font-weight: var(--display-weight, 500); + font-style: var(--display-style, normal); + font-size: clamp(1.125rem, 1vw + 0.75rem, 1.5rem); + line-height: var(--lh-snug); + letter-spacing: var(--tracking-tight); + color: var(--color-ink); + font-optical-sizing: auto; + grid-column: 2; +} + +@media (max-width: 50rem) { + .faq__q { grid-column: 1; } +} + +.faq__a { + margin: 0.4rem 0 0; + font-family: var(--font-serif); + font-style: italic; + font-size: 0.9375rem; + line-height: var(--lh-normal); + color: var(--color-ink-2); + max-width: 64ch; + grid-column: 2; +} + +@media (max-width: 50rem) { + .faq__a { grid-column: 1; } +} + +.faq__a code { + font-family: var(--font-mono); + font-style: normal; + font-size: 0.875em; + background: var(--color-paper-2); + padding: 0.05em 0.35em; + border: 1px solid var(--color-rule); + border-radius: 2px; + color: var(--color-ink); +} + +.faq__a strong { + font-weight: 600; + color: var(--color-ink); +} + +.faq__a em { + font-style: italic; + font-weight: 500; + color: var(--color-ink); +} + +[data-theme="brutal"] .faq__a, +[data-theme="manifesto"] .faq__a, +[data-theme="terminal"] .faq__a, +[data-theme="sport"] .faq__a, +[data-theme="almanac"] .faq__a, +[data-theme="quiet"] .faq__a, +[data-theme="bloom"] .faq__a { + font-family: var(--font-body); + font-style: normal; +} + +[data-theme="manifesto"] .faq__q, +[data-theme="brutal"] .faq__q, +[data-theme="sport"] .faq__q { text-transform: uppercase; } + +[data-theme="riso"] .faq__q { text-transform: lowercase; } + /* — 06 · Install pane —————————————————————————————— A single bordered "manual page" card with three numbered rows — I · Run, II · In, III · Then. Replaces the old 4-card grid. The diff --git a/site/css/tokens.css b/site/css/tokens.css index 2b9d31d..da8a72d 100644 --- a/site/css/tokens.css +++ b/site/css/tokens.css @@ -771,52 +771,98 @@ ────────────────────────────────────────────────────────────── */ [data-theme="quiet"] { - --color-paper: oklch(99.5% 0.002 90); /* barely-warm near-white */ - --color-paper-2: oklch(97% 0.003 90); - --color-paper-3: oklch(94% 0.004 90); - --color-rule: oklch(86% 0.003 240); - --color-rule-2: oklch(72% 0.003 240); - --color-muted: oklch(50% 0.004 240); - --color-neutral: oklch(38% 0.004 240); - --color-ink-2: oklch(22% 0.005 240); - --color-ink: oklch(16% 0.005 240); /* barely-cool near-black */ - --color-accent: oklch(16% 0.005 240); /* accent IS ink — no chroma */ - --color-accent-ink: oklch(16% 0.005 240); - --color-focus: oklch(16% 0.005 240); + --color-paper: oklch(100% 0 0); /* pure white */ + --color-paper-2: oklch(98.5% 0 0); /* card surface */ + --color-paper-3: oklch(96% 0 0); /* hover surface */ + --color-rule: oklch(91% 0 0); /* hairline */ + --color-rule-2: oklch(82% 0 0); /* prominent border */ + --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); /* near-black */ + --color-accent: oklch(15% 0 0); /* mono — accent IS ink */ + --color-accent-ink: oklch(15% 0 0); + --color-focus: oklch(60% 0.10 240); /* a single tiny blue for keyboard focus only */ - --font-display: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; - --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --font-display: "Geist", "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif; + --font-body: "Geist", "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif; --font-serif: ui-serif, Georgia, "Times New Roman", serif; - --font-label: ui-monospace, "SF Mono", Menlo, Consolas, monospace; - --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; + --font-label: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; + --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; --display-weight: 500; --display-style: normal; - /* Average-sized headers — much smaller than other themes' 3.5–8rem display */ - --text-display: clamp(1.75rem, 2.5vw + 0.5rem, 2.75rem); - --text-display-s: clamp(1.5rem, 2vw + 0.25rem, 2rem); - --text-2xl: 1.625rem; - --text-xl: 1.25rem; - --text-lg: 1.125rem; + /* A confident, large display — minimalism with conviction, not timidity. */ + --text-display: clamp(2.5rem, 5vw + 0.5rem, 4.75rem); + --text-display-s: clamp(2rem, 3vw + 0.5rem, 3rem); + --text-2xl: clamp(1.75rem, 2vw + 0.5rem, 2.5rem); + --text-xl: 1.5rem; + --text-lg: 1.25rem; - --tracking-display: -0.02em; - --tracking-tight: -0.01em; - --tracking-label: 0.06em; + --tracking-display: -0.025em; + --tracking-tight: -0.015em; + --tracking-label: 0.04em; - --lh-tight: 1.15; - --lh-snug: 1.4; - --lh-normal: 1.6; - --lh-relaxed: 1.75; + --lh-tight: 1.08; + --lh-snug: 1.25; + --lh-normal: 1.55; + --lh-relaxed: 1.7; /* Generous whitespace — the page breathes */ - --section-gap: 12rem; + --section-gap: 10rem; --section-head-gap: 2.5rem; - --space-2xl: 6rem; - --space-3xl: 9rem; - --space-4xl: 14rem; + --space-2xl: 4.5rem; + --space-3xl: 7rem; + --space-4xl: 11rem; - --measure: 60ch; + --measure: 64ch; +} + +/* ────────────────────────────────────────────────────────────── + BLOOM — atmospheric dark · warm radial bloom · expressive + For the "AI-creative product" page (think Suno, Runway, music + tools). Dark canvas, two soft colour blooms behind the content, + confident sans display, single warm accent. The first non-print + theme in the catalog. + ────────────────────────────────────────────────────────────── */ + +[data-theme="bloom"] { + --color-paper: oklch(8% 0.012 35); /* near-black, hint warm */ + --color-paper-2: oklch(11% 0.014 35); /* elevated surface */ + --color-paper-3: oklch(14% 0.018 38); /* hover / inset */ + --color-rule: oklch(22% 0.018 38); + --color-rule-2: oklch(34% 0.022 40); + --color-muted: oklch(58% 0.018 40); + --color-neutral: oklch(74% 0.012 40); + --color-ink-2: oklch(86% 0.008 40); + --color-ink: oklch(98% 0.005 40); + --color-accent: oklch(72% 0.190 45); /* warm orange/amber */ + --color-accent-ink: oklch(72% 0.190 45); + --color-accent-2: oklch(64% 0.220 18); /* secondary pink/red bloom */ + --color-focus: oklch(72% 0.190 45); + + --font-display: "Geist", "Inter", ui-sans-serif, system-ui, sans-serif; + --font-body: "Geist", "Inter", ui-sans-serif, system-ui, sans-serif; + --font-serif: "Instrument Serif", ui-serif, Georgia, serif; + --font-label: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; + --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; + + --display-weight: 600; + --display-style: normal; + + --text-display: clamp(3rem, 6vw + 1rem, 6rem); + --text-display-s: clamp(2rem, 3vw + 1rem, 3.5rem); + + --tracking-display: -0.035em; + --tracking-tight: -0.02em; + --tracking-label: 0.10em; + + --lh-tight: 1.04; + --lh-snug: 1.18; + + --section-gap: 9rem; + --section-head-gap: 2rem; } /* ────────────────────────────────────────────────────────────── @@ -840,5 +886,8 @@ [data-theme="pastel"] { --radius-card: 12px; --radius-pill: 999px; --radius-input: 8px; --shadow-card: 0 8px 24px -10px oklch(50% 0.13 282 / 0.14); } [data-theme="riso"] { --radius-card: 2px; --radius-pill: 2px; } -[data-theme="quiet"] { --radius-card: 4px; --radius-pill: 4px; --shadow-card: 0 1px 2px oklch(20% 0.005 240 / 0.05); } +[data-theme="quiet"] { --radius-card: 8px; --radius-pill: 999px; --radius-input: 8px; } +[data-theme="bloom"] { --radius-card: 16px; --radius-pill: 999px; --radius-input: 12px; + --shadow-card: 0 24px 48px -16px oklch(8% 0.04 35 / 0.6), + 0 1px 2px oklch(8% 0.04 35 / 0.4); } diff --git a/site/index.html b/site/index.html index f0ce5f8..084d6c0 100644 --- a/site/index.html +++ b/site/index.html @@ -46,12 +46,22 @@
@@ -133,6 +152,13 @@ Indie podcast · warm cream, mono masthead Open ↗ + @@ -145,6 +171,13 @@ Open-source CLI · prose-led docs, embedded terminal Open ↗ + @@ -157,6 +190,13 @@ Artisan bakery · market-board layout, hand-built SVG Open ↗ + @@ -169,6 +209,13 @@ Studio manifesto · dark paper, yellow strike Open ↗ + @@ -181,6 +228,13 @@ Observability SaaS · dark paper, sticky trace panel Open ↗ + @@ -193,6 +247,13 @@ Software architect personal site · sticky TOC, pure white Open ↗ + @@ -205,6 +266,13 @@ SOC2 compliance SaaS · six-tile bento, deep-red accent Open ↗ + @@ -217,12 +285,19 @@ Cohort-based courses · stat-led, three supporting figures Open ↗ + - +
-
-
-
Without Hallmark
-
-

✨ Introducing

-

Notes that think with you.

-

A writing tool that organizes your ideas as you draft.

- Try free + + + + + +
+ + + + +
+ +
+ +
+
+
Without Hallmark
+
+

✨ Introducing

+

Notes that think with you.

+

A writing tool that organizes your ideas as you draft.

+ Try free +
+

+ Centred everything. Purple-to-pink gradient on the headline. Inter top to bottom. A pill button with a gradient fill. A page every LLM has produced ten thousand times. +

-

- Centred everything. Purple-to-pink gradient on the headline. Inter top to bottom. A pill button with a gradient fill. A page every LLM has produced ten thousand times. -

-
-
-
With Hallmark
-
-

Introducing

-

Notes that think with you.

-

A writing tool that organizes your ideas as you draft.

- Try free → +
+
With Hallmark
+
+

Introducing

+

Notes that think with you.

+

A writing tool that organizes your ideas as you draft.

+ Try free → +
+

+ Left-aligned, asymmetric. A display face with character set against a clean body. One small accent on the eyebrow only. A square-edged button. Same words — a page that knows what it is. +

-

- Left-aligned, asymmetric. A display face with character set against a clean body. One small accent on the eyebrow only. A square-edged button. Same words — a page that knows what it is. -

+ +
+
+
Without Hallmark
+
+

📊 Overview

+

1,284

+

Active sessions today

+ ↑ 12% +

Up from yesterday. Across 47 regions.

+ View report +
+

+ A glassmorphic widget. Gradient sparkline. Floating green-glow badge. Pill button with gradient fill. Centred on a softly-shadowed card. The AI-default dashboard. +

+
+
+
With Hallmark
+
+

Overview

+

+ 1,284 + ↑ 12% +

+

Active sessions today.
Up from yesterday — across 47 regions.

+ View report → +
+

+ Hairline rule above the figure. Mono tabular numerals at display size. Inline 12% with a single accent dot, no glow. Asymmetric, left-aligned. Square-edge CTA. Information, not decoration. +

+
+
+ +
+
+
Without Hallmark
+
+ ✨ Most popular +

Starter

+

$24/month

+

For solo writers and side projects.

+
    +
  • ✅ 5,000 words / month
  • +
  • ✅ Unlimited drafts
  • +
  • ✅ Email support
  • +
+ Choose Starter +
+

+ Glassmorphic centred card. "Most popular" badge with sparkle emoji. Gradient on the price. Emoji checkmarks. Pill button with gradient fill. The B2B SaaS pricing card the LLM ships every time. +

+
+
+
With Hallmark
+
+

Recommended

+

Starter

+

+ $24 + / month +

+

For solo writers and side projects.

+
    +
  • 5,000 words a month
  • +
  • Unlimited drafts
  • +
  • Reply-by-Wednesday email support
  • +
+ Choose Starter → +
+

+ Tier name set in display face above a hairline rule. Mono tabular price. No gradients, no glow, no emoji. Plain bullets, plain rules. The price is the design. +

+
+
+ +
+
+
Without Hallmark
+
+ +

Anya Reyes

+

Software architect & distributed-systems builder.

+
+ About + Work + Contact +
+
+

+ Centred avatar bubble. Gradient on the name. Three pill nav buttons in a row. Inter top to bottom. The portfolio template the LLM falls back to when it doesn't know who you are. +

+
+
+
With Hallmark
+
+

About · 01

+

Anya Reyes.

+

Software architect, ten years on distributed systems. Currently building Streampipe. Writes about latency and the small print of caches.

+ +
+

+ No avatar. Italic name with a roman emphasis on the surname. Numbered eyebrow (01) like a chapter mark. Body serif bio that reads like a letter. Plain text links separated by middots. A page that knows its subject. +

+
+
+
- +

- Eight things Hallmark holds the line on — explained without the jargon. + Five things Hallmark holds the line on — explained without the jargon.

@@ -352,58 +560,12 @@ -
+
- States + Voice F / 05
-

Buttons and inputs need real feedback for every state — hover, press, focus, loading, error, success — not just on and off. Hit targets big enough to actually tap.

-
-
- -

default

-
-
-
- -
-
- Responsive - F / 06 -
-

Mobile first. Add breakpoints where the content starts to look wrong, not where some device used to sit.

-
- -
-
- -
-
- Copy - F / 07 -
-

Write like a person. Specific verbs over vague ones. Link text that makes sense out of context. Real apostrophes, not straight quotes.

+

Write like a person. Specific verbs over vague ones. Link text that makes sense out of context. Real apostrophes, not straight quotes. No "seamless," no "unleash," no "built for the modern team."

@@ -411,97 +573,66 @@

Click here

Get started

Built for the modern team

+

Unleash the future

Specific

View pricing plans

Save changes

The email API for developers

+

Notes that think with you

-
-
- Anti-patterns - F / 08 -
-

Twenty-one named tells the AI keeps reaching for. Run hallmark audit to find them, hallmark refine to fix them in place.

-
- -
-
-
- -
+ +

- Install once. Ask for what you need. Hallmark handles the rest. + The questions people actually ask.

-
+
    -
    - I -
    -

    Install the skill

    -

    One command. Works in Claude Code, Cursor, and Codex out of the box.

    -
    -
    -
    $npx skills add hallmark
    -
    -
    +
  1. +

    Q ⁄ 01

    +

    Does it work with Cursor and Codex?

    +

    Yes. Drop the body of SKILL.md into .cursor/rules/hallmark.mdc for Cursor, or copy the skill/ folder into your Codex agent config. Claude Code picks it up at ~/.claude/skills/hallmark/.

    +
  2. -
    - II -
    -

    Ask for a UI

    -

    In plain language. Hallmark asks a few questions first — who's it for, what's it for, what tone — so the page actually fits the brief.

    -
    -
    -
    > build a landing page for a local bookshop's
    -  new poetry collection. editorial tone,
    -  for literary-minded customers.
    -
    -
    +
  3. +

    Q ⁄ 02

    +

    Can I disable a specific rule?

    +

    Most rules are universal — typography discipline, accessibility, anti-purple-text-gradient. Genre-specific rules can be loosened by picking a different genre: atmospheric allows dark-canvas radial gradients that editorial bans; modern-minimal allows pure white that editorial bans.

    +
  4. -
    - III -
    -

    Five verbs: build, audit, refine, redesign, study

    -

    The default verb builds. hallmark audit scores existing code against the - named tells. hallmark refine polishes in place. hallmark redesign rethinks the - structure. hallmark study reads a screenshot you admire and extracts what makes it work — the structure, the type pairing, the colour anchor — never the pixels.

    -
    -
    -
    $ claude "hallmark study ./reference.png"
    -  → Macrostructure: Split Studio
    -  → Hero: H2 Split (6/6, proof column)
    -  → Type: italic editorial serif + grotesque
    -  → Theme candidate: Studio
    -
    -
    +
  5. +

    Q ⁄ 03

    +

    What if I want my brand colours, not the catalog?

    +

    Mention them in the brief, or attach a brand colour. Hallmark's custom-theme route builds an OKLCH palette tuned to your anchor and pairs free fonts to it — full rules still apply, just on your colours.

    +
  6. -
+
  • +

    Q ⁄ 04

    +

    What does "AI-slop" mean exactly?

    +

    Twenty-one named tells the LLM reaches for by default — purple-gradient hero, centred-everything, icon-tile feature row, glass card, gradient headline, pill button with gradient fill. Run hallmark audit on any page to see them flagged.

    +
  • + +
  • +

    Q ⁄ 05

    +

    Five verbs — what do they do?

    +

    hallmark (default) builds. hallmark audit scores existing code. hallmark refine polishes in place. hallmark redesign rethinks structure — and on a whole project, writes a design.md first so every page shares the system. hallmark study reads a screenshot you admire and extracts its DNA, never the pixels.

    +
  • + +
    @@ -759,7 +890,7 @@

    Catalog

      -
    • 16 themes
    • +
    • 17 themes
    • 21 macrostructures
    • 32 archetypes
    • 38 slop-test gates
    • diff --git a/site/js/main.js b/site/js/main.js index 814db70..ff883ac 100644 --- a/site/js/main.js +++ b/site/js/main.js @@ -41,6 +41,7 @@ const THEMES = { pastel: "Pastel", riso: "Riso", quiet: "Quiet", + bloom: "Bloom", }; const STORAGE_KEY = "hallmark-theme"; @@ -64,7 +65,36 @@ const ARCHETYPES = { studio: { hero: "photographic", footer: "index" }, pastel: { hero: "clipped", footer: "masthead" }, riso: { hero: "quote-led", footer: "dense" }, - quiet: { hero: "letter", footer: "inline" }, + quiet: { hero: "split", footer: "masthead" }, + bloom: { hero: "marquee", footer: "masthead" }, +}; + +/* — Theme → genre map —————————————————————————————————— + Each theme belongs to one of four genres — a rule-set overlay that + scopes which slop-test gates apply and which voice fixtures the + skill picks from. See skill/references/genres/. */ +const THEME_GENRES = { + // editorial — the canonical Hallmark voice (12 themes) + specimen: "editorial", + newsprint: "editorial", + atelier: "editorial", + garden: "editorial", + salon: "editorial", + linen: "editorial", + almanac: "editorial", + studio: "editorial", + riso: "editorial", + sport: "editorial", + brutal: "editorial", + manifesto: "editorial", + // modern-minimal — Stripe / Linear / ElevenLabs school + quiet: "modern-minimal", + // atmospheric — Suno / Runway / dark-AI-tool school + bloom: "atmospheric", + midnight: "atmospheric", + terminal: "atmospheric", + // playful — post-Linear soft school + pastel: "playful", }; /* — Locked hero title ————————————————————————————————— @@ -380,25 +410,46 @@ const COPY = { captionB: "press · 04", }, quiet: { - eyebrow: "v0.4", + eyebrow: "Polished minimal", title: HERO_TITLE, - lede: "System fonts. Near-white paper. Average-sized headers. A theme that argues against itself.", - ctaLabel: "Install", - proofLabel: "Notes", - proofA: "System-native typography", - proofB: "No chromatic accent", - proofC: "No reveal animation", - cta: "Read on", - stat: "16", - qualifier: "themes, one of them is this.", - mockStat: "16", - quote: "Restraint, when it's the brief, is the design.", + lede: "Geist sans. Pure white. One bold display. Generous space. The design decides what to leave out — and stands behind those choices.", + ctaLabel: "Get started", + proofLabel: "Decisions", + proofA: "Pure-white paper, dark ink", + proofB: "Geist sans, single weight", + proofC: "Pill CTAs · monochrome accent", + cta: "Get started", + stat: "1", + qualifier: "decision, made everywhere.", + mockStat: "1", + quote: "The work that looks effortless is the work where the choices were made.", attrib: "Quiet", salutation: "Hello.", - letterBody: "This is a page that doesn't try. The font is whatever your operating system gives. The accent colour is the same as the text. The headlines are average-sized. The whitespace is generous. There is nothing here to surprise you, and that's the design.", + letterBody: "A theme for the modern enterprise page — the Stripe / Linear / ElevenLabs school of restraint. Clean white, confident typography, pill CTAs. Minimalism with conviction, not absence.", signoff: "Yours,", - captionA: "Plain", - captionB: "Quiet", + captionA: "Quiet", + captionB: "v0.8", + }, + bloom: { + eyebrow: "Atmospheric · 2026", + title: HERO_TITLE, + lede: "For the AI-creative product page. Dark canvas, warm bloom, declarative type. The aesthetic of a tool you'd actually want to use after dark.", + ctaLabel: "Try it now", + proofLabel: "Atmosphere", + proofA: "Dark canvas with two warm blooms", + proofB: "Geist sans, one weight, plain English", + proofC: "Single warm accent — never gradient text", + cta: "Try it now", + stat: "1", + qualifier: "warm canvas — many uses.", + mockStat: "1", + quote: "The page should feel like a place you could sit in.", + attrib: "Bloom note", + salutation: "Welcome,", + letterBody: "A dark theme for the AI-creative tool page — Suno, Runway, the late-night software where atmosphere matters. Two soft colour blooms, plain confident type, a single warm accent. Restraint of a different kind.", + signoff: "— Bloom", + captionA: "Bloom", + captionB: "Late-night", }, }; @@ -441,7 +492,7 @@ function buildDenseColophon(themeName) { `# hallmark · v0.7.0 · ${themeName.toLowerCase()}`, `# build: ${today} · MIT · powered by together ai`, `#`, - `# stats: 21 macrostructures · 32 archetypes · 16 themes · 38 gates`, + `# stats: 21 macrostructures · 32 archetypes · 17 themes · 38 gates`, `# catalog (16 named) + custom (per-brand, opt-in)`, `#`, `# repo: github.com/Luffixos/hallmark`, @@ -512,12 +563,28 @@ function attachCopyButtons(scope = document) { attachCopyButtons(); /* — Theme application ————————————————————————————————— */ +/* Cached banner subnodes — populated once at startup. */ +const themeLabelEl = document.querySelector(".banner__theme"); +const themeGenreEl = document.querySelector("[data-theme-genre]"); +const stampTextEl = document.querySelector("[data-stamp-text]"); +const stampDetailEl = document.querySelector("[data-stamp-detail]"); + function setPressed(theme) { dots.forEach((btn) => { const active = btn.dataset.themeBtn === theme; btn.setAttribute("aria-pressed", active ? "true" : "false"); }); - if (currentLabel) currentLabel.textContent = THEMES[theme] || "Specimen"; + const themeName = THEMES[theme] || "Specimen"; + const genre = THEME_GENRES[theme] || "editorial"; + const archetype = (ARCHETYPES[theme] && ARCHETYPES[theme].hero) || "marquee"; + + if (themeLabelEl) themeLabelEl.textContent = themeName; + if (themeGenreEl) themeGenreEl.textContent = genre; + if (stampTextEl) stampTextEl.textContent = `${archetype} · ${theme} · enrich-A`; + if (stampDetailEl) stampDetailEl.textContent = `/* Hallmark · genre: ${genre} · macrostructure: ${archetype} · theme: ${theme} · enrichment: A */`; + + // Fallback for older callers — keep the public theme-current span up to date. + if (currentLabel && !themeLabelEl) currentLabel.textContent = themeName; } function applyTheme(theme) { @@ -567,6 +634,55 @@ if (shuffleBtn) { shuffleBtn.addEventListener("click", () => applyTheme(pickRandomTheme())); } +/* — T-key onboarding tooltip ———————————————————————————————— + First-time visitors don't know T cycles themes. After ~5s of no T + presses (and only if they haven't seen the tooltip before), fade + it in near the shuffle button. Dismisses on first T press, on + click, or after 8s of being shown. localStorage flag is set on + dismiss so it never returns. */ +const T_TOOLTIP_KEY = "hallmark-t-tooltip-seen"; +const T_TOOLTIP_DELAY_MS = 5000; +const T_TOOLTIP_AUTO_HIDE_MS = 8000; +const T_TOOLTIP_FADE_MS = 240; +const tTooltipEl = document.querySelector("[data-t-tooltip]"); +let tTooltipShown = false; +let tTooltipTimer = null; +let tTooltipAutoHideTimer = null; + +function tTooltipSeen() { + try { return localStorage.getItem(T_TOOLTIP_KEY) === "1"; } catch (e) { return false; } +} + +function markTTooltipSeen() { + try { localStorage.setItem(T_TOOLTIP_KEY, "1"); } catch (e) { } +} + +function showTTooltip() { + if (!tTooltipEl || tTooltipShown || tTooltipSeen()) return; + tTooltipShown = true; + tTooltipEl.hidden = false; + delete tTooltipEl.dataset.state; + clearTimeout(tTooltipAutoHideTimer); + tTooltipAutoHideTimer = setTimeout(hideTTooltip, T_TOOLTIP_AUTO_HIDE_MS); +} + +function hideTTooltip() { + if (!tTooltipEl || !tTooltipShown) return; + clearTimeout(tTooltipAutoHideTimer); + tTooltipEl.dataset.state = "closing"; + setTimeout(() => { + tTooltipEl.hidden = true; + delete tTooltipEl.dataset.state; + tTooltipShown = false; + markTTooltipSeen(); + }, T_TOOLTIP_FADE_MS); +} + +if (tTooltipEl && !tTooltipSeen()) { + tTooltipTimer = setTimeout(showTTooltip, T_TOOLTIP_DELAY_MS); + tTooltipEl.addEventListener("click", hideTTooltip); +} + /* — Easter egg — "chill, designer." ———————————————————————— Spam T fast enough and the page intervenes. We track timestamps in a rolling 3.2s window; if the user crosses the threshold (≈ a full @@ -583,7 +699,7 @@ const EASTER_COOLDOWN_MS = 15000; const EASTER_PUNCHLINES = [ "chill, designer.", "you've seen them all.", - "sixteen is plenty.", + "seventeen is plenty.", "pick. build. ship.", "easy on the keyboard.", "one theme will do.", @@ -655,6 +771,10 @@ document.addEventListener("keydown", (e) => { if (e.key === "t" || e.key === "T") { e.preventDefault(); + // Dismiss the onboarding tooltip on first T press. + clearTimeout(tTooltipTimer); + if (tTooltipShown) hideTTooltip(); + else markTTooltipSeen(); // Easter-egg counter — track press cadence in a rolling window. // Push BEFORE applying the theme so the trigger fires on this same // keystroke if we've crossed the threshold. diff --git a/skill/SKILL.md b/skill/SKILL.md index 3bceaf4..0c73681 100644 --- a/skill/SKILL.md +++ b/skill/SKILL.md @@ -30,7 +30,7 @@ Hallmark has one default behaviour and four explicit verbs. If the user types anything that does not clearly map to `audit`, `refine`, `redesign`, or `study`, treat it as default. If the user attaches an image without a verb prefix, ask: *"Should I `study` this (extract the DNA), or should I treat it as a reference for a fresh build?"* -The default Design flow always picks a theme. By default it picks one of the **16 named themes** — the *catalog* — and rotates among them per the diversification rule. There is also a quiet *custom* branch that constructs a one-off OKLCH palette + free-font pairing for the brief; the custom route fires **only when the brief carries a creative-intent signal** (the user names a brand colour, names a multi-attribute vibe the catalog can't carry, or explicitly asks for a custom theme). For vanilla briefs, the user never sees the words "catalog" or "custom" — the catalog runs silently. See Step 1 (signal detection) and Step 2.6 (dispatch); the protocol lives in [`references/custom-theme.md`](references/custom-theme.md). +The default Design flow always picks a theme. By default it picks one of the **17 named themes** — the *catalog* — and rotates among them per the diversification rule. There is also a quiet *custom* branch that constructs a one-off OKLCH palette + free-font pairing for the brief; the custom route fires **only when the brief carries a creative-intent signal** (the user names a brand colour, names a multi-attribute vibe the catalog can't carry, or explicitly asks for a custom theme). For vanilla briefs, the user never sees the words "catalog" or "custom" — the catalog runs silently. See Step 1 (signal detection) and Step 2.6 (dispatch); the protocol lives in [`references/custom-theme.md`](references/custom-theme.md). --- @@ -40,8 +40,9 @@ The default Design flow always picks a theme. By default it picks one of the **1 If the project already has code — a `package.json`, a `tailwind.config.*`, an `index.html`, any CSS — Hallmark should **read it before asking the user anything**. Stomping on an established palette or font stack is the difference between a skill the user keeps and a skill the user uninstalls. -**Five signal sources, scanned in order:** +**Six signal sources, scanned in order:** +0. **`design.md`** — at the project root (or `DESIGN.md`). If present, this is the **locked design system for the project** — written by a previous `hallmark redesign` run on the whole app, or by hand. **Read it first; it overrides everything else.** Subsequent picks (genre, theme, type, motion) defer to it. The diversification rule is *inverted* on `design.md`-managed projects: pages must share the system, not differ from each other. See [`verbs/redesign.md`](references/verbs/redesign.md) § Multi-page flow for how the file is produced and amended. 1. **Font stack** — `package.json` for `next/font`, `@fontsource/*`, `expo-google-fonts`, `geist`; any `` in HTML / layout files; `tailwind.config.{js,ts}` `theme.extend.fontFamily`; `@import url("fonts.googleapis.com/...")` in any stylesheet. 2. **Palette** — OKLCH / HSL / hex values inside `:root` blocks; `tailwind.config` `theme.extend.colors`; any `tokens.json`, `design-tokens.{json,yaml}`, or DTCG-shaped file. 3. **Microinteraction stance** — `package.json` dependencies for `framer-motion`, `gsap`, `motion`, `lenis`, `lottie-react`, `@react-spring/*`, `auto-animate`. Any one of those = "motion-on" project. None = "motion-cut" project. @@ -73,6 +74,7 @@ If the cache is re-used, emit a one-line note instead of the full block: *"Pre-f **Edge cases:** +- **`design.md` found** → emit *"`design.md` detected at project root — this is a system-managed project. Reading the locked design system; subsequent picks defer to it."* Then read the file in full and use it as the source of truth for genre / theme / typography / spacing / motion / CTA voice. Skip Step 1's catalog/custom dispatch; the system is already chosen. Proceed to macrostructure pick (Step 2) within the family `design.md` allows for this page's type. - **No signals found** (vanilla HTML project, empty repo, scratch directory) → silent. One line only: *"No pre-flight signals — proceeding with full Hallmark stack."* - **Conflicting signals** (e.g. `framer-motion` installed but no `motion.div` usage anywhere; or `Geist` import in `package.json` but `font-family: Inter` hard-coded in CSS) → flag the conflict explicitly: *"Conflict: Geist imported via next/font but a hard-coded `font-family: Inter` in app/globals.css L4. I'll preserve next/font Geist; please confirm or remove the Inter declaration."* - **Empty project** (no `package.json`, no `index.html`) → silent. @@ -99,16 +101,26 @@ Hallmark works best when you know three things before writing code: **Ask once, then commit.** If any of the three is missing, ask for all missing items in **one** short message — not one at a time, not in a follow-up. Offer the user an opt-out at the end of that message: *"or say 'go ahead' and I'll infer from the brief — I'll tell you what I picked."* -**Theme route — only surface when the brief signals it.** Hallmark has two theme routes: **catalog** (the 16 named themes — Specimen, Atelier, Pastel, Brutal, Salon, Newsprint, Linen, Studio, Manifesto, Plain, Terminal, Midnight, Almanac, Garden, Quiet, Riso) and **custom** (an OKLCH palette + free-font pairing tuned to this one brief). **Catalog is the default.** Do **not** offer the user a choice on every prompt — that's friction, not discipline. Surface the route only when the brief carries one of these signals: +**Genre — pick before themes.** Before the theme route, settle on a genre. Hallmark ships four: **editorial** (default · the canonical anti-slop voice), **modern-minimal** (Stripe / Linear / ElevenLabs school), **atmospheric** (Suno / Runway / dark-AI-tool school), **playful** (post-Linear soft school). The genre scopes which themes can rotate, which slop-test gates apply, and which voice fixtures the LLM picks from. Detection is signal-based — silent default to editorial unless the brief fires one of these: + +- *AI tool, generative, music, video, voice, late-night, dark mode, atmospheric* → **atmospheric** → load [`references/genres/atmospheric.md`](references/genres/atmospheric.md) +- *SaaS, enterprise, API, platform, developer tool, infra, B2B, dev experience* → **modern-minimal** → load [`references/genres/modern-minimal.md`](references/genres/modern-minimal.md) +- *fun, consumer, casual, friendly, onboarding, family, community* → **playful** → load [`references/genres/playful.md`](references/genres/playful.md) + +If two non-default signals fire (rare), ask one short follow-up: *"This brief fits both modern-minimal and atmospheric — which feels closer? \[modern-minimal · atmospheric]"*. Default with no signal: silent **editorial** → load [`references/genres/editorial.md`](references/genres/editorial.md). The chosen genre file is loaded eagerly (it scopes everything downstream); other genre files stay on disk. + +State the genre out loud at Step 2.5 alongside the macrostructure and theme picks: *"Genre: atmospheric. Macrostructure: Marquee Hero. Theme: Bloom (atmospheric cluster)."* + +**Theme route — only surface when the brief signals it.** Hallmark has two theme routes: **catalog** (the 17 named themes — Specimen, Atelier, Pastel, Brutal, Salon, Newsprint, Linen, Studio, Manifesto, Terminal, Midnight, Almanac, Garden, Quiet, Riso, Sport, Bloom) and **custom** (an OKLCH palette + free-font pairing tuned to this one brief). **Catalog is the default.** The catalog rotation is *scoped to the genre's theme cluster* — atmospheric rotates Bloom/Midnight/Terminal, modern-minimal stays on Quiet, playful stays on Pastel, editorial walks the remaining twelve. Do **not** offer the user a choice on every prompt — that's friction, not discipline. Surface the catalog/custom fork only when the brief carries one of these signals: - The user explicitly says **custom theme** / **tailored to our brand** / **make it ours** / **something unique** / **play with the colors and fonts**. - The user names a **specific brand colour** as the anchor (e.g., "use our terracotta", "the brand red is hex #c0392b", "anchor on sea-blue"). - The user describes a **multi-attribute aesthetic that doesn't map to a single catalog theme** — three or more vibe words pointing at a specific feel (e.g., "moss, lichen, soft pink, herbal" / "sun-drenched, market-day, carbon-black" / "late-night, neon, brutalist deli"). One adjective ("warm", "technical", "playful") is *not* a custom signal — that's a tone, and the catalog already carries it. - The user attaches a **brand-mood reference** (a colour swatch, a moodboard, a Pantone chip) without asking to study a screenshot. -If any of those fires, ask one short follow-up before picking: *"This brief reads like a custom palette would fit better than the 16 named themes. Want me to construct a custom OKLCH palette + free-font pairing tuned to , or stay on the catalog for variety + speed?"* Wait for the user to say custom (or catalog). Default is still catalog — silence routes to catalog, not custom. +If any of those fires, ask one short follow-up before picking: *"This brief reads like a custom palette would fit better than the catalog. Want me to construct a custom OKLCH palette + free-font pairing tuned to , or stay on the catalog for variety + speed?"* Wait for the user to say custom (or catalog). Default is still catalog — silence routes to catalog, not custom. -If none of the signals fires, **proceed with catalog silently. Do not mention the fork.** Most briefs don't need a custom theme — the catalog's 16 themes plus the rotation rule already deliver structural variety. See Step 2.6 for the dispatch. +If none of the signals fires, **proceed with catalog silently. Do not mention the fork.** Most briefs don't need a custom theme — the catalog's 17 themes plus the rotation rule already deliver structural variety. See Step 2.6 for the dispatch. **If the user opts out** (says "go ahead", "you pick", "skip", "just build it", "don't ask", or simply doesn't engage with the question after one prompt): @@ -189,10 +201,10 @@ The rotation block keeps the user inside the discipline without making them read By the time you reach this step, one of three things is true: 1. **The user named custom** (because they said so, or because Step 1's signal detection fired and they confirmed) → load [`references/custom-theme.md`](references/custom-theme.md), ask the **one** follow-up (vibe in 4–8 words + optional anchor colour), construct the OKLCH palette + free-font pairing, compute the three axis values (paper-band / display-style / accent-hue), then continue to Step 3. -2. **The user named catalog** (or implicitly accepted it by not naming custom) → pick one of the 16 named themes per the diversification rule above. Existing flow — continue to Step 3. +2. **The user named catalog** (or implicitly accepted it by not naming custom) → pick one of the 17 named themes per the diversification rule above. Existing flow — continue to Step 3. 3. **Neither was discussed** (Step 1's signals didn't fire — vanilla brief) → default to **catalog**. Do not pause. Do not ask. Continue to Step 3. -**Custom is a quiet branch, not a default question.** Most briefs route to catalog and the user never sees the words "catalog" or "custom." The 16 named themes plus the rotation rule already deliver structural variety; the fork is reserved for when the brief specifically asks for a tuned look the catalog can't carry. +**Custom is a quiet branch, not a default question.** Most briefs route to catalog and the user never sees the words "catalog" or "custom." The 17 named themes plus the rotation rule already deliver structural variety; the fork is reserved for when the brief specifically asks for a tuned look the catalog can't carry. A custom theme is a **complete** OKLCH palette + font pairing tuned to the brief — not a one-off colour swap, not an excuse to bypass the rules. Every constraint in [`color.md`](references/color.md), [`typography.md`](references/typography.md), and [`anti-patterns.md`](references/anti-patterns.md) still applies. The 38 slop-test gates fire unchanged. The Step 5 preview block surfaces the palette + pairing in plain text **before** any code is emitted, so the user can redirect. @@ -200,28 +212,42 @@ The diversification rule is theme-route-blind: a custom run that follows another ### 3. Load the visual ruleset -The non-negotiables live in [`references/`](references/). Read only what you need: +The non-negotiables live in [`references/`](references/). **Be precise about what to load when.** Loading every file every time costs ~40k tokens; loading only what's needed costs ~15k. Discipline matters. +**Always-load (the eager bundle, ~3 files):** +- The genre file picked in Step 1 — [`genres/editorial.md`](references/genres/editorial.md), [`genres/modern-minimal.md`](references/genres/modern-minimal.md), [`genres/atmospheric.md`](references/genres/atmospheric.md), or [`genres/playful.md`](references/genres/playful.md). Scopes everything downstream. +- [`macrostructures.md`](references/macrostructures.md) — 21 named whole-page shapes; you must pick one before writing code (Step 2 already required this). +- [`component-cookbook.md`](references/component-cookbook.md) — 32 component archetypes (hero shapes, section heads, feature blocks, CTA shapes, testimonials, footers, navigations) composable into any macrostructure. + +**Load-per-build (universal rules — load every build):** - [`typography.md`](references/typography.md) — fonts, scale, pairing, weights, measure -- [`color.md`](references/color.md) — OKLCH, palette construction, accent discipline, dark mode -- [`layout-and-space.md`](references/layout-and-space.md) — 4pt scale, grid-breaks, asymmetry, depth -- [`macrostructures.md`](references/macrostructures.md) — twenty-one named whole-page shapes (Bento Grid, Long Document, Marquee Hero, Stat-Led, Workbench, etc.); pick one before writing code -- [`component-cookbook.md`](references/component-cookbook.md) — thirty-two component archetypes (six hero shapes, five section-head shapes, five feature blocks, four CTA shapes, four testimonials, four footers, four navigations) you can compose into any macrostructure -- [`structure.md`](references/structure.md) — the six primitive axes underlying the macrostructures, for when you need to deviate +- [`color.md`](references/color.md) — OKLCH, palette construction, accent discipline +- [`layout-and-space.md`](references/layout-and-space.md) — 4 pt scale, grid-breaks, asymmetry, depth - [`motion.md`](references/motion.md) — durations, easings, what to animate, reduced-motion -- [`microinteractions.md`](references/microinteractions.md) — per-interaction recipes (button press, focus, modal, toast, optimistic update, command palette, drag, copy-to-clipboard, search-as-you-type) and the named microinteraction tells -- [`interaction-and-states.md`](references/interaction-and-states.md) — the eight states, focus, hit-targets, forms -- [`responsive.md`](references/responsive.md) — mobile-first, content-driven breakpoints, safe areas - [`copy.md`](references/copy.md) — verbs, labels, error structure, link text - [`anti-patterns.md`](references/anti-patterns.md) — the named tells you must not emit -- [`hero-enrichment.md`](references/hero-enrichment.md) — when (and when not) to add a demo video / illustration / mockup / animated loop / abstract background to the hero, plus the eight enrichment archetypes (load when reaching Step 4) -- [`custom-craft.md`](references/custom-craft.md) — *how* to hand-build hero artwork: pure CSS art, hand-built SVG, declarative animation (`@property`, `animation-timeline`, View Transitions), JS-driven (Motion / GSAP), and when Three.js earns its place. Load only when an enrichment archetype requires construction. -- [`assets.md`](references/assets.md) — the sourcing catalogue: icons (Lucide / Phosphor / Heroicons / Tabler), brand logos (Simple Icons / SVGL), generated illustration (Nanobanana 2 / Recraft V4 / Midjourney), library illustration, app mockups, hero video, photography, abstract backgrounds, Lottie. Per-category rules and what to avoid. Load only when an enrichment archetype actually needs an external asset. -- [`study.md`](references/study.md) — vision-extraction protocol for the `hallmark study` verb (load only when that verb runs) -- [`study-examples.md`](references/study-examples.md) — three worked DNA-extractions (Pentagram-style portfolio · Klim-style specimen · Rauno-style personal site) showing the diagnose → confirm → build flow end-to-end. Load whenever `study` runs and the user wants to see what a good DNA-extraction looks like before pasting their own screenshot. -- [`recipes.md`](references/recipes.md) — eight worked briefs with the prompt, inferred trio, picked macrostructure + theme + enrichment, and a one-paragraph excerpt of the output. The first recipe (Coffeebox) is the canonical try-it prompt the README points users at. Load when the user asks "what does Hallmark output look like?" or wants a near-match for a brief in hand. -For most design work you need `macrostructures`, `component-cookbook`, `typography`, `color`, `layout-and-space`, and `anti-patterns`. **Load `microinteractions` whenever the output has *any* interactive element** — buttons, links, inputs, forms, modals, tabs, dropdowns, toasts, drag handles, command palettes, copy buttons, anything with hover/focus/active states. That is most pages. +**Load-conditionally (only when the page actually needs it):** +- [`microinteractions.md`](references/microinteractions.md) — load whenever the output has *any* interactive element (buttons, inputs, modals, tabs, dropdowns, toasts, drag handles, copy buttons). That is most pages. +- [`interaction-and-states.md`](references/interaction-and-states.md) — load when the page has stateful UI (forms, command palettes, optimistic updates). +- [`responsive.md`](references/responsive.md) — load when mobile is in scope. +- [`structure.md`](references/structure.md) — load only when deviating from a named macrostructure. +- [`hero-enrichment.md`](references/hero-enrichment.md) — load at Step 4 if the brief asks for enrichment. +- [`custom-craft.md`](references/custom-craft.md) — load only when an enrichment archetype requires construction (CSS art, SVG, declarative animation, etc.). +- [`assets.md`](references/assets.md) — load only when an enrichment archetype needs an external asset (icons, illustration, photography, Lottie). +- [`custom-theme.md`](references/custom-theme.md) — load only when Step 2.6 routes to custom. + +**Load-at-the-end:** +- [`slop-test.md`](references/slop-test.md) — load at Step 7 (the gate-check after build), not earlier. +- [`contract.md`](references/contract.md) — load at handoff time for output-contract + scope rules. + +**Verb-specific:** +- [`verbs/audit.md`](references/verbs/audit.md), [`verbs/refine.md`](references/verbs/refine.md), [`verbs/redesign.md`](references/verbs/redesign.md) — load only when that verb runs. +- [`study.md`](references/study.md) — load only when `hallmark study` runs. + +**Human-only (do NOT auto-load):** +- [`../docs/recipes.md`](../docs/recipes.md) — eight worked briefs for human readers. +- [`../docs/study-examples.md`](../docs/study-examples.md) — three worked DNA-extractions for human readers. ### 4. Decide on hero enrichment @@ -330,129 +356,29 @@ Always: ### 7. The slop test -Before handing back, run the output through these thirty-eight questions. Every answer must be **no**. Run this BEFORE writing the Slop test row in the Step 5 preview block — that row reflects the actual outcome of this step. +Before handing back, run the output through the 38-gate slop test in [`references/slop-test.md`](references/slop-test.md). Every answer must be **no**. Load that file at this step (not earlier — it isn't needed until handoff). The active genre matters: some gates are universal, some are genre-scoped (atmospheric loosens the radial-bloom gate; modern-minimal loosens the zero-chroma neutral gate; etc.). The full per-genre overrides are listed inline in `slop-test.md`. -**Visual:** +Run the slop test BEFORE writing the Slop test row in the Step 5 preview block — that row reflects the actual outcome of this step. -1. Is the display font Inter, Roboto, Open Sans, Poppins, Lato, or a system default? -2. Is there a purple-to-blue (or cyan-to-magenta) gradient anywhere? -3. Is there a 3-equal-column card grid with icon-above-heading tiles? -4. Is any card nested inside another card? -5. Is there a `background-clip: text` gradient headline? -6. Is any card using a thick coloured left/right side-stripe border? -7. Is the hero `min-height: 100vh` with everything centred? -8. Is pure `#000` or pure `#fff` used as a base colour anywhere? - -**Structural:** - -9. Does the page use the *same* structural fingerprint as the last page you built? (Hero → 3 features → CTA → footer is the AI structural template; reject it.) -10. Are sections separated only by equal whitespace, with no rule, no ornament, no colour shift — every section identical in rhythm? - -**Microinteractions:** - -11. Is `transition-all` (or `transition: all`) used anywhere? (Specify the properties.) -12. Is `hover:scale-105` (or any uniform hover-scale) applied across multiple unrelated elements? -13. Are bouncy / overshoot easings (`cubic-bezier(0.34, 1.56, ...)`, etc.) used on UI state changes — buttons, modals, tooltips? (Reserve overshoots for physical interactions only.) -14. Does any element have *more than one* hover effect at the same time (translate + scale + shadow + colour + rotate)? -15. Are you animating `width`, `height`, `top`, `left`, `margin`, or `padding` anywhere? -16. Does the focus ring transition into existence (fade in)? (Focus rings must appear instantly — keyboard users need an immediate indicator.) -17. Is there a celebratory success toast for an action whose effect the user can already see? (Silent success is taste; toasts are for failures and invisible effects.) -18. Are tooltip hover-delay and focus-delay equal? (Hover should delay 800–1000 ms; focus should be 0 ms.) -19. Is auto-rotating content (carousel, banner, stats) lacking pause-on-hover-and-focus? (WCAG 2.2.2.) -20. Is there a placeholder name "Jane Doe / John Smith" or a startup cliché (Acme, Nexus, Seamless, Unleash)? - -**Variety:** - -21. Is the `/* Hallmark · macrostructure: · ... */` stamp missing from the top of the CSS? (It must be present.) -22. Is the macrostructure I picked the same as a previous Hallmark output's stamp in this project? (Read the file system; if a stamp exists, mine must differ.) -23. Did I default to the **Specimen** macrostructure (numbered left-margin labels + huge serif + asymmetric spans + typographic-only CTA) when the brief did not explicitly call for editorial / foundry / specimen energy? (Specimen fall-through is banned.) - -**Implementation gates** (the rules that used to be advice; now they're checks): - -24. Does any neutral / surface colour have `oklch(... 0 ...)` (zero chroma)? Pure greys read as flat. Tint every neutral toward the anchor hue — minimum 0.005 chroma. -25. Does the accent colour cover more than ~5 % of any single viewport (count by area: solid fills, large headings in accent, full-bleed accent backgrounds)? If yes, retreat — accent is for emphasis, not for filling. -26. Is any padding / gap / margin a value that isn't on the named spacing scale (`--space-3xs` … `--space-5xl`, multiples of 4 px)? Arbitrary `padding: 17px` is a tell. -27. Is any prose container's `max-width` outside the 45–75 ch range? Measure must read; under 45 ch is choppy, over 75 ch loses the eye. -28. Does any interactive element lack `:focus-visible`, `:active`, OR `:disabled` styling? (Eight states is the rule. Default + hover is two; you need at least default + hover + focus-visible + active + disabled present in code.) -29. Is there any `transform` / `animation` keyframe that is NOT covered by a `@media (prefers-reduced-motion: reduce)` fallback? Every motion gets a reduced-motion alternative. - -**Hero enrichment gates** (when the page carries enrichment — see [`references/hero-enrichment.md`](references/hero-enrichment.md)): - -30. If the page has a demo video, does it autoplay with sound, lack a `poster`, lack `fetchpriority="high"`, or use `loading="lazy"` on the LCP element? (LCP-killers fail this gate.) -31. If the page has an abstract background, is it more than one accent colour, more than ~5 % footprint, or animating mesh-gradient on the whole page? (Aurora blobs and mesh-on-everything fail this gate.) -32. Does the page mix two or more icon libraries? (Material + Heroicons + Lucide on the same page = the icon-set tell.) -33. If the page has illustration, did I default to a Lottie library when a hand-built SVG or pure-CSS shape would have worked? (Lottie is last resort, not the default.) - -**Diversification gates** (cross-reference [`.hallmark/log.json`](#25-check-project-memory) when present): - -34. If I used the same archetype as a previous Hallmark output (per `.hallmark/log.json` or the latest macrostructure stamp), did I pick at least one different *variation knob*? Two Bento Grids with `tiles=6, spans=irregular, accent=corner-only` are the same Bento — the within-archetype knobs in [`component-cookbook.md`](references/component-cookbook.md) exist precisely to prevent that. State the knob deltas in the stamp. -35. Does any visual-only ``, custom-art `
      `, ``, or decorative figure lack `aria-label` or `aria-hidden="true"`? Hand-built CSS art and SVG illustrations need an accessible name *or* an explicit hide. Skipping this is the new accessibility tell. - -**Layout-safety gates** (the page must survive every viewport): - -36. Does the page horizontally scroll on any viewport between 320 px and 1920 px? Open the rendered page; drag the dev-tools width slider across that range. If a horizontal scrollbar appears at any width, fail. The fix is `html { overflow-x: clip; }` plus `body { overflow-x: clip; }` as a safety net for any clipped-edge enrichment that pushes past the viewport. Use `overflow-x: clip` (not `hidden`) — `clip` preserves `position: sticky` and `position: fixed` on descendants. (Cross-reference: [`layout-and-space.md` § Page-edge clipping](references/layout-and-space.md).) -37. For every decorative effect on text — highlighter `` / `` band / accent stroke / underline — did I visually confirm the position and size? A highlighter band must sit behind the x-height (`linear-gradient(180deg, transparent ~38%, accent ~38%, accent ~92%, transparent ~92%)`), **not** at the baseline (which reads as a fat underline). Underlines must be 1–2 px and offset 1–2 px from the baseline, never 5+ px. Decorative strokes must not exceed 5 % of the viewport (gate 25). The check is *visual*: imagine the rendered output and confirm the band lands in the right vertical zone. -38. Are interactive bars (nav, toolbar, command bar, hero CTA row, footer link strip) explicitly vertically centered? Default flex layouts inherit `align-items: stretch`, which makes a button taller than its sibling text and breaks the visual baseline. Every flex row mixing height-different elements (button + text, icon + text, mark + body) must declare `align-items: center` and `line-height: 1` on the items with intrinsic height. Inheriting `line-height: 1.55` from `html` fights the row's vertical rhythm. - -If any answer is yes, fix it. Do not ship slop. +If any gate fails, fix it. Do not ship slop. --- ## `hallmark audit` -Read the file(s) the user pointed at. For each finding, return: - -- **Tell** — the named anti-pattern from `anti-patterns.md`. -- **Where** — file path and line range. -- **Severity** — `critical` (ships as slop), `major` (looks AI-generated), `minor` (small taste issue). -- **Fix** — one-line concrete correction. - -Group by severity. Do not edit. Do not redesign. End with a count: `N critical · M major · K minor`. - -Audit *also* checks structural fingerprint: if the page uses the AI template (centered hero, 3 equal feature cards, CTA, footer, with no asymmetry or surprise), flag it as a critical structural finding even if the visual treatment is fine. - -**Stamp-vs-page check.** If the audited file contains a `/* Hallmark · macrostructure: · ... */` stamp, verify the page actually matches that name. If the stamp says **Bento Grid** but the page is a centered single-column hero with a CTA, flag it as a critical structural finding: `stamp lies` — the stamp must reflect what shipped or be removed. This catches drift where a previous Hallmark run stamped one thing and a later edit pulled the page back toward the AI template. +Load [`references/verbs/audit.md`](references/verbs/audit.md) and follow it. --- ## `hallmark refine` -The user has code they are happy with structurally but wants polished. Your job is to apply the ruleset with the smallest possible diff. - -- Do not move or rename elements unless necessary. -- Do not restructure the DOM. -- Do swap fonts, rewrite colour tokens to OKLCH, tighten the type scale, correct easings, add missing states, fix any flagged anti-patterns. -- At the end, list what you changed and which reference file prompted each change. +Load [`references/verbs/refine.md`](references/verbs/refine.md) and follow it. --- ## `hallmark redesign` -The user wants a different page from the same content. They are not happy with the current structure — typically because it reads as templated, generic, or AI-shaped. Your job is to throw the structure out and build a new one. - -**What to preserve:** -- The copy (every word, ideally) -- The information architecture (which sections exist, in roughly what order) -- The brand (colours and fonts they've named, if any) -- The primary action - -**What to replace:** -- The structural fingerprint — pick a **different** combination from `structure.md` than the source had. -- The component voice — different button style, different divider language, different image treatment. -- The reveal pattern — if the original faded everything in on scroll, the new one might have no reveals at all. -- The visual rhythm — different sections having different padding, different alignments, deliberate breaks. - -**Optional `--mood ` argument:** - -If the user specifies a mood (`hallmark redesign ./hero.tsx --mood luxury`), pick a tone aligned to that mood and let it drive the structural fingerprint. Mood names map to tones from `references/typography.md` and `references/structure.md`. If no mood is given, ask the user what *feeling* they want — one word — and proceed. - -**Output:** - -Return the redesigned code, plus a short note explaining: - -- The structural fingerprint you picked, axis by axis. -- Why this combination fits the brief better than the original. -- One thing you removed and why. +Load [`references/verbs/redesign.md`](references/verbs/redesign.md) and follow it. --- @@ -500,28 +426,9 @@ If `references/study.md` cannot be loaded for any reason, refuse the verb polite --- -## Output contract +## Output contract & scope -When producing new work: - -- Put design tokens in one place at the top of the stylesheet (`:root` custom properties) or in a `tokens.css` / `tokens.ts` file if the project uses one. -- Name tokens by semantic role, not value. `--color-ink`, not `--color-black`. -- If the project uses Tailwind, extend the theme; do not inline arbitrary values across components. -- If the project uses a framework, match the framework's file conventions — don't reinvent them. -- Include a short comment block at the top of the stylesheet naming the tone the user picked, the palette's anchor hue, and the structural fingerprint. This is the only comment you need. - ---- - -## Scope and limits - -Hallmark is a *taste* skill. It will not: - -- Invent product copy. If the user hasn't given you the words, ask. -- Pick a brand identity. It will follow one you give it. -- Enforce a specific style (dark mode, glassmorphism, brutalism). It will execute whichever tone the user committed to. -- Build logic — state management, data fetching, business rules. It is a visual / interaction layer only. - -If a request falls outside taste — "build the auth flow", "wire up Stripe" — do the work, but apply Hallmark to the rendered surface. +Load [`references/contract.md`](references/contract.md) once, at handoff time, for the full output contract and scope-of-skill rules. --- diff --git a/skill/references/contract.md b/skill/references/contract.md new file mode 100644 index 0000000..233273f --- /dev/null +++ b/skill/references/contract.md @@ -0,0 +1,24 @@ +# Output contract & scope + +Loaded once per build, at handoff time. + +## Output contract + +When producing new work: + +- Put design tokens in one place at the top of the stylesheet (`:root` custom properties) or in a `tokens.css` / `tokens.ts` file if the project uses one. +- Name tokens by semantic role, not value. `--color-ink`, not `--color-black`. +- If the project uses Tailwind, extend the theme; do not inline arbitrary values across components. +- If the project uses a framework, match the framework's file conventions — don't reinvent them. +- Include a short comment block at the top of the stylesheet naming the genre, the tone the user picked, the palette's anchor hue, and the structural fingerprint. This is the only comment you need. + +## Scope and limits + +Hallmark is a *taste* skill. It will not: + +- Invent product copy. If the user hasn't given you the words, ask. +- Pick a brand identity. It will follow one you give it. +- Enforce a specific style (dark mode, glassmorphism, brutalism). It will execute whichever genre + tone the user committed to. +- Build logic — state management, data fetching, business rules. It is a visual / interaction layer only. + +If a request falls outside taste — "build the auth flow", "wire up Stripe" — do the work, but apply Hallmark to the rendered surface. diff --git a/skill/references/genres/atmospheric.md b/skill/references/genres/atmospheric.md new file mode 100644 index 0000000..8e4f626 --- /dev/null +++ b/skill/references/genres/atmospheric.md @@ -0,0 +1,55 @@ +# Genre — atmospheric + +For the AI-creative product page. Dark canvas with warm radial blooms, confident sans display, expressive but plain-English copy, single warm accent. The aesthetic of a tool you'd actually want to use after dark — generative music, video, image, voice. + +## When to pick it + +Brief mentions any of: *AI tool, generative, music, video, image, voice, late-night, atmospheric, dark mode, expressive, creative tool, model playground, vibe-coded, dreamlike, nocturnal*. Also pick when the user names a *mood* that requires darkness (e.g. "moody", "cinematic", "after hours"). + +## Themes that belong + +`Bloom` (canonical), `Midnight`, `Terminal`. Three dark-paper themes; the rotation walks them when atmospheric is active. + +## Voice + +- **Display** — Geist Sans 600 or similar weighty sans, plain English, no ornament. Letter-spacing tight (`-0.03em` or tighter). +- **Body** — same family, 400. Light grey on dark (`oklch(86% 0.008 40)`). +- **Accent** — single warm hue (orange / amber / red / pink). Used in radial-gradient blooms on the canvas, on focus rings, on small tags. Never on display text (that's gate 5 universal — gradient text stays banned). +- **Layout** — centred or near-centred heroes. The canvas itself is the design; the type sits on top of an atmospheric ground. +- **Motion** — fade-in only. No slide, no bounce. The atmosphere does the work. +- **Copy tone** — direct, slightly poetic, specific. *"Make a house song about quitting your job."* is the calibration. + +## What this genre allows + +- **Radial-gradient bloom** on the body background — up to two blooms, each ~20–30 % footprint, fixed-attached, no animation. Gate 31 universal is loosened here. +- **Centred heroes** — gate 7 universal is loosened. The canvas frames the type. +- **Pill-rounded CTAs** with accent fill — confident, not pastel. +- **Glow shadows** on hover (cards lift toward the user with a soft warm shadow). +- **Larger expressive type** — display can hit 6 rem (`clamp(3rem, 6vw + 1rem, 6rem)`). + +## What this genre disallows + +- **Light-paper aesthetics** — the canvas is dark. Don't sneak white sections in. +- **Italic serif body** — atmospheric stays sans top-to-bottom. +- **Hairlines** — atmospheric uses elevated cards (`paper-2`, `paper-3`) instead of hairline-on-paper. +- **Multiple accent hues** — one warm bloom + one secondary (pink/red) is the maximum. No teal-and-amber juggling. +- **Glassmorphism** — banned. Atmospheric is *atmospheric*, not glass. +- **Gradient text** — gate 5 universal. Stays banned. + +## Voice fixtures + +- *"Built for the dark."* +- *"The page should feel like a place you could sit in."* +- *"A canvas, then a tool."* +- *"Generate, refine, ship — between Tuesday and Wednesday."* +- *"The instrument is dark. The output is yours."* + +## Stamp signature + +```css +/* Hallmark · genre: atmospheric · macrostructure: · theme: · enrichment: */ +``` + +## Reference register + +The aesthetic to match: dark canvas with two warm blooms behind the content, plain-English heroic display, single warm accent on small surfaces. Hand-built, not stock-AI. diff --git a/skill/references/genres/editorial.md b/skill/references/genres/editorial.md new file mode 100644 index 0000000..5b063d9 --- /dev/null +++ b/skill/references/genres/editorial.md @@ -0,0 +1,60 @@ +# Genre — editorial (default) + +The canonical Hallmark voice. Pages built for content-led briefs: portfolios, manifestos, type specimens, agency sites, magazine pieces, indie podcasts, bakery / brand stories, considered B2C marketing. + +This is what Hallmark looks like when no other genre signal fires. It is the silent default. + +## When to pick it + +Default. Pick editorial when the brief does not name a specialised aesthetic — when the user said "a landing page for X" without telling you whether X is enterprise, atmospheric, or playful. Most briefs land here. + +## Themes that belong + +`Specimen`, `Newsprint`, `Atelier`, `Garden`, `Salon`, `Linen`, `Almanac`, `Studio`, `Riso`, `Sport`, `Brutal`, `Manifesto`. Twelve themes — plenty of variety inside the genre. + +## Voice + +- **Display** — italic serif, condensed sans, or display-heavy. Not Inter. Not Geist. The weight commits to an extreme (300 or 700+). +- **Body** — workhorse serif (Newsreader, Cormorant) or a plain non-default sans (The Future, Söhne). Readable at 45–75 ch. +- **Accent** — single warm or cool hue, used at < 5 % of any viewport. +- **Layout** — asymmetric. Hairlines, not card borders. Generous whitespace. +- **Motion** — quiet. One orchestrated entrance. No bounces. +- **Copy tone** — specific, hand-set, slightly literary. Verbs over adjectives. + +## What this genre allows + +- Hairline rules, fleurons, drop caps, double rules. +- Italic body in long-form content. +- Asymmetric column counts (2:5, 3:7) on prose pages. +- Hand-built SVG illustrations, pure-CSS art (Tier A enrichment). +- Numbered display labels, edge-aligned headlines. +- Single-accent-colour highlighting (`` band at x-height). + +## What this genre disallows + +The universal slop-test gates apply, plus these editorial-specific bans: + +- **Pill-rounded buttons** with gradient fill — pill is fine, gradient on a pill is not. +- **Centred-everything heroes** (gate 7 universal). Editorial heroes are left-biased or asymmetric. +- **Card-in-card** layouts (gate 4 universal). +- **Three-column equal-icon-tile feature grid** (gate 3 universal). +- **Glassmorphism** — never; the medium is paper, not glass. +- **Pure black or pure white** as paper or ink (gate 8). Tint everything toward the anchor. + +## Voice fixtures + +Each macrostructure under editorial picks from these opening-line patterns. Imitate the *shape*, not the wording. + +- *"Type, set with care."* +- *"Print discipline, on screen."* +- *"A small skill that argues against the average."* +- *"We compose the page like a broadsheet — hairlines, columns, restraint."* +- *"Restraint, repeated, becomes a signature."* + +## Stamp signature + +Output's CSS comment header reads: + +```css +/* Hallmark · genre: editorial · macrostructure: · theme: · enrichment: */ +``` diff --git a/skill/references/genres/modern-minimal.md b/skill/references/genres/modern-minimal.md new file mode 100644 index 0000000..a5497f8 --- /dev/null +++ b/skill/references/genres/modern-minimal.md @@ -0,0 +1,57 @@ +# Genre — modern-minimal + +For the polished enterprise / dev-tool / API page. Stripe / Linear / ElevenLabs school: Geist sans, large confident displays, generous whitespace, pill CTAs, monochrome with optional accent. Minimalism with conviction, not the absence of choice. + +## When to pick it + +Brief mentions any of: *SaaS, enterprise, API, platform, developer tool, infra, B2B, dashboard, billing, Stripe-like, Linear-like, ElevenLabs-like, dev experience, ship fast*. Also pick when the user names a brand colour but the rest of the brief is restrained. + +## Themes that belong + +`Quiet` (canonical). Future themes can be added to this genre — anything monochrome or near-monochrome with Geist / Inter-class sans display and pill CTAs. + +## Voice + +- **Display** — Geist Sans 500–700, Inter Tight Display 600+, or similar. Letter-spacing tight (`-0.02em` to `-0.035em`). +- **Body** — Geist Sans 400, Inter 400. Same family as display (single-family discipline). +- **Accent** — monochrome (accent IS ink) or a single restrained hue used only on focus rings. No chromatic floods. +- **Layout** — two-column heroes (title left, lede right), generous whitespace, refined card surfaces with subtle borders. +- **Motion** — minimal. Reveals are off; the page is composed. +- **Copy tone** — declarative, specific, technical. "Built for X" is not banned but must name the X concretely. + +## What this genre allows + +- **Pill-rounded CTAs** — both filled and outlined. Black-filled primary + white-outlined secondary is the canonical pair. +- **Pure white paper** (`#fff` / `oklch(100% 0 0)`) — gate 8 is loosened here. +- **Zero-chroma neutrals** — gate 24 is loosened here. The Stripe / ElevenLabs school is monochrome by design. +- **Two-column hero with title-left + paragraph-right** — explicitly canonical for this genre. +- **Refined card surface** with very subtle border (`oklch(91% 0 0)`) and 8 px radius. +- **Large, tight-set displays** (`clamp(2.5rem, 5vw + 0.5rem, 4.75rem)`). + +## What this genre disallows + +- **Italic serif body** — modern-minimal stays sans top-to-bottom. +- **Hairline-everything** — borders are thin but visible, not the editorial 0.5 px hairline aesthetic. +- **Asymmetric prose columns** — modern-minimal aligns left, justified to a regular grid. +- **Drop caps, fleurons, ornament** — none of it. +- **Bouncy / overshoot easings** — gate 13 universal applies strictly here. +- **Gradient text** — gate 5 universal. Stays banned. +- **Glassmorphism** — banned. + +## Voice fixtures + +- *"Built to ship."* +- *"The platform that scales with you."* +- *"From idea to production in an afternoon."* +- *"Thirty thousand teams build with X."* +- *"One API. Every channel."* + +## Stamp signature + +```css +/* Hallmark · genre: modern-minimal · macrostructure: · theme: · enrichment: */ +``` + +## Reference register (for the LLM, not credited to anyone) + +The aesthetic to match: confident sans display, clean white canvas, two-column hero, pill CTAs, mono accent. The user knows what this looks like when they see it. Do not name external sites in the output. diff --git a/skill/references/genres/playful.md b/skill/references/genres/playful.md new file mode 100644 index 0000000..fd3ca3c --- /dev/null +++ b/skill/references/genres/playful.md @@ -0,0 +1,55 @@ +# Genre — playful + +For the consumer / friendly / onboarding-led page. Soft surfaces, mild colour, motion that responds to hover, friendlier voice. Closer to Notion's marketing or Figma's onboarding than to Stripe's API docs. + +## When to pick it + +Brief mentions any of: *fun, consumer, casual, family, kids, friendly, approachable, onboarding-heavy, community, social, tactile-but-soft, post-Linear-soft*. Pick playful sparingly — most consumer briefs still belong to editorial (warm-paper, hand-set) unless the user explicitly asks for *softer* and *friendlier*. + +## Themes that belong + +`Pastel` (canonical). The genre is small intentionally — playful is easy to overdo. + +## Voice + +- **Display** — Geist Sans 600 with tighter tracking (`-0.025em`), or a bricolage-style display weighted at 700. Friendly, not childish. +- **Body** — Geist Sans 400 in a slightly muted ink (not pure black). +- **Accent** — soft indigo, warm coral, or muted rose at low chroma. Always low — never the saturated consumer-app pop. +- **Layout** — slightly rounded surfaces, soft drop shadows, friendlier card edges (12 px radius is the upper bound). +- **Motion** — responsive on hover (cards lift slightly). One small bounce-free reveal per section. No spring physics on UI state. +- **Copy tone** — warm, direct, specific. Avoid quirk for quirk's sake. *"Made for teams who write together."* over *"For the squad ✨"*. + +## What this genre allows + +- **Soft drop shadows** on cards (`0 8px 24px -10px `). Restrained. +- **12 px radius** on cards, 8 px on inputs, 999 px on pills. +- **Hover-lift animations** on cards (`translateY(-2px)` + shadow expansion). +- **Mild tinted backgrounds** on alternating sections (paper-2 vs paper, with a tinted band). +- **Soft accent colours** — `oklch(50% 0.13 282)` (indigo) and similar, never above 0.16 chroma. + +## What this genre disallows + +- **Saturated consumer-app pinks / purples** — colour stays low chroma even if friendly. +- **Emoji-as-decoration** — emoji can appear in copy ("we built X 🌱") but never as visual ornament replacing iconography. +- **Comic Sans, Comic Neue, anything that signals "we're zany"** — playful stays sophisticated. +- **Bouncy / overshoot easings** — gate 13 universal. Even playful uses smooth easings. +- **Glassmorphism** — banned across all genres. +- **Gradient text** — gate 5 universal. Stays banned. + +## Voice fixtures + +- *"Made for teams who write together."* +- *"Soft, but exact."* +- *"Software can be soft and exact at once. That's the trick."* +- *"For the people who keep things tidy."* +- *"A small tool, gently opinionated."* + +## Stamp signature + +```css +/* Hallmark · genre: playful · macrostructure: · theme: · enrichment: */ +``` + +## Reference register + +The aesthetic to match: soft surfaces, low-chroma colour, friendly-but-restrained type, hover-responsive motion. The post-Linear soft school. Never childish, never quirk-for-quirk. diff --git a/skill/references/slop-test.md b/skill/references/slop-test.md new file mode 100644 index 0000000..8c215e4 --- /dev/null +++ b/skill/references/slop-test.md @@ -0,0 +1,79 @@ +# Slop test — 38 gates + +Run this list before handing back any output. Every answer must be **no**. Update the Step 5 preview block's `Slop test` row to reflect the actual outcome of this run. + +Some gates are **universal** (apply to every genre); some are **genre-scoped** (apply only when the active genre is editorial, atmospheric, modern-minimal, or playful). Genre overrides are noted inline. Where a gate has *no* genre note, treat it as universal. + +--- + +## Visual + +1. Is the display font Inter, Roboto, Open Sans, Poppins, Lato, or a system default? +2. Is there a purple-to-blue (or cyan-to-magenta) gradient anywhere? *Genre note: atmospheric allows radial gradients on background only — never on text or pill buttons.* +3. Is there a 3-equal-column card grid with icon-above-heading tiles? +4. Is any card nested inside another card? +5. Is there a `background-clip: text` gradient headline? (Universal — no genre allows gradient text.) +6. Is any card using a thick coloured left/right side-stripe border? +7. Is the hero `min-height: 100vh` with everything centred? *Genre note: atmospheric and playful allow centred heroes when the canvas itself is the design (Suno-style).* +8. Is pure `#000` or pure `#fff` used as a base colour anywhere? *Genre note: modern-minimal allows pure `#fff` paper (the Stripe / ElevenLabs school).* + +## Structural + +9. Does the page use the *same* structural fingerprint as the last page you built? (Hero → 3 features → CTA → footer is the AI structural template; reject it.) +10. Are sections separated only by equal whitespace, with no rule, no ornament, no colour shift — every section identical in rhythm? + +## Microinteractions + +11. Is `transition-all` (or `transition: all`) used anywhere? (Specify the properties.) +12. Is `hover:scale-105` (or any uniform hover-scale) applied across multiple unrelated elements? +13. Are bouncy / overshoot easings (`cubic-bezier(0.34, 1.56, ...)`, etc.) used on UI state changes — buttons, modals, tooltips? (Reserve overshoots for physical interactions only.) +14. Does any element have *more than one* hover effect at the same time (translate + scale + shadow + colour + rotate)? +15. Are you animating `width`, `height`, `top`, `left`, `margin`, or `padding` anywhere? +16. Does the focus ring transition into existence (fade in)? (Focus rings must appear instantly — keyboard users need an immediate indicator.) +17. Is there a celebratory success toast for an action whose effect the user can already see? (Silent success is taste; toasts are for failures and invisible effects.) +18. Are tooltip hover-delay and focus-delay equal? (Hover should delay 800–1000 ms; focus should be 0 ms.) +19. Is auto-rotating content (carousel, banner, stats) lacking pause-on-hover-and-focus? (WCAG 2.2.2.) +20. Is there a placeholder name "Jane Doe / John Smith" or a startup cliché (Acme, Nexus, Seamless, Unleash)? + +## Variety + +21. Is the `/* Hallmark · macrostructure: · ... */` stamp missing from the top of the CSS? (It must be present.) +22. Is the macrostructure I picked the same as a previous Hallmark output's stamp in this project? (Read the file system; if a stamp exists, mine must differ.) +23. Did I default to the **Specimen** macrostructure (numbered left-margin labels + huge serif + asymmetric spans + typographic-only CTA) when the brief did not explicitly call for editorial / foundry / specimen energy? (Specimen fall-through is banned.) *Genre note: atmospheric, modern-minimal, and playful never default to Specimen — only editorial does, and only when the brief signals it.* + +## Implementation gates + +24. Does any neutral / surface colour have `oklch(... 0 ...)` (zero chroma)? Pure greys read as flat. Tint every neutral toward the anchor hue — minimum 0.005 chroma. *Genre note: modern-minimal allows zero-chroma neutrals (the monochrome Stripe / ElevenLabs school).* +25. Does the accent colour cover more than ~5 % of any single viewport (count by area: solid fills, large headings in accent, full-bleed accent backgrounds)? If yes, retreat — accent is for emphasis, not for filling. *Genre note: atmospheric allows accent-tinted radial blooms covering up to ~20 % of the canvas, since the bloom is the design.* +26. Is any padding / gap / margin a value that isn't on the named spacing scale (`--space-3xs` … `--space-5xl`, multiples of 4 px)? Arbitrary `padding: 17px` is a tell. +27. Is any prose container's `max-width` outside the 45–75 ch range? Measure must read; under 45 ch is choppy, over 75 ch loses the eye. +28. Does any interactive element lack `:focus-visible`, `:active`, OR `:disabled` styling? (Eight states is the rule. Default + hover is two; you need at least default + hover + focus-visible + active + disabled present in code.) +29. Is there any `transform` / `animation` keyframe that is NOT covered by a `@media (prefers-reduced-motion: reduce)` fallback? Every motion gets a reduced-motion alternative. + +## Hero enrichment gates + +(When the page carries enrichment — see [`hero-enrichment.md`](hero-enrichment.md).) + +30. If the page has a demo video, does it autoplay with sound, lack a `poster`, lack `fetchpriority="high"`, or use `loading="lazy"` on the LCP element? (LCP-killers fail this gate.) +31. If the page has an abstract background, is it more than one accent colour, more than ~5 % footprint, or animating mesh-gradient on the whole page? (Aurora blobs and mesh-on-everything fail this gate.) *Genre note: atmospheric allows up to two warm-toned radial blooms covering ~20–30 % of the canvas, fixed-attached, no animation.* +32. Does the page mix two or more icon libraries? (Material + Heroicons + Lucide on the same page = the icon-set tell.) +33. If the page has illustration, did I default to a Lottie library when a hand-built SVG or pure-CSS shape would have worked? (Lottie is last resort, not the default.) + +## Diversification gates + +(Cross-reference `.hallmark/log.json` when present.) + +34. If I used the same archetype as a previous Hallmark output (per `.hallmark/log.json` or the latest macrostructure stamp), did I pick at least one different *variation knob*? Two Bento Grids with `tiles=6, spans=irregular, accent=corner-only` are the same Bento — the within-archetype knobs in [`component-cookbook.md`](component-cookbook.md) exist precisely to prevent that. State the knob deltas in the stamp. +35. Does any visual-only ``, custom-art `
      `, ``, or decorative figure lack `aria-label` or `aria-hidden="true"`? Hand-built CSS art and SVG illustrations need an accessible name *or* an explicit hide. Skipping this is the new accessibility tell. + +## Layout-safety gates + +(The page must survive every viewport.) + +36. Does the page horizontally scroll on any viewport between 320 px and 1920 px? Open the rendered page; drag the dev-tools width slider across that range. If a horizontal scrollbar appears at any width, fail. The fix is `html { overflow-x: clip; }` plus `body { overflow-x: clip; }` as a safety net for any clipped-edge enrichment that pushes past the viewport. Use `overflow-x: clip` (not `hidden`) — `clip` preserves `position: sticky` and `position: fixed` on descendants. (Cross-reference: [`layout-and-space.md` § Page-edge clipping](layout-and-space.md).) +37. For every decorative effect on text — highlighter `` / `` band / accent stroke / underline — did I visually confirm the position and size? A highlighter band must sit behind the x-height (`linear-gradient(180deg, transparent ~38%, accent ~38%, accent ~92%, transparent ~92%)`), **not** at the baseline (which reads as a fat underline). Underlines must be 1–2 px and offset 1–2 px from the baseline, never 5+ px. Decorative strokes must not exceed 5 % of the viewport (gate 25). The check is *visual*: imagine the rendered output and confirm the band lands in the right vertical zone. +38. Are interactive bars (nav, toolbar, command bar, hero CTA row, footer link strip) explicitly vertically centered? Default flex layouts inherit `align-items: stretch`, which makes a button taller than its sibling text and breaks the visual baseline. Every flex row mixing height-different elements (button + text, icon + text, mark + body) must declare `align-items: center` and `line-height: 1` on the items with intrinsic height. Inheriting `line-height: 1.55` from `html` fights the row's vertical rhythm. + +--- + +If any answer is **yes**, fix it. Do not ship slop. diff --git a/skill/references/verbs/audit.md b/skill/references/verbs/audit.md new file mode 100644 index 0000000..855ed5c --- /dev/null +++ b/skill/references/verbs/audit.md @@ -0,0 +1,25 @@ +# `hallmark audit` + +Read the file(s) the user pointed at. For each finding, return: + +- **Tell** — the named anti-pattern from [`anti-patterns.md`](../anti-patterns.md). +- **Where** — file path and line range. +- **Severity** — `critical` (ships as slop), `major` (looks AI-generated), `minor` (small taste issue). +- **Fix** — one-line concrete correction. + +Group by severity. Do not edit. Do not redesign. End with a count: `N critical · M major · K minor`. + +Audit *also* checks structural fingerprint: if the page uses the AI template (centered hero, 3 equal feature cards, CTA, footer, with no asymmetry or surprise), flag it as a critical structural finding even if the visual treatment is fine. + +**Stamp-vs-page check.** If the audited file contains a `/* Hallmark · macrostructure: · ... */` stamp, verify the page actually matches that name. If the stamp says **Bento Grid** but the page is a centered single-column hero with a CTA, flag it as a critical structural finding: `stamp lies` — the stamp must reflect what shipped or be removed. This catches drift where a previous Hallmark run stamped one thing and a later edit pulled the page back toward the AI template. + +**Genre-aware audit.** If the audited file's stamp names a genre (e.g. `genre: atmospheric`), apply the genre-scoped overrides from [`slop-test.md`](../slop-test.md) when grading. A radial-gradient background is a critical tell for editorial — but allowed for atmospheric. A pure-white paper is a tell for editorial — but allowed for modern-minimal. The audit verb must respect the genre the page declared. + +**`design.md` audit.** If the project root has a `design.md` (or `DESIGN.md`), read it before grading. Then check every audited page against the system: + +- **Theme drift.** Page uses tokens / fonts / accent that don't match `design.md`'s declared system → flag as `critical: design-system drift`. Per-page theme picks are slop on a system-managed project even if each page is internally fine. +- **Macrostructure family violation.** `design.md` says marketing pages use Marquee Hero or Stat-Led — the audited page is a Letter format → flag as `major: outside design.md family`. +- **Stamp mismatch.** The page's CSS stamp says `designed-as-app` but reads `design-system: design.md` and the page actually drifts from `design.md` → flag as `critical: stamp lies`. The stamp claims compliance the code doesn't deliver. +- **No stamp at all on a system-managed project** → flag as `major: missing system reference`. Every page on a `design.md` project must stamp its allegiance to the system. + +Inversely, on a project *without* `design.md`, the standard diversification rule applies — flag pages that share macrostructure / theme with a previous Hallmark output as `minor: variety drift`. diff --git a/skill/references/verbs/redesign.md b/skill/references/verbs/redesign.md new file mode 100644 index 0000000..fa86397 --- /dev/null +++ b/skill/references/verbs/redesign.md @@ -0,0 +1,170 @@ +# `hallmark redesign` + +The user wants a different page from the same content. They are not happy with the current structure — typically because it reads as templated, generic, or AI-shaped. Your job is to throw the structure out and build a new one. + +## Step 0 · Detect scope first + +Before anything else, decide whether the redesign is **single-page** or **multi-page**. The behaviour diverges hard. + +**Multi-page signals (any one fires):** +- The target is a directory (e.g. `./app/`, `./pages/`, `./src/routes/`). +- The target is a glob (`**/*.tsx`, `app/*/page.tsx`). +- The user names more than one file in the brief (`./hero.tsx and ./pricing.tsx`). +- The user says "the whole site", "every page", "the app", "all the pages", "the marketing site". +- The codebase has multiple route files (`app/page.tsx`, `app/about/page.tsx`, `app/pricing/page.tsx`, etc.) and the user pointed at the project root. + +If any of those fires → **multi-page redesign**. Go to § Multi-page flow. +If none fires → **single-page redesign**. Go to § Single-page flow. + +--- + +## § Multi-page flow — design.md first, then redesign + +A web app needs a *design system*, not seventeen unrelated theme pickings. Hallmark's diversification rule is wrong here: across pages of the same product, **consistency is the goal, not variety**. If you redesign every page with a different macrostructure / theme / accent, you've shipped a slop split-personality app, even if each individual page is fine. + +The flow is: + +### 1. Read the project, then pause + +Before redesigning a single file: + +- Walk the target directory. List every page-level file you found, with a one-line description of what it does. (Hero / pricing / docs / dashboard / etc.) +- Note any existing design assets: a `tokens.css`, a tailwind config with brand values, a logo, brand colours mentioned in `README`, a marketing screenshot. +- Check for an existing `.hallmark/log.json` — if it has prior runs, read the most recent stamp; if all those entries are different macrostructures / themes, it confirms the user's complaint. + +### 2. Produce `design.md` at the project root + +Write a single file at the project root: `design.md` (or `DESIGN.md` — match the project's existing case convention). This file is the **one source of truth** every subsequent page redesign reads. Format: + +```markdown +# Design — + +A locked design system for this app. Every page redesign reads this file before +emitting code. Do not regenerate per page — extend or amend this file when the +system needs to grow. + +## Genre + + +## Macrostructure family +Pick one base macrostructure for marketing pages, one for app pages, one for +content pages (if applicable). Pages within a family share the family's shape; +they vary only in component archetypes. + +- Marketing pages: +- App pages: +- Content pages: + +## Theme +- `--color-paper` oklch( ) +- `--color-paper-2` oklch( ) +- `--color-ink` oklch( ) +- `--color-ink-2` oklch( ) +- `--color-rule` oklch( ) +- `--color-accent` oklch( ) +- `--color-focus` oklch( ) + +## Typography +- Display: , weight , style +- Body: , weight +- Mono: , weight +- Display tracking: +- Type scale anchor: = clamp(...) + +## Spacing +4-point named scale. The values are in `tokens.css`. Pages must use named +tokens (`var(--space-md)`), never raw values. + +## Motion +- Easings: cubic-bezier(, , , ) named `--ease-out`, etc. +- Reveal pattern: +- Reduced-motion fallback: opacity-only, ≤ 150 ms. + +## Microinteractions stance +- +- +- + +## CTA voice +- Primary CTA: +- Secondary CTA: + +## Per-page allowances +- Marketing pages MAY use enrichment (Tier-A CSS art, Tier-B SVG, etc.). +- App pages MUST NOT use enrichment — function carries the page. +- Content pages: typography only. + +## What pages MUST share +- The wordmark / logotype. +- The accent colour and its placement (≤ 5 % per viewport). +- The display + body fonts. +- The CTA voice (button shape, border-radius, padding rhythm). +- Section heading rhythm (numeral + label + display heading pattern). + +## What pages MAY differ on +- Macrostructure within the page-type family (a marketing page can be Marquee + Hero on one route and Long Document on another — both still use the system's + type, colour, and CTA voice). +- Hero archetype (within the family's allowance). +- Enrichment — only on marketing pages, only Tier-A or Tier-B. +``` + +State the picks aloud in plain text BEFORE writing the file. *"Genre: modern-minimal. Theme: a custom OKLCH palette anchored on your brand teal. Display: Geist 600. Body: Geist 400. Three macrostructure families: Marquee Hero (marketing), Workbench (app), Long Document (content)."* Then ask: *"Want me to proceed with this system across every page, or amend any of it first?"* + +Wait for confirmation. Only after the user confirms (or says "go ahead") do you write `design.md` and start redesigning pages. + +### 3. Redesign each page reading from `design.md` + +For each target page: + +- **Read `design.md` first.** It is now the rule of the project; the per-build references in [`references/`](../) defer to it. Where `design.md` and the references conflict, `design.md` wins. +- Pick the macrostructure from the family declared in `design.md` for this page's type (marketing / app / content). Within the family, you may vary archetypes — but only those `design.md` allows. +- Apply the locked theme. Do **not** swap to a different theme to "add variety". The variety lives in macrostructure / archetype choice, not theme. +- Apply the locked typography, spacing, motion, microinteractions stance. +- Stamp every page's CSS with: `/* Hallmark · genre: · macrostructure: · design-system: design.md · designed-as-app */`. The `designed-as-app` flag tells future Hallmark runs to read `design.md`, not invent a new system. +- Write a single combined `.hallmark/log.json` entry for the multi-page redesign, with `"scope": "app"` instead of one entry per page. + +### 4. Diversification rule — INVERTED for multi-page + +Across pages of the same app, the diversification rule is *inverted*: consecutive pages MUST share theme, accent, type pairing. They may differ on macrostructure within the family. The 38 slop-test gates that check "differs from previous Hallmark run" are skipped for `designed-as-app` outputs — the system overrides the catalog rotation here. + +Pages that drift from `design.md` are slop. The audit verb flags `design.md` drift as a critical structural finding (`stamp-vs-design.md disagreement`). + +### 5. When to amend `design.md` instead of overriding + +If a page genuinely needs something `design.md` doesn't allow (e.g. a marketing landing for a new sub-product wants a different theme), the rule is **amend `design.md` first**, not override locally. Add an explicit per-page allowance or a `## Variants` section. The file evolves; per-page overrides do not. + +--- + +## § Single-page flow + +(The classic redesign behaviour — unchanged.) + +**What to preserve:** +- The copy (every word, ideally) +- The information architecture (which sections exist, in roughly what order) +- The brand (colours and fonts they've named, if any) +- The primary action + +**What to replace:** +- The structural fingerprint — pick a **different** combination from [`structure.md`](../structure.md) than the source had. +- The component voice — different button style, different divider language, different image treatment. +- The reveal pattern — if the original faded everything in on scroll, the new one might have no reveals at all. +- The visual rhythm — different sections having different padding, different alignments, deliberate breaks. + +**Optional `--mood ` argument:** + +If the user specifies a mood (`hallmark redesign ./hero.tsx --mood luxury`), pick a tone aligned to that mood and let it drive the structural fingerprint. Mood names map to tones from [`typography.md`](../typography.md) and [`structure.md`](../structure.md). If no mood is given, ask the user what *feeling* they want — one word — and proceed. + +**Genre escape hatch.** If the user explicitly asks for a *kind* of design that the current genre doesn't fit (e.g. "redesign this editorial page as a modern SaaS hero"), switch genre too. Load [`genres/.md`](../genres/) and apply its rule overlay. Stamp the new genre into the output so future runs respect it. + +**Project-level check.** Before treating this as a true single-page redesign, look for `design.md` at the project root. If it exists, the project is being designed as an app and **the single-page rules don't apply** — read `design.md` and follow it instead. The diversification rule reverses (consistency wins). If you actually want to break from the locked system, *update `design.md` first*, then redesign. + +**Output:** + +Return the redesigned code, plus a short note explaining: + +- The structural fingerprint you picked, axis by axis. +- Why this combination fits the brief better than the original. +- One thing you removed and why. +- (If genre changed) why the new genre fits the user's stated kind of design. diff --git a/skill/references/verbs/refine.md b/skill/references/verbs/refine.md new file mode 100644 index 0000000..51302e1 --- /dev/null +++ b/skill/references/verbs/refine.md @@ -0,0 +1,12 @@ +# `hallmark refine` + +The user has code they are happy with structurally but wants polished. Your job is to apply the ruleset with the smallest possible diff. + +- Do not move or rename elements unless necessary. +- Do not restructure the DOM. +- Do swap fonts, rewrite colour tokens to OKLCH, tighten the type scale, correct easings, add missing states, fix any flagged anti-patterns. +- At the end, list what you changed and which reference file prompted each change. + +**If the page has a Hallmark stamp** with a genre, refine within that genre's rule overlay (loaded from [`genres/.md`](../genres/)). A modern-minimal page being refined keeps its pill CTAs and large display; an atmospheric page keeps its radial blooms; an editorial page keeps its hairlines and asymmetry. + +**If the page has no stamp**, the genre is unknown. Refine to the universal ruleset only — do not impose editorial defaults on what may have been a deliberate atmospheric or modern-minimal choice. The smallest-possible-diff principle means *not* repainting the page in your default voice.