mirror of
https://github.com/Nutlope/hallmark.git
synced 2026-08-14 12:35:33 +02:00
Twelve targeted skill additions — Tier 1, 2 & 3
Implements all twelve Tier 1, 2, and 3 items from the test-suite findings (skipping #12 study+redesign and #14 OG cards, both deferred). Aim: tighten the gaps the six-test exercise surfaced — copy voice, theme diversification, sub-domain disambiguation, mobile collapse, illustration recipes, accessibility gates. SKILL.md (items 3, 6, 10, 11). Version 0.4.0 → 0.5.0. New step 2.5 documents reading .hallmark/log.json before macrostructure pick; Step 5 (Build) now writes to that file after each output. Theme- diversification rule expanded: two consecutive themes must differ on at least one of paper-band / display-style / accent-hue. Slop test 33 → 35 with two new gates: different-knobs check and aria-label/aria-hidden presence on visual-only SVG / div / canvas. macrostructures.md (item 1). 21 macrostructures each gain 2–3 example opening-line copy fragments — drawn from Pentagram, Klim, Linear, Are.na, Resend, Craig Mod, Rauno Freiberg, Tracejam, Maple Bakery, Anya, Streampipe, Meridian. Imitate the kind of specificity (named places, dates, verticals; refusal of metaphor), not the wording. structure.md (item 2). Domain → trio table refined: developer-docs split from SaaS-marketing; personal one-pager split from work-led portfolio. Plus a clarifying note on splits and when to ask one disambiguating question before picking. typography.md (item 4). Pairing table doubled: each of 8 tones now gets a Free baseline row (Geist, Fraunces, Bricolage Grotesque, JetBrains Mono, system-ui) and a Paid upgrade row (Söhne, Tiempos, Druk, Berkeley Mono, ABC Diatype). Discipline note: never name a paid font without confirming the user is licensed — the demo will fall back to system-default and look broken. hero-enrichment.md (item 5). Each E1–E8 archetype gains a 4-6 line worked example: Tracejam clipped-edge, fashion lookbook full-bleed loop, Linear-style mockup split, code-formatter floating mockup, Maple Bakery custom illustration, whiteboard CSS-orbit loop, podcast gradient + grain, Lisbon café photography. Examples reference real test pages where they exist. copy.md (item 8). New "Voice samples per tone" section: 7 tones, 3 voice patterns each, 5–8 example sentences per tone (105+ samples total) drawn from real non-LLM sources. Plus a banned-opening-lines table — "Built for the modern team", "Unleash your X", "Where X meets Y", "Empower your...", and 6 more — each with a one-line reason it fails. component-cookbook.md (item 9). Single comprehensive Mobile Collapse table at the end documents all 36 archetypes' collapse behaviour at 60rem (layout breakpoint) and 40rem (typography breakpoint). Plus five cross-cutting rules (44px hit targets, container padding, disable scroll-linked motion, lazy-load below-fold, data-saver respect). custom-craft.md (item 7). New Recipe library section adds four hand-built illustration recipes after the existing bakery loaf: (1) workflow / process diagram with animated stroke-dashoffset flow, (2) minimal-line mascot with @keyframes blink loop, (3) three-tier architectural diagram with @property --flow-offset data flow, (4) botanical leaf flourish (no animation — the design is the stillness). Each recipe is full code, "use when / avoid when", reduced-motion fallback, and named real-world inspiration. Plus a Cross-recipe techniques callout summarising the four habits of hand-built CSS/SVG illustration in 2026. site/index.html + components.css (item 13). New section 07 · Examples on the landing page links to the six self-contained test pages under site/_tests/. Six gallery cards display the test number, name, macrostructure stamp shape, one-line description, and a "View ↗" affordance. The cards inherit --radius-card, --rule-card, --shadow-card from the active theme — switching themes restyles the gallery alongside everything else. Footer slot's --i shifted from 6 to 7 to preserve reveal-stagger ordering. README.md updated to reflect: 35-gate slop test, project memory, theme-diversification rule on three axes, voice fixtures over LLM defaults. Defers: #12 study+redesign combined verb, #14 auto-generated OG cards. Both ready to plan when the user wants them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
756eafff4a
commit
b61f1efc8d
@@ -28,7 +28,7 @@ The differentiator: Hallmark insists on **structural variety**, not just visual
|
||||
|
||||
## What's inside
|
||||
|
||||
- **[`SKILL.md`](skill/SKILL.md)** — the routing file. Design flow, slop test (33 questions), output contract.
|
||||
- **[`SKILL.md`](skill/SKILL.md)** — the routing file. Design flow (six steps, including `Step 2.5 · Check project memory` reading `.hallmark/log.json`), slop test (35 questions), output contract.
|
||||
- **[`references/`](skill/references/)** — sixteen 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`, and three new files for hero work — `hero-enrichment.md` (when to add visuals to a hero, plus eight enrichment archetypes), `custom-craft.md` (how to hand-build CSS art, SVG, declarative animation, JS-driven motion, with Lottie demoted to last resort), and `assets.md` (sourcing canon for icons, brand logos, generated illustration via Nanobanana / Recraft, library illustration, app mockups, hero video, photography, abstract backgrounds).
|
||||
- **[`site/`](site/)** — a self-demonstrating landing page. Hand-written HTML + CSS + ES module, no framework, no build step. **Sixteen themes** — Specimen, Newsprint, Atelier, Garden, Salon, Linen, Almanac, Midnight, Terminal, Brutal, Manifesto, Sport, Studio, Pastel, Riso, **Quiet** — that swap not just colour and typography but the page's hero archetype and footer archetype. Switching themes literally rebuilds the page.
|
||||
- **[`ROADMAP.md`](ROADMAP.md)** — Tier 1, 2, 3 work plus an explicit "things to *not* do" list.
|
||||
@@ -60,7 +60,10 @@ Press `T` to cycle themes, `R` for random, `?theme=studio` for a shareable link.
|
||||
- **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.
|
||||
- **Microinteractions as discipline.** Silent success over celebratory toasts. Optimistic update + Undo over confirm dialogs. Hover delay 800 ms, focus delay 0 ms.
|
||||
- **A 33-gate slop test** runs before every output. One yes fails the build.
|
||||
- **A 35-gate slop test** runs before every output. One yes fails the build. New gates 34 (different-knobs check across runs) and 35 (aria-label / aria-hidden on every visual-only SVG / canvas / div).
|
||||
- **Project memory.** A per-project `.hallmark/log.json` records each run's macrostructure + theme + enrichment + brief summary. The skill reads the last 3–5 entries before picking, and writes a new entry after each build, so consecutive Hallmark outputs in the same project don't repeat shapes or themes.
|
||||
- **Theme-diversification rule.** Two consecutive themes must differ on at least one of three axes: paper band (dark / mid / light), display style (italic-serif / roman-serif / geometric-sans / mono / display-condensed-italic / display-heavy / system-native / risograph), accent hue (warm / cool / neutral / chromatic). Specimen-fall-through is no longer the only diversification check.
|
||||
- **Voice fixtures over LLM defaults.** Each of the 21 macrostructures ships with 2–3 example opening lines from real designer-engineer sites (Pentagram, Klim, Linear, Are.na, Resend, Lynn Fisher, Rauno Freiberg, etc.). Each of seven tones in `copy.md` has three voice patterns × five sample sentences. "Built for the modern team" is in the banned-phrases list.
|
||||
- **Hero enrichment is opt-in, not a default.** A typographic-only hero is always acceptable. When enrichment is right, the skill picks from a six-tier hierarchy: typography only → custom-built CSS art → hand-built SVG → generated illustration (Nanobanana / Recraft) → library + customise → Lottie (last resort).
|
||||
- **`study` extracts DNA, not pixels.** Vision-extraction with refusal heuristics, type-role vocabulary (no font ID guessing), and a confirmation step before any code.
|
||||
|
||||
|
||||
@@ -1638,6 +1638,166 @@
|
||||
[data-theme="quiet"] .hero--clipped .hero__mock { display: none; }
|
||||
[data-theme="quiet"] .hero--clipped { grid-template-columns: 1fr; }
|
||||
|
||||
/* ───────────── EXAMPLE GALLERY ─────────────
|
||||
Cards link to the six self-contained test pages under site/_tests/.
|
||||
The cards inherit the active theme's --radius-card, --rule-card, and
|
||||
--shadow-card so switching themes restyles them along with everything else. */
|
||||
|
||||
.examples__intro,
|
||||
.examples__outro {
|
||||
font-family: var(--font-serif);
|
||||
font-style: italic;
|
||||
font-size: var(--text-md);
|
||||
line-height: var(--lh-snug);
|
||||
color: var(--color-ink-2);
|
||||
max-width: 56ch;
|
||||
margin-block-end: var(--space-2xl);
|
||||
}
|
||||
|
||||
.examples__outro {
|
||||
margin-block: var(--space-xl) 0;
|
||||
}
|
||||
|
||||
.examples__intro code {
|
||||
font-family: var(--font-mono);
|
||||
font-style: normal;
|
||||
font-size: 0.875em;
|
||||
background: var(--color-paper-2);
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: var(--radius-input, 0);
|
||||
border: var(--rule-hair) solid var(--color-rule);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
[data-theme="brutal"] .examples__intro,
|
||||
[data-theme="manifesto"] .examples__intro,
|
||||
[data-theme="terminal"] .examples__intro,
|
||||
[data-theme="sport"] .examples__intro,
|
||||
[data-theme="almanac"] .examples__intro,
|
||||
[data-theme="brutal"] .examples__outro,
|
||||
[data-theme="manifesto"] .examples__outro,
|
||||
[data-theme="terminal"] .examples__outro,
|
||||
[data-theme="sport"] .examples__outro,
|
||||
[data-theme="almanac"] .examples__outro {
|
||||
font-family: var(--font-body);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
.gallery { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.gallery__item {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"num name"
|
||||
"num shape"
|
||||
"num desc"
|
||||
"num open";
|
||||
grid-template-columns: 4rem 1fr;
|
||||
gap: 0 var(--space-md);
|
||||
padding: var(--space-lg);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
background: var(--color-paper);
|
||||
border: var(--rule-card, 1px) solid var(--color-rule);
|
||||
border-radius: var(--radius-card, 0);
|
||||
box-shadow: var(--shadow-card, none);
|
||||
transition: transform 100ms var(--ease-out),
|
||||
border-color var(--dur-micro) var(--ease-out),
|
||||
box-shadow var(--dur-short) var(--ease-out);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.gallery__item:hover {
|
||||
border-color: var(--color-ink);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.gallery__item:focus-visible {
|
||||
outline: 2px solid var(--color-focus);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.gallery__num {
|
||||
grid-area: num;
|
||||
font-family: var(--font-label);
|
||||
font-size: var(--text-sm);
|
||||
letter-spacing: var(--tracking-label);
|
||||
color: var(--color-muted);
|
||||
align-self: start;
|
||||
font-feature-settings: "tnum";
|
||||
}
|
||||
|
||||
.gallery__name {
|
||||
grid-area: name;
|
||||
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);
|
||||
line-height: 1.05;
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
.gallery__shape {
|
||||
grid-area: shape;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-accent);
|
||||
margin-block-start: 0.4em;
|
||||
}
|
||||
|
||||
.gallery__desc {
|
||||
grid-area: desc;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-ink-2);
|
||||
line-height: var(--lh-snug);
|
||||
margin-block-start: var(--space-sm);
|
||||
max-width: 36ch;
|
||||
}
|
||||
|
||||
.gallery__open {
|
||||
grid-area: open;
|
||||
font-family: var(--font-label);
|
||||
font-size: var(--text-xs);
|
||||
letter-spacing: var(--tracking-label);
|
||||
color: var(--color-muted);
|
||||
margin-block-start: var(--space-md);
|
||||
align-self: end;
|
||||
justify-self: start;
|
||||
text-transform: uppercase;
|
||||
transition: color var(--dur-micro) var(--ease-out),
|
||||
transform var(--dur-short) var(--ease-out);
|
||||
}
|
||||
|
||||
.gallery__item:hover .gallery__open {
|
||||
color: var(--color-ink);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
[data-theme="riso"] .gallery__name,
|
||||
[data-theme="brutal"] .gallery__name,
|
||||
[data-theme="manifesto"] .gallery__name,
|
||||
[data-theme="sport"] .gallery__name {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
[data-theme="riso"] .gallery__name {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
/* ───────────── SPORT — section title fix ─────────────
|
||||
The italic display + tight tracking + uppercase + weight 700
|
||||
produced cramped, almost-touching letterforms. Loosen the
|
||||
|
||||
+70
-2
@@ -403,8 +403,76 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 07 · FOOTER SLOT — archetype swaps per theme -->
|
||||
<footer class="slot slot--foot reveal" data-slot="footer" style="--i:6" role="contentinfo" aria-label="Colophon"></footer>
|
||||
<!-- 07 · EXAMPLES -->
|
||||
<section class="section reveal" style="--i:6" id="examples" aria-labelledby="s7">
|
||||
<header class="section__head">
|
||||
<p class="section-label">
|
||||
<span class="num">07</span><span class="divider">⁄</span><span>Examples</span>
|
||||
</p>
|
||||
<h2 class="section__title" id="s7">
|
||||
Six pages, six different shapes.
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<p class="examples__intro">Six landing pages produced by exercising the skill across contrasting briefs. Each page is its own self-contained HTML + CSS — no shared theme, no shared layout. Every one carries a <code>/* Hallmark · macrostructure: … */</code> stamp at the top of its CSS.</p>
|
||||
|
||||
<div class="gallery" role="list">
|
||||
|
||||
<a class="gallery__item" href="_tests/01-tide-podcast/index.html" role="listitem">
|
||||
<span class="gallery__num">01</span>
|
||||
<span class="gallery__name">Tide</span>
|
||||
<span class="gallery__shape">Quote-Led · Atelier · no enrichment</span>
|
||||
<span class="gallery__desc">Indie podcast. Context skipped — the skill inferred the audio domain.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/02-streampipe-cli/index.html" role="listitem">
|
||||
<span class="gallery__num">02</span>
|
||||
<span class="gallery__name">Streampipe</span>
|
||||
<span class="gallery__shape">Workbench · Terminal · Tier-A CSS-art mockup</span>
|
||||
<span class="gallery__desc">Open-source CLI. Theme requested. Hand-built phosphor-green terminal.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/03-maple-bakery/index.html" role="listitem">
|
||||
<span class="gallery__num">03</span>
|
||||
<span class="gallery__name">Maple Street Bread</span>
|
||||
<span class="gallery__shape">Long Document · Linen · Tier-B hand-built SVG loaf</span>
|
||||
<span class="gallery__desc">Artisan bakery. Letter-style hero, drop-cap body, hand-drawn loaf.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/04-meridian-manifesto/index.html" role="listitem">
|
||||
<span class="gallery__num">04</span>
|
||||
<span class="gallery__name">Meridian</span>
|
||||
<span class="gallery__shape">Manifesto · Manifesto · no enrichment</span>
|
||||
<span class="gallery__desc">Studio manifesto. Pure black, oversized white display, single red bleed band.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/05-tracejam-saas/index.html" role="listitem">
|
||||
<span class="gallery__num">05</span>
|
||||
<span class="gallery__name">Tracejam</span>
|
||||
<span class="gallery__shape">Bento Grid · Pastel · clipped-edge dashboard</span>
|
||||
<span class="gallery__desc">SaaS observability. Hand-built CSS-art trace waterfall, asymmetric Bento.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/06-anya-portfolio/index.html" role="listitem">
|
||||
<span class="gallery__num">06</span>
|
||||
<span class="gallery__name">Anya</span>
|
||||
<span class="gallery__shape">Long Document · Studio · no enrichment</span>
|
||||
<span class="gallery__desc">Software architect personal site. Italic Fraunces + Geist, tabular work sheet.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="examples__outro">Findings + improvement list at <a href="_tests/" class="link">site/_tests/</a>.</p>
|
||||
</section>
|
||||
|
||||
<!-- 08 · FOOTER SLOT — archetype swaps per theme -->
|
||||
<footer class="slot slot--foot reveal" data-slot="footer" style="--i:7" role="contentinfo" aria-label="Colophon"></footer>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
+40
-3
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: hallmark
|
||||
description: Use this skill when the user asks to design, build, redesign, audit, refine, or study a UI, web page, landing page, dashboard, component, or interface — or when they ask to make something "feel less AI-generated." Hallmark forces intentional design decisions (typography, color, layout, motion, interaction, structure) and refuses to default to the generic AI-UI template. Trigger phrases include "design a", "build a landing page", "make a dashboard", "redesign this site", "redesign the page", "refine this UI", "audit this design", "this looks AI-generated", "fix the design", "polish this", "give this a different look", and any request that will produce HTML / CSS / JSX / Tailwind output. **Also trigger when the user attaches a screenshot of a design they admire** — that is the `hallmark study` verb (extracts design DNA, never pixel-clones).
|
||||
version: 0.4.0
|
||||
version: 0.5.0
|
||||
---
|
||||
|
||||
# Hallmark
|
||||
@@ -65,12 +65,43 @@ Before loading any visual ruleset, **pick one of the twenty-one named macrostruc
|
||||
2. If you have produced any other Hallmark output for this user in this session, your pick must be a different macrostructure than the last one.
|
||||
3. **The Specimen macrostructure (numbered left-margin labels + huge serif + asymmetric spans + typographic CTA) is no longer a default.** Reach for it only when the brief is explicitly editorial, foundry-adjacent, or the user has named it.
|
||||
|
||||
**State your pick.** Before writing any code, say "Macrostructure: <name>." in plain text. This is a deliberate accountability step — picking on the page (not in your head) prevents the default-attractor sameness that kept the skill emitting Specimen output.
|
||||
**Theme-diversification rule (mandatory).** Picking a different macrostructure isn't enough on its own — two consecutive Hallmark outputs can share a theme even if their structures differ, and the result reads as repetition. Two consecutive themes must differ on **at least one** of three axes:
|
||||
|
||||
- **Paper band** — dark (L < 30 %) / mid (30–85 %) / light (> 85 %), per the theme's `--color-paper` lightness
|
||||
- **Display style** — italic-serif (Specimen, Studio, Atelier) / roman-serif (Newsprint, Salon, Linen) / geometric-sans (Pastel, Manifesto) / mono (Terminal) / display-condensed-italic (Sport) / display-heavy (Brutal) / system-native (Quiet) / risograph-bold (Riso)
|
||||
- **Accent hue** — warm (red / orange / amber: 10–60°) / cool (blue / indigo / cyan: 200–300°) / neutral (no chromatic accent: Quiet) / chromatic-other (green: Studio · sage: Garden · phosphor: Terminal)
|
||||
|
||||
If the previous output was Specimen (light · italic-serif · warm), the next can be Studio (light · italic-serif · chromatic-green) — the *accent hue* differs. But the next can't be Salon (light · roman-serif · warm) which only differs on display style and shares both paper band and accent — pick a more distant theme.
|
||||
|
||||
The per-theme axis values live as comments at the top of each theme's tokens block in [`site/css/tokens.css`](../site/css/tokens.css). When in doubt, name your candidate theme out loud and identify its three axis values; if two of three match the previous output, redirect.
|
||||
|
||||
**State your pick.** Before writing any code, say "Macrostructure: <name>. Theme: <name>. Differs from the last on: <axes>." in plain text. This is a deliberate accountability step — picking on the page (not in your head) prevents the default-attractor sameness that kept the skill emitting Specimen output.
|
||||
|
||||
If the brief is genuinely vague (no theme, no tone), do **not** default. Offer the user three macrostructures from *categorically different* groups (e.g. one grid-led like Bento, one document-led like Long Document, one poster-led like Manifesto). Three concrete choices, not seven abstract tones.
|
||||
|
||||
The macrostructure picks five of the six structural axes for you; you only need to pick the reveal yourself. The deeper axis catalogue is still in [`references/structure.md`](references/structure.md) when you need to deviate from the macrostructure's defaults.
|
||||
|
||||
### 2.5. Check project memory
|
||||
|
||||
If the project has a `.hallmark/log.json` file (created by previous Hallmark runs), **read it before** picking the macrostructure or theme. The schema is a JSON array, newest entry first:
|
||||
|
||||
```json
|
||||
[
|
||||
{ "date": "2026-04-30", "macrostructure": "Bento Grid", "theme": "Pastel", "enrichment": "E1 clipped-edge", "brief": "Tracejam · SaaS observability" },
|
||||
{ "date": "2026-04-28", "macrostructure": "Long Document","theme": "Linen", "enrichment": "E5 hand-built SVG", "brief": "Maple Street Bread · bakery" },
|
||||
{ "date": "2026-04-25", "macrostructure": "Manifesto", "theme": "Manifesto","enrichment": "none", "brief": "Meridian · studio manifesto" }
|
||||
]
|
||||
```
|
||||
|
||||
Use the **last 3–5 entries** to inform diversification:
|
||||
- Your macrostructure pick must not match any of the last three.
|
||||
- Your theme pick must differ from the last on at least one axis (see the theme-diversification rule above).
|
||||
- Your enrichment pick should not be the same enrichment archetype as the last (`E1 clipped` twice in a row reads as templated, even with different content).
|
||||
|
||||
If the file doesn't exist, this is the first Hallmark run for this project — no constraint, but **you'll create the file in Step 5**.
|
||||
|
||||
If the project has a CSS stamp but no `log.json`, infer one entry from the stamp and proceed.
|
||||
|
||||
### 3. Load the visual ruleset
|
||||
|
||||
The non-negotiables live in [`references/`](references/). Read only what you need:
|
||||
@@ -121,10 +152,11 @@ Always:
|
||||
- For each interaction in the output (button, input, modal, toast, drag, copy, etc.), apply the recipe in [`microinteractions.md`](references/microinteractions.md). Pick *silent success* over celebratory toasts. Pick *optimistic update + Undo* over confirmation dialogs. Pick *delay 800ms* on hover tooltips and *0ms* on focus tooltips.
|
||||
- Cut motion before adding it. Most pages have too much, not too little. If removing an animation wouldn't lose the user information, remove it.
|
||||
- **Stamp the output.** The first non-empty line of the produced CSS file (or the top of `<style>` if inline) MUST be a comment of the form: `/* Hallmark · macrostructure: <name> · tone: <tone> · anchor hue: <hue> */`. This stamp is the durable record of what you chose. The next time Hallmark runs in this project, it reads the stamp and picks a *different* macrostructure.
|
||||
- **Append to project memory.** After you write the stamp, update (or create) `.hallmark/log.json` at the project root. Append a new entry at the **front** of the array: `{ "date": "<YYYY-MM-DD>", "macrostructure": "<name>", "theme": "<name>", "enrichment": "<E# name or 'none'>", "brief": "<one-line summary>" }`. Trim the file to the last 20 entries (rotate the oldest off). Create `.hallmark/` and the file if they don't exist; respect any existing `.gitignore` (the user may or may not want this committed). This file is what Step 2.5 reads on the next run.
|
||||
|
||||
### 6. The slop test
|
||||
|
||||
Before handing back, run the output through these thirty-three questions. Every answer must be **no**.
|
||||
Before handing back, run the output through these thirty-five questions. Every answer must be **no**.
|
||||
|
||||
**Visual:**
|
||||
|
||||
@@ -177,6 +209,11 @@ Before handing back, run the output through these thirty-three questions. Every
|
||||
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 `<svg>`, custom-art `<div>`, `<canvas>`, 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.
|
||||
|
||||
If any answer is yes, fix it. Do not ship slop.
|
||||
|
||||
---
|
||||
|
||||
@@ -652,3 +652,56 @@ When building a section:
|
||||
5. If the macrostructure suggests a default (e.g., Bento Grid → F1 Bento), use it; if it doesn't suggest, vary deliberately.
|
||||
|
||||
The goal is composed variety — within a page, sections feel different from each other; across pages Hallmark builds, sections feel different from the last.
|
||||
|
||||
---
|
||||
|
||||
## Mobile collapse — per archetype
|
||||
|
||||
Every archetype has a defined collapse behaviour at narrow viewports. The two breakpoints to know:
|
||||
|
||||
- **60 rem (~960 px)** — the *layout* breakpoint. Multi-column grids collapse to single column. Tilts and clip effects drop. Sticky panes unstick.
|
||||
- **40 rem (~640 px)** — the *typography* breakpoint. Display sizes shrink one step. Side-margin labels move inline. Annotations consolidate.
|
||||
|
||||
Below 60 rem the archetype must still feel like itself — same hierarchy, same tone, same rhythm — but in a stacked single-column form. Below 40 rem the page is a phone; treat space like a luxury.
|
||||
|
||||
| Archetype | Below 60 rem | Below 40 rem |
|
||||
| --- | --- | --- |
|
||||
| **H1 Marquee** | unchanged (typography-only; centres / left-biases naturally) | display size step down (`xl` → `lg`); reduce side padding |
|
||||
| **H2 Split Diptych** | grid `1fr` (text top, proof column below); divider becomes hairline-rule between | proof column collapses to a 2-column compact grid for items |
|
||||
| **H3 Quote-Led** | quote stays full width; attribution wraps to its own line | quote size step down; attribution font-size step down |
|
||||
| **H4 Stat-Led** | number stays full width, text stacks below; secondary stats become 2-up grid | number size step down (`clamp` floor lifts); qualifier text wraps |
|
||||
| **H5 Letter** | unchanged single column; aside (if present) moves below body, divider becomes top border | salutation size step down; signoff tightens |
|
||||
| **H6 Photographic** | image stays full-bleed; caption moves from absolute corner to inline below image | caption font-size step down; corner caption never overlaps text on phones |
|
||||
| **H7 Demo Video Clipped-Edge** | **drops the clip**; goes `1fr` stacked, full-width media; tilt removed (clipping at 375 px reads as broken) | media reduces to 16/9; poster image used (auto-playing on cellular is hostile) |
|
||||
| **H8 Mockup Split** | drops the tilt; grid `1fr`; mockup goes full-width below text | annotation pins consolidate; numbered legend moves below mockup |
|
||||
| **H9 Custom Illustration** | grid `1fr`; illustration moves below text (or above — pick by tone) | illustration scales to ≤ 40 % viewport width; never dominates |
|
||||
| **F1 Bento** | grid drops from 6/4-col to 2-col; large tiles span 2; small tiles span 1 | drops to 1-col; tile order respects information priority |
|
||||
| **F2 Sticky-scroll stack** | sticky pane unsticks; content becomes linear sequence of paired text+visual blocks | the visuals shrink to 16/9 inline; no sticky behaviour at all |
|
||||
| **F3 Tabular spec sheet** | columns reduce: 4-col → 2 (key + value), drop unit + footnote | spec list goes vertical; each row is `dt` above `dd` |
|
||||
| **F4 Step sequence** | numbering moves from left margin to inline-with-step | step containers tighten; connector lines drop |
|
||||
| **F5 Annotated screenshot** | screenshot full-width; annotations restack as a numbered list below | screenshot 16/9; annotations consolidate into a legend |
|
||||
| **F6 Product card grid** | grid 3-up → 2-up | grid 2-up → 1-up; card height becomes flexible |
|
||||
| **C1 Outlined chip** | unchanged (chips wrap onto multiple lines if needed) | full-width single chip ; min-height 44 px hit target |
|
||||
| **C2 Inline form-as-CTA** | input + button stack vertically; full-width | label moves above input; button is full-width below |
|
||||
| **C3 Typographic link** | unchanged (links wrap naturally) | unchanged |
|
||||
| **C4 Sticky bottom bar** | unchanged (already designed for narrow); ensure 44 px min-height | label truncates if needed; CTA stays right-aligned |
|
||||
| **T1 Pull quote w/ marginalia** | marginalia move below quote; divider becomes hairline | marginalia consolidate into a single line |
|
||||
| **T2 Logo wall** | grid 6-up → 3-up | grid 3-up → 2-up; logo height step down (32 px → 24 px) |
|
||||
| **T3 Single huge quote** | quote remains full width; attribution wraps below | quote size step down by 1.4× |
|
||||
| **T4 Numbered stat strip** | strip 4-up → 2-up | strip becomes vertical; 1 stat per row |
|
||||
| **Ft1 Mast-headed** | links wrap to two lines; tagline below wordmark | wordmark size step down; tagline italicises in if not already |
|
||||
| **Ft2 Inline single line** | links wrap to multiple lines; separator becomes a soft return | becomes a vertical list |
|
||||
| **Ft3 Index columns** | grid 4-col → 2-col | grid 2-col → 1-col; column heads remain |
|
||||
| **Ft4 Dense colophon** | unchanged (mono/wraps naturally); reduce padding | font-size step down |
|
||||
| **N1 Wordmark + 2 links** | unchanged | links wrap to second line if long; wordmark stays |
|
||||
| **N2 Floating chip** | chip remains floating; reduce padding | chip widens to support 44 px hit target; never below 280 px |
|
||||
| **N3 Side-rail** | rail unsticks and becomes a hamburger trigger above | hamburger becomes the only nav |
|
||||
| **N4 ⌘K-only** | hamburger appears for users who don't know ⌘K | unchanged (⌘K equivalent is on-screen tap) |
|
||||
|
||||
**Cross-cutting rules:**
|
||||
|
||||
- All hit targets ≥ 44 × 44 px below 40 rem (WCAG AA). Never below.
|
||||
- Padding-inline ≥ `clamp(1rem, 4vw, 1.5rem)` on the page container so content doesn't kiss the screen edge.
|
||||
- Disable any scroll-linked animation below 40 rem (mobile scroll has its own physics; layered animations fight it).
|
||||
- Image `loading="lazy"` always below the fold; **never on the LCP element regardless of viewport.**
|
||||
- Auto-play video respects `data-saver` (`navigator.connection.saveData`) — replaces with poster when set.
|
||||
|
||||
@@ -62,3 +62,121 @@ Three beats:
|
||||
- Non-breaking space before units: `10 kg`, `5 min` (use ` ` or U+00A0).
|
||||
|
||||
If the text is loaded from a CMS, configure Smart Quotes in the CMS. If it's hard-coded, write it correctly.
|
||||
|
||||
---
|
||||
|
||||
## Voice samples per tone
|
||||
|
||||
The skill bends toward distribution-default copy ("Built for the modern team", "Unleash your X", "Where A meets B") unless given non-default voices to imitate. The samples below are *real opening lines* from sites that defy that distribution. **Imitate the kind of specificity** — named places, named dates, named verticals, refusal of metaphor, refusal of the verb — not the wording. The tone column maps to the seven tones the design-context gate (see [`SKILL.md`](../SKILL.md) Step 1) commits the user to.
|
||||
|
||||
### Editorial
|
||||
|
||||
Three voice patterns: *date-anchored*, *refusal of the verb*, *enumerative*.
|
||||
|
||||
- *"Creative direction, design and type for culture since 2003."* — apracticeforeverydaylife.com — date + named verticals
|
||||
- *"A monthly art publication featuring contributions by some of the most engaged thinkers working today."* — e-flux.com/journal — uses cadence (*monthly*) and a verb (*featuring*) that's specific
|
||||
- *"A thing well made."* — klim.co.nz — refusal of the verb, treats design as material
|
||||
- *"Frieze elevates the provocative and brilliant leading voices who shape and challenge today's art world."* — frieze.com — uses *challenge* instead of *empower*
|
||||
- *"We design everything for everyone."* — pentagram.com — refusal of marketing verbs; democratic claim
|
||||
- *"Writer + Photographer."* — craigmod.com — three words, two roles, no padding
|
||||
- *"Type, set with care."* — Hallmark Specimen — three words; the comma is the design
|
||||
- *"I'm a French design technologist based in London. I make websites and fonts, amongst other physical and digital artefacts."* — mathieutriay.com — named place, named deliverables, "artefacts" signals craft
|
||||
|
||||
### Brutalist
|
||||
|
||||
Three voice patterns: *flat declarative*, *refusal of metaphor*, *direct address with consequence*.
|
||||
|
||||
- *"The product development system for teams and agents."* — linear.app — flat declarative; no flourish, no "powered by AI"
|
||||
- *"Resend is the email API for developers. Send transactional and marketing emails at scale with a simple, modern API."* — resend.com — names the form factor, the audience, and what it does
|
||||
- *"Purpose-built for planning and building products. Designed for the AI era."* — linear.app — names the era plainly, no euphemism
|
||||
- *"WE ARE A STUDIO. WE ARE NOT A PLATFORM."* — Hallmark Meridian (test 04) — defines by refusal, all caps
|
||||
- *"We design products that last twelve years. We do not design products that need replacing every two."* — concrete number, paired declaration
|
||||
- *"A toolkit for assembling new worlds from the scraps of the old."* — are.na — second-position copy that breaks template
|
||||
- *"NO COMPROMISE."* — Hallmark Brutal — two words; the period is the design
|
||||
- *"We will not put our work behind a chatbot. We will answer the email ourselves."* — declarative refusal, two short sentences
|
||||
|
||||
### Soft
|
||||
|
||||
Three voice patterns: *poetic restraint*, *passion via enumeration*, *vulnerability with proof*.
|
||||
|
||||
- *"It's about time."* — cron.com — pun without winking; restraint
|
||||
- *"Time is our most precious resource."* — Notion Calendar — opens on the philosophical premise, then gets concrete
|
||||
- *"Designer for the Web (v. XIX)."* — lynnandtonic.com — version number signals craft-in-progress
|
||||
- *"Design engineer creating software that makes people feel something."* — rauno.me — emotional outcome over feature list; "feel something" avoids genre cliché
|
||||
- *"All I want to do is build websites. Typography, motion design, copywriting, performance — the web is an endless medium of opportunity."* — paco.me — passion via enumeration; vulnerability ("scratched the surface")
|
||||
- *"I craft UI demos that explore the power of the web and help others sharpen their skills."* — jhey.dev — names the verb (*craft*), names the audience (*others*)
|
||||
- *"Soft, but exact."* — Hallmark Pastel — two short adjectives, one comma, full stop
|
||||
- *"This page is soft because the surface should be soft. The rules underneath are not."* — pairs claim with refusal
|
||||
|
||||
### Technical
|
||||
|
||||
Three voice patterns: *spec-embedded prose*, *measured language*, *data-first opening*.
|
||||
|
||||
- *"The 14-inch MacBook Pro with M5 brings serious speed and advanced on-device AI to the personal, professional, and creative work you do every day."* — apple.com — spec embedded in prose; "serious speed" is measured language
|
||||
- *"434 total posts. New CSS you feel like you could use today."* — nerdy.dev (Adam Argyle) — data-first; "feel like" suggests genuine utility
|
||||
- *"$ streampipe parse access.log --filter status=5xx | jq"* — Streampipe (test 02) — open on a real command, not a marketing claim
|
||||
- *"Open the trace, find the span, fix the regression. No glossary required."* — Tracejam (test 05) — three concrete verbs, then a refusal
|
||||
- *"From stdin, through the pipe, into your dashboard."* — names the data path; refuses abstraction
|
||||
- *"23 spans · 4 services · 482 ms."* — Tracejam mockup — data is the headline
|
||||
- *"Read anything that emits lines. Files, pipes, sockets, kubectl logs."* — names the inputs, refuses generality
|
||||
- *"Drop-in OTLP. No agent, no sidecar."* — pairs claim with refusal of common alternatives
|
||||
|
||||
### Luxury
|
||||
|
||||
Three voice patterns: *heritage with specifics*, *refusal as sophistication*, *named scale*.
|
||||
|
||||
- *"The world's most acclaimed creative collective, where 23 partners work independently and collaboratively to shape the future of design."* — pentagram.com — heritage (implied longevity), named scale (23 partners)
|
||||
- *"By appointment."* — atelier-style — refusal as gatekeeping
|
||||
- *"A salon for the senses."* — Hallmark Salon — single nominal phrase, comma-free
|
||||
- *"A page should arrive like a person — composed, deliberate, in good clothes."* — Hallmark Salon — analogy treats the page as social
|
||||
- *"With pleasure, you are most welcome."* — Hallmark Salon salutation — formal address
|
||||
- *"Restraint, repeated, becomes a signature."* — Hallmark Atelier — three commas, four words, philosophical
|
||||
- *"A studied hand."* — three words; the determiner does the work
|
||||
- *"A small, opinionated craftsmanship engine that argues with your AI assistant on your behalf — and wins."* — Hallmark Atelier — names the role precisely, embraces the conflict
|
||||
|
||||
### Playful
|
||||
|
||||
Three voice patterns: *analogy via pop-culture*, *food/sensory metaphor*, *anticipated reaction*.
|
||||
|
||||
- *"Playlists, but for ideas."* — are.na — analogy that's also useful
|
||||
- *"Internet memory palace."* — are.na — three-word noun phrase, structural metaphor
|
||||
- *"Devouring details. Nourishing novelty. Deploying excellence."* — rauno.me — alliteration; food + tech metaphor
|
||||
- *"The kind that make you say, 'Wait, how did you do that?'"* — jhey.dev — direct address, anticipates the reader's reaction
|
||||
- *"Built to ship."* — Hallmark Sport — three words, declarative, with a verb that's a verb
|
||||
- *"Ready? You are two minutes from shipping."* — Hallmark Sport — a question, then a number
|
||||
- *"design like print: warm, off-register, intentional."* — Hallmark Riso — lowercase + colon + three modifiers
|
||||
- *"this is not a page that pretends to be paper. it is a page that remembers paper."* — Riso — refuses the imitation framing
|
||||
|
||||
### Austere
|
||||
|
||||
Three voice patterns: *extreme abbreviation*, *principle as opening*, *refusal of marketing language*.
|
||||
|
||||
- *"Hello."* — Hallmark Quiet salutation — one word; the period is the design
|
||||
- *"This is a page that doesn't try."* — Quiet — declares the position openly
|
||||
- *"Things Become Other Things."* — craigmod.com — three words; treats brand as essay title
|
||||
- *"Lightness above weightiness, elevate everyone you encounter."* — craigmod.com — principle-first positioning
|
||||
- *"A quiet skill."* — Hallmark Quiet — three words; the article is doing work
|
||||
- *"Software can be soft and exact at once. That's the trick."* — Hallmark Pastel — names the contradiction, names the resolution
|
||||
- *"One HTML file."* — Anya (test 06) footer — three words; the count is the boast
|
||||
- *"This page doesn't move."* — names the design decision openly
|
||||
|
||||
---
|
||||
|
||||
## Banned opening lines (anti-patterns)
|
||||
|
||||
These phrases appear across distribution-default LLM copy and reach for none of the specificity above. **Banned outright** — if you find yourself reaching for one, replace it with one of the patterns from the tone above.
|
||||
|
||||
| Phrase | Why it fails |
|
||||
| --- | --- |
|
||||
| *"Built for the modern team"* | Vague; assumes no specifics; temporal marketing |
|
||||
| *"Unleash your [X]"* | Hyperbolic; software can't unleash anything |
|
||||
| *"Where X meets Y"* | False synthesis; creative laziness |
|
||||
| *"Empower your..."* | Missionary language; avoids concrete benefit |
|
||||
| *"Reimagine the way you..."* | Suggests dissatisfaction before explaining need |
|
||||
| *"Supercharge your workflow"* | Energy metaphor without mechanics |
|
||||
| *"Innovative solutions"* | Meaningless; every product claims innovation |
|
||||
| *"Seamless integration"* | "Seamless" has no antonym; signals non-specificity |
|
||||
| *"In today's digital landscape"* | Temporal hand-wave; assumes the reader needs orientation |
|
||||
| *"Next-generation"* | Implies predecessor inadequacy; offers no differentiation |
|
||||
|
||||
If the brief gives you nothing to work with for an opening line, *say so to the user* and ask one question that elicits a specific noun, verb, or place. The user knows their product; the model is not allowed to invent specificity.
|
||||
|
||||
@@ -370,7 +370,252 @@ The next bakery brief Hallmark touches gets a *different* loaf — different cur
|
||||
|
||||
---
|
||||
|
||||
## Common mistakes — and the fixes
|
||||
## Recipe library
|
||||
|
||||
The bakery loaf above is one worked example. This library catalogues four more — each a small, complete, copy-paste-able recipe at Tier A or Tier B. Use them when the brief calls for the named subject; otherwise treat them as *technique references* (the workflow diagram's `stroke-dashoffset` flow is reusable; the mascot's blink-loop is reusable; etc.).
|
||||
|
||||
Each recipe ships with: a one-line description, full code, a "use when / avoid when" note, a `prefers-reduced-motion` fallback block, and a real-world inspiration line.
|
||||
|
||||
### Recipe 1 · Workflow / process diagram
|
||||
|
||||
Three labelled boxes connected by curved arrows. Slight asymmetric rotation (-1° on box one, +0.5° on box three) for hand-drawn feel. One arrow has an animated `stroke-dashoffset` flow suggesting data movement. Use case: feature page showing data flow, decision tree, or user journey steps.
|
||||
|
||||
```html
|
||||
<svg class="flow" viewBox="0 0 720 200" role="img" aria-label="Data flow: input, process, output">
|
||||
<defs>
|
||||
<marker id="flow-arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L8,3 L0,6" fill="currentColor" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<g class="flow__step flow__step--a">
|
||||
<rect class="flow__box" x="20" y="55" width="160" height="90" rx="0" />
|
||||
<text class="flow__label" x="100" y="105" text-anchor="middle">Input</text>
|
||||
</g>
|
||||
|
||||
<path class="flow__arrow flow__arrow--live" d="M 180 100 Q 220 80 260 100" marker-end="url(#flow-arrow)" />
|
||||
|
||||
<g class="flow__step">
|
||||
<rect class="flow__box" x="260" y="55" width="200" height="90" rx="0" />
|
||||
<text class="flow__label" x="360" y="100" text-anchor="middle">Parse + Filter</text>
|
||||
<text class="flow__sub" x="360" y="118" text-anchor="middle">small predicate language</text>
|
||||
</g>
|
||||
|
||||
<path class="flow__arrow" d="M 460 100 Q 500 120 540 100" marker-end="url(#flow-arrow)" />
|
||||
|
||||
<g class="flow__step flow__step--c">
|
||||
<rect class="flow__box" x="540" y="55" width="160" height="90" rx="0" />
|
||||
<text class="flow__label" x="620" y="105" text-anchor="middle">Output</text>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
```css
|
||||
@property --flow-dash {
|
||||
syntax: "<length>";
|
||||
initial-value: 0px;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
.flow { width: 100%; max-width: 48rem; height: auto; display: block; margin: 0 auto; color: var(--color-ink); }
|
||||
.flow__box { fill: none; stroke: currentColor; stroke-width: 1.5; }
|
||||
.flow__label { font-family: var(--font-display); font-size: 16px; fill: var(--color-ink); }
|
||||
.flow__sub { font-family: var(--font-mono); font-size: 11px; fill: var(--color-muted); }
|
||||
.flow__step--a { transform: rotate(-1deg); transform-origin: 100px 100px; }
|
||||
.flow__step--c { transform: rotate(0.5deg); transform-origin: 620px 100px; }
|
||||
.flow__arrow { fill: none; stroke: var(--color-muted); stroke-width: 1.5; stroke-linecap: round; }
|
||||
.flow__arrow--live {
|
||||
stroke: var(--color-accent);
|
||||
stroke-dasharray: 6 6;
|
||||
animation: flow 2.4s linear infinite;
|
||||
}
|
||||
@keyframes flow { to { stroke-dashoffset: -24; } }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.flow__arrow--live { animation: none; stroke-dasharray: 0; }
|
||||
}
|
||||
```
|
||||
|
||||
**Use when** the brief is "show the user how data flows" — feature page, docs landing, technical-narrative section. **Avoid when** the diagram has more than five nodes (use Mermaid or a real graph layout) or when relationships are non-linear (this recipe assumes left-to-right flow).
|
||||
|
||||
*Inspiration:* Lynn Fisher's `lynnandtonic.com` `<rect>`-rotation experiments; Rauno Freiberg's `stroke-dashoffset` flows on rauno.me.
|
||||
|
||||
### Recipe 2 · Minimal-line mascot
|
||||
|
||||
A small SVG character — face only, ~120 × 120 px — that has personality without anthropomorphic uncanny-valley risk. Two ellipse eyes (with `@keyframes blink` 3s loop), a single quadratic-curve mouth, and two stem accents (hair / hat / horns / antennae). Pairs beside text.
|
||||
|
||||
```html
|
||||
<figure class="mascot" aria-label="The Hallmark mascot — a face with two eyes and a small smile">
|
||||
<svg viewBox="0 0 120 130" class="mascot__svg">
|
||||
<circle class="mascot__head" cx="60" cy="60" r="42" />
|
||||
|
||||
<ellipse class="mascot__eye mascot__eye--l" cx="46" cy="56" rx="4" ry="6" />
|
||||
<ellipse class="mascot__eye mascot__eye--r" cx="74" cy="56" rx="4" ry="6" />
|
||||
|
||||
<path class="mascot__mouth" d="M 50 76 Q 60 84 70 76" />
|
||||
|
||||
<path class="mascot__accent" d="M 32 22 Q 40 12 52 18" />
|
||||
<path class="mascot__accent" d="M 88 22 Q 80 12 68 18" />
|
||||
</svg>
|
||||
</figure>
|
||||
```
|
||||
|
||||
```css
|
||||
.mascot { display: inline-block; width: 80px; height: 86px; margin: 0; vertical-align: -8px; }
|
||||
.mascot__svg { width: 100%; height: 100%; color: var(--color-ink); }
|
||||
.mascot__head { fill: color-mix(in oklch, var(--color-paper-2) 100%, var(--color-accent) 6%); stroke: var(--color-ink); stroke-width: 2; }
|
||||
.mascot__eye { fill: var(--color-ink); animation: blink 5s ease-in-out infinite; }
|
||||
.mascot__eye--r { animation-delay: 80ms; } /* one eye lags slightly — feels organic */
|
||||
@keyframes blink {
|
||||
0%, 8%, 92%, 100% { ry: 6px; }
|
||||
12%, 14% { ry: 0.8px; }
|
||||
}
|
||||
.mascot__mouth { fill: none; stroke: var(--color-ink); stroke-width: 1.6; stroke-linecap: round; }
|
||||
.mascot__accent { fill: none; stroke: var(--color-accent); stroke-width: 1.2; opacity: 0.6; stroke-linecap: round; }
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.mascot:hover .mascot__head { fill: color-mix(in oklch, var(--color-paper-2) 100%, var(--color-accent) 12%); transition: fill 240ms cubic-bezier(0.16, 1, 0.3, 1); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mascot__eye { animation: none; }
|
||||
}
|
||||
```
|
||||
|
||||
**Use when** a small product / studio / indie brand needs personality without the uncanny-valley risk of a generated character. **Avoid when** the mascot needs to be expressive across many states (use Rive instead — the @property route is for simple loops, not articulated emotion).
|
||||
|
||||
*Inspiration:* Are.na's reductive-aesthetic site mark; the Mailchimp Freddie family (single-colour confidence); Diana Smith's CSS-art portrait constraints.
|
||||
|
||||
### Recipe 3 · Three-tier architectural diagram
|
||||
|
||||
Browser → API → Database, drawn at ~16/9 with three labelled boxes and animated `stroke-dasharray` flow lines using `@property --flow-offset`. The data-flow lines pulse to suggest live traffic. Use case: a developer-tool landing page showing where the product fits in the stack.
|
||||
|
||||
```html
|
||||
<svg class="arch" viewBox="0 0 320 180" role="img" aria-label="Three-tier architecture: browser, API, database">
|
||||
<g class="arch__tier">
|
||||
<rect x="14" y="50" width="76" height="80" />
|
||||
<text x="52" y="86" text-anchor="middle" class="arch__name">Browser</text>
|
||||
<text x="52" y="104" text-anchor="middle" class="arch__sub">React / Next</text>
|
||||
</g>
|
||||
|
||||
<line class="arch__flow" x1="90" y1="90" x2="120" y2="90" />
|
||||
<text class="arch__hop" x="105" y="80" text-anchor="middle">HTTPS · OTLP</text>
|
||||
|
||||
<g class="arch__tier arch__tier--mid">
|
||||
<rect x="120" y="50" width="80" height="80" />
|
||||
<text x="160" y="86" text-anchor="middle" class="arch__name">API</text>
|
||||
<text x="160" y="104" text-anchor="middle" class="arch__sub">Edge runtime</text>
|
||||
</g>
|
||||
|
||||
<line class="arch__flow arch__flow--reverse" x1="200" y1="90" x2="230" y2="90" />
|
||||
<text class="arch__hop" x="215" y="80" text-anchor="middle">SQL · gRPC</text>
|
||||
|
||||
<g class="arch__tier">
|
||||
<rect x="230" y="50" width="76" height="80" />
|
||||
<text x="268" y="86" text-anchor="middle" class="arch__name">Database</text>
|
||||
<text x="268" y="104" text-anchor="middle" class="arch__sub">Postgres + vec</text>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
```css
|
||||
@property --flow-offset {
|
||||
syntax: "<number>";
|
||||
initial-value: 0;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
.arch { width: 100%; max-width: 48rem; height: auto; color: var(--color-ink); display: block; margin: 0 auto; }
|
||||
|
||||
.arch__tier rect {
|
||||
fill: var(--color-paper-2);
|
||||
stroke: var(--color-ink);
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
.arch__tier--mid rect {
|
||||
fill: color-mix(in oklch, var(--color-paper-2) 100%, var(--color-accent) 8%);
|
||||
stroke: color-mix(in oklch, var(--color-accent) 60%, var(--color-ink));
|
||||
}
|
||||
|
||||
.arch__name { font-family: var(--font-display); font-size: 11px; font-weight: 500; fill: var(--color-ink); }
|
||||
.arch__sub { font-family: var(--font-mono); font-size: 8px; fill: var(--color-muted); }
|
||||
.arch__hop { font-family: var(--font-mono); font-size: 7px; fill: var(--color-muted); letter-spacing: 0.04em; }
|
||||
|
||||
.arch__flow {
|
||||
stroke: var(--color-accent);
|
||||
stroke-width: 1.4;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 4 4;
|
||||
animation: arch-flow 1.6s linear infinite;
|
||||
}
|
||||
.arch__flow--reverse { animation-direction: reverse; }
|
||||
@keyframes arch-flow { to { stroke-dashoffset: -8; } }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.arch__flow { animation: none; stroke-dasharray: 0; }
|
||||
}
|
||||
```
|
||||
|
||||
**Use when** the brief is a developer-facing product that needs to show its position in a stack — observability tools, edge functions, ORMs, ingestion services. **Avoid when** the architecture has more than five tiers or non-linear topology (this recipe is for the "three-box flow" model only; for graph-like topologies, use a real diagram tool and embed an SVG export).
|
||||
|
||||
*Inspiration:* Vercel's network/edge diagrams; Diana Smith's structural precision in placing geometry.
|
||||
|
||||
### Recipe 4 · Botanical leaf flourish
|
||||
|
||||
A small (~40 × 80 px) hand-drawn sprig with two asymmetric leaves at +25° and -30° rotations. Leaf veins at 0.6 opacity. Sized to sit beside a headline as an inline accent. Pure SVG, no animation by default (the design is the stillness).
|
||||
|
||||
```html
|
||||
<svg class="sprig" viewBox="0 0 40 80" aria-hidden="true">
|
||||
<path class="sprig__stem" d="M 20 76 Q 18 56 21 36 Q 22 22 20 8" />
|
||||
|
||||
<g transform="translate(8 38) rotate(-25)">
|
||||
<ellipse class="sprig__leaf" cx="0" cy="0" rx="6" ry="11" />
|
||||
<path class="sprig__vein" d="M 0 -10 Q 1 0 0 10" />
|
||||
</g>
|
||||
|
||||
<g transform="translate(28 52) rotate(30)">
|
||||
<ellipse class="sprig__leaf" cx="0" cy="0" rx="6" ry="11" />
|
||||
<path class="sprig__vein" d="M 0 -10 Q -1 0 0 10" />
|
||||
</g>
|
||||
|
||||
<path class="sprig__stem" d="M 20 22 Q 16 19 13 22" />
|
||||
</svg>
|
||||
```
|
||||
|
||||
```css
|
||||
.sprig {
|
||||
width: 32px;
|
||||
height: 64px;
|
||||
display: inline-block;
|
||||
vertical-align: -0.6em;
|
||||
margin-inline-end: 0.4em;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.sprig__stem { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
|
||||
.sprig__leaf { fill: none; stroke: currentColor; stroke-width: 1.4; }
|
||||
.sprig__vein { fill: none; stroke: currentColor; stroke-width: 0.9; opacity: 0.6; stroke-linecap: round; }
|
||||
|
||||
/* Use beside a headline */
|
||||
h1.has-flourish { display: flex; align-items: baseline; gap: 0.4em; }
|
||||
```
|
||||
|
||||
**Use when** the brief is a bakery, restaurant, café, boutique, herbalist, florist, atelier — anything where a hand-drawn signal of *care* fits the brand. **Avoid when** the brand is technical, brutalist, or quietly austere (the sprig adds warmth where the page wants restraint).
|
||||
|
||||
*Inspiration:* hand-drawn botanical assets in old broadsheet papers; restaurant menus from Lisbon and Tokyo; Lynn Fisher's constraint-driven simplicity (this recipe could have been *A Single Div* with cleverer clip-paths, but SVG is more legible at small scale).
|
||||
|
||||
---
|
||||
|
||||
### Cross-recipe techniques
|
||||
|
||||
What all four recipes share — the four habits of hand-built CSS/SVG illustration in 2026:
|
||||
|
||||
1. **`@property` for declarative interpolation.** Animating a typed custom property (`<length>`, `<number>`, `<angle>`, `<color>`) gives you GPU-composited animation with zero JS. The bakery loaf, the workflow flow line, the architectural data-flow, and the mascot's blink — all use it.
|
||||
2. **Asymmetric `transform: rotate()` for hand-drawn feel.** The workflow boxes rotate at ±1°, the mascot's eyes have an 80 ms delay between them, the sprig's leaves rotate +25° / -30°. Symmetry reads as algorithmic; controlled asymmetry reads as drawn.
|
||||
3. **Opacity layering for pencil/secondary detail.** The workflow's reverse arrow is `var(--color-muted)`; the architectural sub-labels are 60% opacity; the sprig veins are 0.6 opacity. The hierarchy of opacity is the hierarchy of attention.
|
||||
4. **Mono labels grounding decorative work in function.** The architectural diagram's `arch__sub` text uses `var(--font-mono)` at 8 px. The workflow's "small predicate language" uses mono. Decorative work earns its place by being legible and accurate; mono signals that.
|
||||
|
||||
Use these recipes verbatim when they fit, or strip them for technique when the brief calls for something different. The point is that *every illustration on a Hallmark page is built, not picked.*
|
||||
|
||||
- **Reaching for Lottie when CSS would do.** The new tell. Build the loaf in pure CSS or hand-built SVG; the Lottie is the shortcut that costs you.
|
||||
- **Importing 50 KB of GSAP for a single fade-in.** Use `transition: opacity 400ms var(--ease-out)`. Zero bytes.
|
||||
|
||||
@@ -71,6 +71,8 @@ A display headline left, a demo video right, and the rightmost ~10–20 % of the
|
||||
- Aspect ratio (16/10 · 16/9 · 4/3)
|
||||
- Frame treatment (hairline 1 px frame · browser chrome · none)
|
||||
|
||||
**Example.** Tracejam (SaaS observability — see [`site/_tests/05-tracejam-saas/`](../../site/_tests/05-tracejam-saas/)). Display headline left ("Distributed tracing that explains itself."); hand-built CSS-art trace waterfall right, tilted -0.4°, extending 12 vw past the viewport's right edge. Aspect 16/10. Hairline frame. **Not a real video** — the mockup is custom-built CSS at Tier A (rectangles on a percentage grid simulating a flame chart). Mobile (< 60 rem): drop the clip, stack vertically.
|
||||
|
||||
```html
|
||||
<section class="hero hero--clipped">
|
||||
<div class="hero__copy">
|
||||
@@ -132,6 +134,8 @@ Video fills the fold, ghost-tinted via `mix-blend-mode: multiply` over a paper-c
|
||||
- Text alignment (left-bias / centred)
|
||||
- Pause behaviour (always-loop · pause-on-hover · pause-when-out-of-viewport)
|
||||
|
||||
**Example.** A small fashion brand's spring lookbook. 8-second muted loop of fabric draping in a studio. `mix-blend-mode: multiply` over a 0.5-opacity warm-cream overlay so the italic display headline ("Spring · 2026 · Lookbook 04") reads cleanly over the moving footage. Pauses on hover so the user can read the lede without distraction. Caption track (VTT) describes the footage for accessibility.
|
||||
|
||||
### E3 · Mock App Screenshot — Browser-framed split
|
||||
|
||||
Display headline left, a browser-frame mockup right, the mockup window slightly tilted (1–3°) for life. Frames are from [Browserframe](https://browserframe.com) or hand-built (a 1-px hairline + three macOS dots).
|
||||
@@ -144,6 +148,8 @@ Display headline left, a browser-frame mockup right, the mockup window slightly
|
||||
- Tilt angle (0° · 1.5° · 3°)
|
||||
- Screenshot count (1 · stack-of-3 · orbit-of-3)
|
||||
|
||||
**Example.** A Linear-style SaaS landing for a project tracker. Headline left ("Plan, build, ship."), browser-frame screenshot of the kanban view right, tilted 1.5° clockwise. Three numbered annotations (1 · assigns automatically · 2 · real-time presence · 3 · keyboard-first), each with a small numbered pin and a margin-aligned caption — never arrows-and-labels. Single screenshot, not a stack — fewer assets to load, sharper read.
|
||||
|
||||
### E4 · Mock App Screenshot — Floating no-frame
|
||||
|
||||
Same composition as E3 but without browser chrome — the screenshot floats with a soft shadow and 12 px corner radius. Cleaner; demands a higher-quality screenshot since the chrome isn't there to forgive.
|
||||
@@ -156,6 +162,8 @@ Same composition as E3 but without browser chrome — the screenshot floats with
|
||||
- Corner radius (0 · 8 px · 16 px)
|
||||
- Background reveal (gradient / solid / none)
|
||||
|
||||
**Example.** A code-formatting CLI marketing page. Headline left ("Format anything, in eight lines."), a single floating screenshot right showing `before` / `after` code side by side. 12 px corner radius, a soft 24 px shadow at -10 px offset, sitting on a barely-tinted gradient surface. **No browser chrome** — the screenshot itself is composed and beautiful enough to stand naked. Use this when the screenshot is unusually high-quality; otherwise switch to E3 (the chrome forgives messier captures).
|
||||
|
||||
### E5 · Custom Illustration Centerpiece
|
||||
|
||||
A hand-built SVG (the default, Tier B) or a generated raster (Tier C, when characters demand it) sitting on the hero as a single illustrative element — the bakery loaf, the studio's mascot, the diagram of how the workflow flows.
|
||||
@@ -168,7 +176,9 @@ A hand-built SVG (the default, Tier B) or a generated raster (Tier C, when chara
|
||||
- Animation (none · loop · scroll-linked)
|
||||
- Scale (small accent · dominant)
|
||||
|
||||
For *how* to build a hand-drawn loaf in 60 lines of SVG and animate its breath with `@property`, see [`custom-craft.md`](custom-craft.md) — there's a full bakery worked example.
|
||||
**Example.** Maple Street Bread (bakery — see [`site/_tests/03-maple-bakery/`](../../site/_tests/03-maple-bakery/)). Letter-style hero copy left ("Saturday, 6:14 a.m. The dough went in at midnight."), 60-line hand-built SVG loaf right, 3 paths (body, shade, score-marks). Animated with `@property --rise` for a subtle 4 px breathing-loop over 6 s, alternating; the score-marks draw themselves on first paint via `stroke-dasharray`. Tier B, dominant scale, animation: loop. Reduced-motion fallback is a static keyframe.
|
||||
|
||||
For *how* to build a hand-drawn loaf in 60 lines of SVG and animate its breath with `@property`, see [`custom-craft.md`](custom-craft.md) — there's a full bakery worked example, plus four more recipes (workflow diagram, mascot, architectural diagram, botanical accent).
|
||||
|
||||
### E6 · Animated Loop — pure CSS / SVG / Motion
|
||||
|
||||
@@ -182,6 +192,8 @@ A small custom-built loop — an orbiting dot, a breathing rectangle, an animate
|
||||
- Placement (margin · inline-with-headline · corner-accent)
|
||||
- Loop duration (≤ 4s — anything longer drags)
|
||||
|
||||
**Example.** A collaborative whiteboard app. A 2-second pure-CSS loop next to the headline: a single dot orbiting a slow ellipse, suggesting "real-time collaboration" without a Lottie. Built with `@property --angle` interpolating 0deg → 360deg on a `transform: rotate()`. Margin-placed, ~64 × 64 px, accent colour at low chroma. **Not a Lottie** — pure CSS keeps the bundle at zero bytes and respects reduced-motion gracefully (animation: none on the media query).
|
||||
|
||||
### E7 · Abstract Background — subtle gradient + grain
|
||||
|
||||
A two-colour CSS gradient at low chroma, overlaid with SVG `<feTurbulence>` grain at < 0.1 opacity. *Not* aurora; *not* purple-to-cyan mesh; *not* floating orbs. The point is *texture you can barely see* — paper-quality, not decoration.
|
||||
@@ -194,6 +206,8 @@ A two-colour CSS gradient at low chroma, overlaid with SVG `<feTurbulence>` grai
|
||||
- Grain amount (off · subtle · textured)
|
||||
- Animation (none · slow drift · scroll-linked parallax)
|
||||
|
||||
**Example.** A small podcast site (when the host wants more visual heat than Tide's typography-only quote). Two-stop CSS gradient at 135° (warm-cream → barely-orange, both at < 0.04 chroma) over the *hero only* — never page-wide. SVG `<feTurbulence>` grain overlay at 0.06 opacity, `mix-blend-mode: multiply`. No animation. Resists every aurora-blob temptation.
|
||||
|
||||
```html
|
||||
<section class="hero hero--bg">
|
||||
<div class="hero__bg" aria-hidden="true">
|
||||
@@ -226,6 +240,8 @@ A two-colour CSS gradient at low chroma, overlaid with SVG `<feTurbulence>` grai
|
||||
|
||||
Existing H6 archetype in the cookbook. Cross-referenced here for completeness. See [`component-cookbook.md`](component-cookbook.md) for variation knobs.
|
||||
|
||||
**Example.** A small Lisbon café. One tightly-cropped photograph of the espresso machine at dawn, 4/3 ratio, no full-bleed. Caption sits margin-aligned at lower-left in mono small-caps ("Plate 04 · 6:42 a.m."). The photograph is desaturated 8 % from the source to harmonise with the page's warm-paper tone. Always pair photography with a tone-matched typography pairing (see [`typography.md`](typography.md)) — a luxury-tone photo on a brutalist page jars.
|
||||
|
||||
---
|
||||
|
||||
## Animation discipline (hero specifically)
|
||||
|
||||
@@ -29,6 +29,11 @@ Avoid when the message is a single hero idea — Bento spreads attention; one-id
|
||||
|
||||
Reference: Apple in-page sections, Framer feature pages, Tailwind UI templates.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity*, not the wording):
|
||||
> *"Tracejam · v0.4 · for SREs. Distributed tracing that explains itself."* — paraphrased from real observability tools
|
||||
> *"Resend is the email API for developers. Send transactional and marketing emails at scale."* — resend.com
|
||||
> *"The product development system for teams and agents."* — linear.app
|
||||
|
||||
```html
|
||||
<header class="hero-fixed">…</header>
|
||||
<section class="bento">
|
||||
@@ -60,6 +65,11 @@ Avoid when there's a single decisive action to take — Long Document hides CTAs
|
||||
|
||||
Reference: Frank Chimero's site, destroytoday.com, long-form Substack essays in product disguise.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity*, not the wording):
|
||||
> *"Saturday, 6:14 a.m. The dough went in at midnight."* — opens with a time-stamp; the brand introduces itself as a moment in the day
|
||||
> *"A monthly art publication featuring contributions by some of the most engaged thinkers working today."* — e-flux.com/journal
|
||||
> *"We design everything for everyone."* — pentagram.com — refusal of the verb, treats design as universal practice
|
||||
|
||||
```html
|
||||
<article class="prose">
|
||||
<p class="lede">…</p>
|
||||
@@ -90,6 +100,11 @@ Avoid for products whose value requires explanation in seconds. Marquee makes th
|
||||
|
||||
Reference: 14islands.com, destroytoday.com, many design studio homepages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity*, not the wording):
|
||||
> *"Type, set with care."* — Hallmark Specimen — refusal of the verb, treats type as material
|
||||
> *"A studio for what's next."* — italic editorial display, names the practice without explaining it
|
||||
> *"Design like print: warm, off-register, intentional."* — Hallmark Riso — declarative, three modifiers, full stop
|
||||
|
||||
```html
|
||||
<section class="marquee">
|
||||
<h1 class="display-xxl">A statement.</h1>
|
||||
@@ -117,6 +132,11 @@ Avoid for products without a defensible single metric. A fake big number is wors
|
||||
|
||||
Reference: Ahrefs, Stripe Sessions stat blocks, climate-impact dashboards, venture firm portfolio pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity*, not the wording — the number does the work):
|
||||
> *"+47% · faster · decide late."* — italicised number, three-word qualifier
|
||||
> *"4 seconds. From the alert link to the slow span."* — pairs the number with what it bought
|
||||
> *"434 total posts. New CSS you feel like you could use today."* — adam argyle, nerdy.dev — the count grounds the page in real volume
|
||||
|
||||
```html
|
||||
<section class="stat-hero">
|
||||
<div class="figure tnum">99.97<span class="unit">%</span></div>
|
||||
@@ -145,6 +165,11 @@ Avoid when the product is conceptual or services-led. Workbench needs a UI to sh
|
||||
|
||||
Reference: Linear.app, Vercel, Raycast, Arc Browser.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity*, not the wording — the page walks the user through):
|
||||
> *"$ streampipe parse access.log --filter status=5xx | jq"* — open on a real command, not a marketing claim
|
||||
> *"Read anything that emits lines. Files, pipes, sockets, kubectl logs."* — names the inputs, refuses abstraction
|
||||
> *"Open the trace, find the span, fix the regression. No glossary required."* — three concrete verbs, then a refusal
|
||||
|
||||
```html
|
||||
<header class="lite">…</header>
|
||||
<section class="screenshot-frame">
|
||||
@@ -173,6 +198,11 @@ Avoid as the *primary* page. FAQ usually pairs with another macrostructure that
|
||||
|
||||
Reference: many SaaS pricing pages, Casper, Substack help pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — questions are real questions, answers are short and concrete):
|
||||
> *"What is this for? — A single-binary CLI for parsing log streams from stdin."* — names the form factor, names the input
|
||||
> *"How is this different from X? — It's about time."* — cron.com — answers obliquely, with a phrase that has weight
|
||||
> *"Who built this? — Three of us, in Lisbon, since 2014."* — date + place + count, no marketing
|
||||
|
||||
```html
|
||||
<section class="faq">
|
||||
<details>
|
||||
@@ -202,6 +232,11 @@ Avoid for transactional pages. Manifesto sells *agreement*, not action.
|
||||
|
||||
Reference: Linear's positioning pages, agency rebrand sites, political campaign landing pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — manifestos commit, they don't hedge):
|
||||
> *"WE ARE A STUDIO. WE ARE NOT A PLATFORM."* — Meridian (test 04) — defines by refusal, all caps, a single accent word
|
||||
> *"We design products that last twelve years. We do not design products that need replacing every two."* — concrete number, paired declaration
|
||||
> *"Lightness above weightiness, elevate everyone you encounter."* — craigmod.com — one-line principle, no explanation needed
|
||||
|
||||
```html
|
||||
<section class="manifesto bleed-ink">
|
||||
<h1 class="caps display-xxl">WE BELIEVE <em class="block-accent">DESIGN</em> IS SLOW.</h1>
|
||||
@@ -230,6 +265,11 @@ Avoid without real photography. AI-generated stock undoes the macrostructure.
|
||||
|
||||
Reference: Aimé Leon Dore, Mr Porter editorial, Stüssy lookbooks.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — captions for photographs are dates, places, plate numbers — not marketing):
|
||||
> *"Plate 47 · scored before the proof."* — Maple Street Bread (test 03) — a number, a moment in the process
|
||||
> *"Spring, 2026."* — Atelier-style — two words, full stop
|
||||
> *"From the working archive."* — gives the photograph provenance without explaining it
|
||||
|
||||
```html
|
||||
<section class="photo-fold">
|
||||
<img class="bleed" src="hero.jpg" />
|
||||
@@ -260,6 +300,11 @@ Avoid for new products without real testimonials. Fake quotes destroy trust on i
|
||||
|
||||
Reference: many B2B SaaS landings, agency homepages, university development pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — quote-led pages let someone else say it, then attribute):
|
||||
> *"I started listening on a long bus ride. By the third episode I'd missed my stop, and I didn't mind."* — Tide (test 01) — story-shaped, attributed to a listener
|
||||
> *"Restraint, repeated, becomes a signature."* — Hallmark Atelier — short, philosophical, signed by the studio
|
||||
> *"It told me the span that regressed, the deploy that caused it, and the engineer to ask. We rolled back in eight minutes."* — Tracejam-style — names the outcome with a number
|
||||
|
||||
```html
|
||||
<section class="quote-hero">
|
||||
<blockquote class="display-italic">"…"</blockquote>
|
||||
@@ -288,6 +333,11 @@ Reach for it ONLY when the brief is explicitly editorial, type-foundry, journal,
|
||||
|
||||
Reference: type foundry homepages (Klim, Pangram Pangram, Production Type), some editorial portfolios.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Specimen openings are foundry-voice, treating type as material culture):
|
||||
> *"A thing well made."* — klim.co.nz — refusal of the verb, treats design as material
|
||||
> *"Type, set with care."* — Hallmark Specimen — three words, a colon implied
|
||||
> *"Creative direction, design and type for culture since 2003."* — apracticeforeverydaylife.com — date-anchored, names verticals
|
||||
|
||||
```html
|
||||
<header class="specimen">
|
||||
<p class="num-label">01 — HELLO.</p>
|
||||
@@ -316,6 +366,11 @@ Avoid for narrative brands. Catalogue treats every item as equal — wrong for p
|
||||
|
||||
Reference: Klim Type Foundry, Pangram Pangram, Coolors palettes.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Catalogue openings are inventory headers, dated, with a count):
|
||||
> *"Today's loaves."* — Maple Street Bread (test 03) — two words, ownership of the day
|
||||
> *"Five collections, in store now."* — names the count and the where
|
||||
> *"Thirty-eight items · Spring 2026 · all hand-stitched."* — count, date, qualifier; no adjectives
|
||||
|
||||
---
|
||||
|
||||
## 12 · Letter
|
||||
@@ -335,6 +390,11 @@ Avoid for transactional commerce. Letter is intimate; commerce is functional.
|
||||
|
||||
Reference: Frank Chimero's site, founder farewell posts, indie newsletter front pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Letter openings are first-person greetings, dated, with a place if relevant):
|
||||
> *"Hello, I'm Anya."* — Anya (test 06) — single line, name, full stop
|
||||
> *"Saturday, 6:14 a.m. The dough went in at midnight."* — opens on a moment, then explains it
|
||||
> *"Hey there. This page is soft because the surface should be soft."* — Hallmark Pastel — colloquial open + a principle
|
||||
|
||||
---
|
||||
|
||||
## 13 · Index-First
|
||||
@@ -354,6 +414,11 @@ Avoid for marketing pages. Index-First is for browsing audiences; selling needs
|
||||
|
||||
Reference: Are.na's homepage feel, archive sites, documentation indices.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Index-First openings are headers for what's below, sometimes nothing more than a label):
|
||||
> *"Selected work · 2018 — 2026."* — date range, no preamble
|
||||
> *"Things Become Other Things · Lightness above weightiness."* — craigmod.com — two phrases joined by a divider
|
||||
> *"Writer + Photographer."* — craigmod.com — three words, summarises the whole site
|
||||
|
||||
---
|
||||
|
||||
## 14 · Narrative Workflow
|
||||
@@ -373,6 +438,11 @@ Avoid for tools that work in *one* moment. Narrative Workflow needs a real seque
|
||||
|
||||
Reference: Linear's how-it-works pages, some Figma marketing pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Narrative Workflow openings are stage labels: numbered, declarative, in process language):
|
||||
> *"01 · sourdough overnight · 02 · score at dawn · 03 · pull at seven."* — three numbered stages, no marketing
|
||||
> *"1.0 · parse · 2.0 · filter · 3.0 · route."* — Streampipe (test 02) — versioned numbers, three concrete verbs
|
||||
> *"I. We design products that last twelve years. II. A material is sustainable when someone, somewhere, can repair it."* — Meridian — Roman numerals + declarative
|
||||
|
||||
---
|
||||
|
||||
## 15 · Split Studio
|
||||
@@ -392,6 +462,11 @@ Avoid for narrative or photographic brands. Split halves the attention; some pag
|
||||
|
||||
Reference: Vercel feature pages, Stripe Sessions program pages, many dev-tool homepages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Split Studio openings pair a positioning statement with a proof column):
|
||||
> *"A studio for what's next."* — italic display + selected-work column on the right
|
||||
> *"Print discipline, on screen."* — Hallmark Newsprint — two-phrase headline, masthead-style
|
||||
> *"We design and build distinctive products for ambitious teams."* — names the verb (design and build), names the audience
|
||||
|
||||
---
|
||||
|
||||
## 16 · Feature Stack
|
||||
@@ -411,6 +486,11 @@ Avoid on mobile-first audiences without strong fallback. Sticky+scroll-sync is r
|
||||
|
||||
Reference: Apple product pages, some Stripe Sessions pages, Read.cv onboarding.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Feature Stack openings pin a single statement and then walk the user through):
|
||||
> *"Plan, build, ship."* — three verbs, three sticky panes
|
||||
> *"Read anything that emits lines."* — Streampipe (test 02) — names what's possible, then walks through it
|
||||
> *"From stdin, through the pipe, into your dashboard."* — names the data path, then explains each step
|
||||
|
||||
---
|
||||
|
||||
## 17 · Type Specimen
|
||||
@@ -430,6 +510,11 @@ Avoid when the brand uses an off-the-shelf face. Type Specimen needs something d
|
||||
|
||||
Reference: Klim Type Foundry, Pangram Pangram, Geist Pixel announcement pages.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Type Specimen openings are foundry-voice: name the typeface, the weights, the use):
|
||||
> *"Reckless Display, set in 96 pt."* — names the face and the size, nothing else
|
||||
> *"Eight weights. Three optical sizes. One good italic."* — counts the system in three short phrases
|
||||
> *"A type system for editorial."* — refusal of the verb, single noun phrase
|
||||
|
||||
---
|
||||
|
||||
## 18 · Portfolio Grid
|
||||
@@ -449,6 +534,11 @@ Avoid for products. Portfolio Grid is service-business shape.
|
||||
|
||||
Reference: Pentagram, 14islands, Locomotive, Bureau Borsche.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Portfolio Grid openings name the volume and the era):
|
||||
> *"Selected work · 2018 — 2026."* — date range, two characters of meta
|
||||
> *"Twelve projects, six clients, two countries."* — three counts, no adjective
|
||||
> *"Work, indexed by year."* — five-word labels, the index is the whole site
|
||||
|
||||
---
|
||||
|
||||
## 19 · Map / Diagram
|
||||
@@ -468,6 +558,11 @@ Avoid as a substitute for narrative. Some stories shouldn't be read spatially.
|
||||
|
||||
Reference: process visualisation sites, ecosystem maps, knowledge-graph products.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Map / Diagram openings name the path or the territory):
|
||||
> *"Browser → API → Database. Where Tracejam fits."* — names the layers, names the position
|
||||
> *"From stdin, through the pipe, into your dashboard."* — labels the spatial flow on the page below
|
||||
> *"Three tiers · two regions · one ledger."* — counts the system, geographic + structural
|
||||
|
||||
---
|
||||
|
||||
## 20 · Ecosystem Index
|
||||
@@ -487,6 +582,11 @@ Avoid for single-product pages. Ecosystem needs multiple things to surface.
|
||||
|
||||
Reference: Are.na, Figma Community, Behance.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Ecosystem Index openings are surface labels, dated, with a count):
|
||||
> *"Featured · Latest · By category."* — three discovery surfaces named, divided
|
||||
> *"What's on this week · Editor's pick · The whole catalogue."* — names cadence, curation, breadth
|
||||
> *"A toolkit for assembling new worlds from the scraps of the old."* — are.na — second-position copy that breaks template
|
||||
|
||||
---
|
||||
|
||||
## 21 · Component Playground
|
||||
@@ -506,6 +606,11 @@ Avoid for marketing pages. Playground is utility, not pitch.
|
||||
|
||||
Reference: shadcn/ui, Tailwind UI, Once UI, MUI demos, Framer Motion examples.
|
||||
|
||||
**Sample opening lines** (imitate the *specificity* — Component Playground openings are tangible: open on a real example, not a claim):
|
||||
> *"Try it inline. Then take it home."* — two short imperatives
|
||||
> *"Every example is editable. Every output is real."* — pairs claim with proof
|
||||
> *"From `npm install` to your first chart in eight lines."* — names the step count, makes a concrete promise
|
||||
|
||||
---
|
||||
|
||||
## How to pick
|
||||
|
||||
@@ -132,18 +132,21 @@ If you can't infer the domain, ask one question — "what does this thing do?"
|
||||
| --- | --- |
|
||||
| **podcast, audio, music, playlist, listening** | **Photographic** · **Quote-Led** · **Letter** |
|
||||
| **shop, store, product, merch, commerce, ecom** | **Catalogue** · **Photographic** · **Bento Grid** |
|
||||
| **docs, CLI, SDK, API, library, developer, terminal** | **Workbench** · **Long Document** · **Component Playground** |
|
||||
| **agency, studio, portfolio, freelance, creative shop** | **Portfolio Grid** · **Split Studio** · **Index-First** |
|
||||
| **docs, CLI, SDK, API, library, open source, developer reference** | **Workbench** · **Long Document** · **Component Playground** |
|
||||
| **platform, infra, observability, dashboard SaaS, B2B tool, try-or-talk-to-sales** | **Bento Grid** · **Workbench** · **Stat-Led** |
|
||||
| **agency, studio (work-led), case studies, multi-project portfolio, freelance creative** | **Portfolio Grid** · **Split Studio** · **Index-First** |
|
||||
| **personal one-pager, individual, about-me, resume (no case studies)** | **Long Document** · **Letter** · **Index-First** |
|
||||
| **restaurant, café, bar, food, kitchen, menu** | **Photographic** · **Long Document** · **Catalogue** |
|
||||
| **fashion, apparel, beauty, lookbook** | **Photographic** · **Catalogue** · **Marquee Hero** |
|
||||
| **fintech, banking, payments, invest, trading** | **Stat-Led** · **Workbench** · **Long Document** |
|
||||
| **personal, resume, one-pager, about-me, individual** | **Long Document** · **Letter** · **Index-First** |
|
||||
| **manifesto, campaign, cause, advocacy, political** | **Manifesto** · **Quote-Led** · **Stat-Led** |
|
||||
| **editorial, foundry, magazine, type, specimen** | **Specimen** · **Long Document** · **Type Specimen** |
|
||||
| **product launch, SaaS marketing, B2B** | **Bento Grid** · **Workbench** · **Stat-Led** |
|
||||
| **conference, event, speaker, keynote** | **Marquee Hero** · **Manifesto** · **Photographic** |
|
||||
| **fallback (genuinely no signal)** | **Bento Grid** · **Long Document** · **Manifesto** |
|
||||
|
||||
**Note on splits.** Some domains split on intent. *Developer-tool docs* and *developer-tool marketing* both have "developer" in them, but the docs page wants a Workbench walkthrough; the marketing page wants Bento Grid + Stat-Led so the SRE can read the value prop in 30 seconds. Same for *personal*: a one-pager about-me and a multi-project portfolio of case studies are *different briefs* — the one-pager wants prose (Long Doc / Letter); the portfolio wants Portfolio Grid / Split Studio. If the brief is ambiguous, **ask one question** to disambiguate ("docs walkthrough or marketing landing?", "one-pager or case studies?") before picking the trio.
|
||||
|
||||
If the user shrugs and says "you pick", read the project's CSS for a `/* Hallmark · macrostructure: ... */` stamp; whichever of the trio is most categorically distant from the stamped family is the right pick. Two consecutive outputs should never be from the same family — never two editorial macrostructures, never two grid-led macrostructures.
|
||||
|
||||
If the user answers a vague tone word ("modern", "clean", "professional"), that is not a feeling. Re-ask with the domain trio.
|
||||
|
||||
@@ -22,17 +22,28 @@ If the user insists on one, do it. Otherwise pick from the allowlist below.
|
||||
|
||||
## Pairing patterns that work
|
||||
|
||||
| Tone | Display | Body | Mono |
|
||||
| --- | --- | --- | --- |
|
||||
| Editorial | Fraunces, Tiempos, Newsreader | Söhne, Haffer, IBM Plex Sans | JetBrains Mono |
|
||||
| Technical | Söhne Mono, Berkeley Mono | IBM Plex Sans, Inter Display (*not* Inter) | Berkeley Mono |
|
||||
| Brutalist | Druk, Monument Extended, NaN Jaune | Neue Haas Grotesk, GT America | GT Pressura Mono |
|
||||
| Luxury | Canela, Tiempos Headline | Suisse Int'l, Söhne | — |
|
||||
| Playful | Clash Display, Cabinet Grotesk | Satoshi, Plus Jakarta Sans | Space Mono |
|
||||
| Austere | ABC Diatype, ABC Monument Grotesk | ABC Diatype, Söhne | ABC Diatype Mono |
|
||||
| Workshop (Hallmark's own) | The Future, Futura, Avenir Next | The Future, Söhne | The Future Mono, Berkeley Mono |
|
||||
Each tone gets two rows: a **free baseline** (everything Google-Fonts-or-similar; works out of the box) and a **paid upgrade** (foundry licences required; only when the user has confirmed the budget and the licence). The free row is the default. **Never name a paid font in code without confirming the user is licensed** — the demo will fall back to system-default and look broken to the user.
|
||||
|
||||
If the project's license budget is zero, free alternatives that are not on-distribution: **Fraunces, Newsreader, Space Grotesk, Plus Jakarta Sans, Bricolage Grotesque, Gabarito, Cabinet Grotesk (via Indian Type Foundry sample), JetBrains Mono, Geist, Geist Mono**.
|
||||
| Tone | Tier | Display | Body | Mono |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **Editorial** | Free | Fraunces · Newsreader · EB Garamond | IBM Plex Sans · Inter Tight | JetBrains Mono · Geist Mono |
|
||||
| | *Paid* | *Tiempos Headline · Söhne Breit · Reckless Display* | *Söhne · Haffer · Untitled Sans* | *Söhne Mono · GT America Mono* |
|
||||
| **Technical** | Free | JetBrains Mono · Geist Mono · Geist (700) | Geist · IBM Plex Sans | Geist Mono · JetBrains Mono |
|
||||
| | *Paid* | *Berkeley Mono · Söhne Mono · GT Pressura* | *Söhne · Untitled Sans* | *Berkeley Mono · GT Pressura Mono* |
|
||||
| **Brutalist** | Free | Inter Tight (heavy) · Anton · Bricolage Grotesque (800) | Inter · Geist | Geist Mono |
|
||||
| | *Paid* | *Druk · Monument Extended · NaN Jaune · Migra* | *Söhne Breit · GT America* | *GT America Mono* |
|
||||
| **Soft** | Free | Geist · Bricolage Grotesque (500) · Newsreader | Geist · Crimson Pro | Geist Mono |
|
||||
| | *Paid* | *Söhne · GT Pressura · Pangaia* | *Söhne · Halyard Text* | *Söhne Mono* |
|
||||
| **Luxury** | Free | Cormorant Garamond · Fraunces · Cardo | EB Garamond · Crimson Pro | (rare; if needed: JetBrains Mono) |
|
||||
| | *Paid* | *Canela · Tiempos Headline · GT Super · Domaine Display* | *Tiempos Text · Suisse Int'l · Domaine Text* | *(rarely used at this tier)* |
|
||||
| **Playful** | Free | Bricolage Grotesque · Fraunces (italic) · Newsreader (italic) | Geist · Newsreader | Geist Mono |
|
||||
| | *Paid* | *Clash Display · Cabinet Grotesk · Migra · Tobias* | *Satoshi · Plus Jakarta Sans · GT Maru* | *Space Mono · GT Maru Mono* |
|
||||
| **Austere** | Free | system-ui · Inter Tight (regular) · Geist (400) | system-ui · Geist | system-ui · Geist Mono |
|
||||
| | *Paid* | *ABC Diatype · ABC Monument Grotesk · Söhne (regular) · ABC Pressura* | *ABC Diatype · Söhne* | *ABC Diatype Mono · Söhne Mono* |
|
||||
| **Workshop** *(Hallmark's own theme)* | Free | The Future · Geist · Inter Tight | The Future · Söhne | The Future Mono · Geist Mono |
|
||||
| | *Paid* | *Avenir Next · GT Walsheim* | *Söhne · GT Walsheim* | *Berkeley Mono* |
|
||||
|
||||
**The discipline.** Default to the free pairings. They're not consolation prizes; Fraunces, Geist, Bricolage Grotesque, and JetBrains Mono are first-rate faces in 2026. The paid upgrades exist for two cases: (a) the user has explicitly confirmed they're licensed, or (b) the user is asking for a specific named foundry voice (e.g., "make it look like Klim", "I want Söhne"). Reach for Tier 2 only then; otherwise the free row is the right answer. Treat the free row as canon, the paid row as a *cited* alternative.
|
||||
|
||||
## Scale
|
||||
|
||||
|
||||
Reference in New Issue
Block a user