mirror of
https://github.com/Nutlope/hallmark.git
synced 2026-08-14 12:35:33 +02:00
Gallery cards: structural fix · SKILL.md adds Step 0 Pre-flight, rotation hints, Step 5 Preview
## Gallery card padding — restructural fix (replaces all prior patches)
The negative-margin trick (`width: calc(100% + 2 * --space-lg)` + `margin-inline: calc(-1 * --space-lg)` on `.gallery__thumb`) kept fighting CSS Grid's track-sizing in the presence of `aspect-ratio` + `overflow: hidden`. Diagnostic agent confirmed the three-way collision is unstable across browsers / themes.
Replaced with a structural split:
- `.gallery__item` is now a flush container (`display: block`, no padding, no grid).
- `.gallery__thumb` (the `<picture>`) sits as a direct child at the top: `width: 100%`, `aspect-ratio: 16/10`, no negative margins. Fills the card edge-to-edge naturally because there's no parent padding to bust out of.
- New `<div class="gallery__body">` wraps the five text spans. Carries `padding: var(--space-lg)` and the existing 4rem-num + 1fr-text grid layout.
HTML and CSS both restructured. Eight cards in `site/index.html` re-wrapped (~50 lines edited). `site/css/components.css` lines 1736–1810 rewritten (~30 line net change).
## SKILL.md — three additive disclosure layers
### Step 0 · Pre-flight scan (NEW)
Before the design-context gate, the skill scans the project for tone signals: font stack (next/font, fontsource, Tailwind fontFamily, Google Fonts links), palette (OKLCH custom-properties, Tailwind colors, tokens.json), microinteraction stance (framer-motion / gsap / motion / lenis / lottie-react in package.json), spacing scale (Tailwind extend.spacing or `--space-*` pattern), framework (Next/Astro/Vue/Svelte/Remix).
Outputs a one-block disclosure with file:line citations, persists findings to `.hallmark/preflight.json`. Re-uses cache on subsequent runs unless user says "refresh pre-flight" or `package.json` / `tailwind.config.*` mtimes are newer. Edge cases for vanilla HTML, conflicting signals, empty projects, "ignore the existing project" all spelled out. Two extra sample outputs included.
~70 lines added before existing Step 1.
### Step 2.5 · Macrostructure rotation hints (EXTENDED)
After reading `.hallmark/log.json`, the skill emits a one-liner before picking: *"Last 5 builds: Bento Grid (Tracejam) · Bento Grid (Foundry) · Long Document (Maple) · Manifesto (Meridian) · Quote-Led (Tide). Bento Grid used 2 of 5 — picking from {Marquee Hero, Stat-Led, Workbench, Letter} this time. I'll go with Marquee Hero."* Plus a theme rotation line with the diversification axes.
Three sample shapes (first-time / mature project / user-overrode-last) seed the model's imitation. Surfacing the rotation makes the skill's discipline visible — users see WHY a different macrostructure was picked.
~20 lines added inside Step 2.5.
### Step 5 · Preview block (NEW)
Inserted between Step 4 (hero enrichment) and the renumbered Step 6 (Build). Slop test renumbered to Step 7.
Before emitting code, the model outputs a tight Markdown bullet summary — six required bullets (Macrostructure, Theme, Enrichment, Sections, Motion, Slop test) plus an optional Diversification bullet when `.hallmark/log.json` has prior entries. Plain bullets, NOT ASCII boxes — they render reliably across every chat client and terminal.
Three sample preview blocks in the spec (Long Document / Bento Grid / Manifesto) show the format across macrostructure types. Slop test row reflects the Step 7 outcome — if a gate fails, return to Step 6, fix, re-emit.
The preview is the user's TL;DR — they should be able to scan it in five seconds and tell the skill to redirect BEFORE 500 lines of CSS that don't match intent.
~60 lines added.
## Step numbering after this commit
0. Pre-flight scan
1. Design-context gate
2. Pick a macrostructure
2.5. Check project memory (with rotation hints)
3. Load the visual ruleset
4. Decide on hero enrichment
5. Preview
6. Build (was Step 5)
7. The slop test (was Step 6)
References to Step 4 / Step 5 / Step 6 inside the file all updated for consistency.
## Skill mirrored
`cp skill/SKILL.md ~/.claude/skills/hallmark/SKILL.md` — installed copy now matches.
## Out of scope (deferred)
T2-A DTCG tokens.json export, T3-A theme knobs, T3-C Figma MCP recipe — all skipped per user direction; not the right fit for the current minimal-surface ethos.
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
6b2e701dc6
commit
63e1c18032
+24
-22
@@ -1733,18 +1733,11 @@
|
||||
.gallery { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* Card = flush container. NO padding here so the thumb fills edge-to-edge
|
||||
without negative-margin tricks. The inner .gallery__body carries the
|
||||
padding + grid for the text rows. */
|
||||
.gallery__item {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"thumb thumb"
|
||||
"num name"
|
||||
"num shape"
|
||||
"num desc"
|
||||
"num open";
|
||||
grid-template-columns: 4rem 1fr;
|
||||
grid-template-rows: auto repeat(4, auto);
|
||||
gap: 0 var(--space-md);
|
||||
padding: var(--space-lg);
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
background: var(--color-paper);
|
||||
@@ -1770,22 +1763,17 @@
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
/* Thumb = top of card, fills full width naturally. No negative margins,
|
||||
no width:calc(...). Just width:100% + aspect-ratio. */
|
||||
.gallery__thumb {
|
||||
grid-area: thumb;
|
||||
display: block;
|
||||
/* Width must EXCEED content area by 2*padding so the negative margins
|
||||
reach the card's inside-border edge on both sides. width: 100% would
|
||||
only fill the content area, leaving padding visible on the right. */
|
||||
width: calc(100% + 2 * var(--space-lg));
|
||||
margin-inline: calc(-1 * var(--space-lg));
|
||||
margin-block-start: calc(-1 * var(--space-lg));
|
||||
margin-block-end: var(--space-lg);
|
||||
width: 100%;
|
||||
border-block-end: var(--rule-card, 1px) solid var(--color-rule);
|
||||
background: var(--color-paper-2);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
font-size: 0; /* kill any inline-element gap */
|
||||
aspect-ratio: 16 / 10; /* explicit, so the image always fits */
|
||||
font-size: 0;
|
||||
aspect-ratio: 16 / 10;
|
||||
}
|
||||
|
||||
.gallery__thumb img {
|
||||
@@ -1793,10 +1781,24 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top left; /* keep hero region; crop empty right */
|
||||
object-position: top left;
|
||||
transition: transform 500ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Body = padded grid for text rows. Replaces the grid the card itself
|
||||
used to carry. Same row layout, same num-column width. */
|
||||
.gallery__body {
|
||||
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);
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.gallery__item:hover .gallery__thumb img,
|
||||
.gallery__item:focus-visible .gallery__thumb img {
|
||||
|
||||
+174
-94
@@ -1,14 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="specimen">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="color-scheme" content="light" />
|
||||
<title>Hallmark — A design skill that refuses to look AI-generated</title>
|
||||
<meta name="description" content="Hallmark is a design skill for AI coding assistants. It encodes the anti-AI-slop consensus — typography, colour, layout, motion, interaction — into one holistic ruleset. Powered by Together AI." />
|
||||
<meta name="description"
|
||||
content="Hallmark is a design skill for AI coding assistants. It encodes the anti-AI-slop consensus — typography, colour, layout, motion, interaction — into one holistic ruleset. Powered by Together AI." />
|
||||
|
||||
<meta property="og:title" content="Hallmark — A design skill that refuses to look AI-generated" />
|
||||
<meta property="og:description" content="A design skill for AI coding assistants. Encodes the anti-slop consensus into one ruleset. Powered by Together AI." />
|
||||
<meta property="og:description"
|
||||
content="A design skill for AI coding assistants. Encodes the anti-slop consensus into one ruleset. Powered by Together AI." />
|
||||
<meta property="og:type" content="website" />
|
||||
|
||||
<link rel="preload" href="fonts/the-future-regular.woff2" as="font" type="font/woff2" crossorigin />
|
||||
@@ -18,7 +21,8 @@
|
||||
<!-- Google Fonts — Fraunces (Specimen + Studio italic display), Geist (Pastel), Bricolage + Newsreader (Riso). -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;0,9..144,600;1,9..144,400;1,9..144,500&family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&display=swap" />
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;0,9..144,600;1,9..144,400;1,9..144,500&family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&display=swap" />
|
||||
|
||||
<link rel="stylesheet" href="css/tokens.css" />
|
||||
<link rel="stylesheet" href="css/base.css" />
|
||||
@@ -32,9 +36,10 @@
|
||||
const q = url.searchParams.get("theme");
|
||||
const t = q || localStorage.getItem("hallmark-theme");
|
||||
if (t) document.documentElement.dataset.theme = t;
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- BANNER — sticky top, thin, holds the theme picker -->
|
||||
@@ -49,27 +54,45 @@
|
||||
<span class="banner__center" data-theme-current>Specimen</span>
|
||||
|
||||
<div class="banner__picker" role="radiogroup" aria-label="Theme">
|
||||
<button class="banner__dot" type="button" data-theme-btn="specimen" aria-label="Specimen" style="--dot: oklch(96% 0.018 80); --dot-edge: oklch(70% 0.04 75);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="newsprint" aria-label="Newsprint" style="--dot: oklch(92% 0.045 50); --dot-edge: oklch(32% 0.10 28);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="atelier" aria-label="Atelier" style="--dot: oklch(94% 0.005 60); --dot-edge: oklch(45% 0.13 60);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="garden" aria-label="Garden" style="--dot: oklch(95% 0.030 130); --dot-edge: oklch(54% 0.16 14);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="salon" aria-label="Salon" style="--dot: oklch(94% 0.030 65); --dot-edge: oklch(40% 0.085 50);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="linen" aria-label="Linen" style="--dot: oklch(89% 0.022 95); --dot-edge: oklch(46% 0.06 95);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="almanac" aria-label="Almanac" style="--dot: oklch(94% 0.008 245); --dot-edge: oklch(38% 0.135 250);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="midnight" aria-label="Midnight" style="--dot: oklch(15% 0.022 250); --dot-edge: oklch(72% 0.16 220);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="terminal" aria-label="Terminal" style="--dot: oklch(11% 0.018 145); --dot-edge: oklch(78% 0.19 138);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="brutal" aria-label="Brutal" style="--dot: oklch(98% 0.001 0); --dot-edge: oklch(60% 0.21 27);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="manifesto" aria-label="Manifesto" style="--dot: oklch(10% 0.005 60); --dot-edge: oklch(60% 0.22 27);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="sport" aria-label="Sport" style="--dot: oklch(98% 0.003 250); --dot-edge: oklch(58% 0.190 35);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="studio" aria-label="Studio" style="--dot: oklch(96.5% 0.014 95); --dot-edge: oklch(46% 0.140 145);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="pastel" aria-label="Pastel" style="--dot: oklch(96% 0.022 285); --dot-edge: oklch(56% 0.130 282);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="riso" aria-label="Riso" style="--dot: oklch(91% 0.034 30); --dot-edge: oklch(58% 0.170 220);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="quiet" aria-label="Quiet" style="--dot: oklch(99.5% 0.002 90); --dot-edge: oklch(16% 0.005 240);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="specimen" aria-label="Specimen"
|
||||
style="--dot: oklch(96% 0.018 80); --dot-edge: oklch(70% 0.04 75);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="newsprint" aria-label="Newsprint"
|
||||
style="--dot: oklch(92% 0.045 50); --dot-edge: oklch(32% 0.10 28);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="atelier" aria-label="Atelier"
|
||||
style="--dot: oklch(94% 0.005 60); --dot-edge: oklch(45% 0.13 60);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="garden" aria-label="Garden"
|
||||
style="--dot: oklch(95% 0.030 130); --dot-edge: oklch(54% 0.16 14);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="salon" aria-label="Salon"
|
||||
style="--dot: oklch(94% 0.030 65); --dot-edge: oklch(40% 0.085 50);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="linen" aria-label="Linen"
|
||||
style="--dot: oklch(89% 0.022 95); --dot-edge: oklch(46% 0.06 95);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="almanac" aria-label="Almanac"
|
||||
style="--dot: oklch(94% 0.008 245); --dot-edge: oklch(38% 0.135 250);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="midnight" aria-label="Midnight"
|
||||
style="--dot: oklch(15% 0.022 250); --dot-edge: oklch(72% 0.16 220);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="terminal" aria-label="Terminal"
|
||||
style="--dot: oklch(11% 0.018 145); --dot-edge: oklch(78% 0.19 138);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="brutal" aria-label="Brutal"
|
||||
style="--dot: oklch(98% 0.001 0); --dot-edge: oklch(60% 0.21 27);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="manifesto" aria-label="Manifesto"
|
||||
style="--dot: oklch(10% 0.005 60); --dot-edge: oklch(60% 0.22 27);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="sport" aria-label="Sport"
|
||||
style="--dot: oklch(98% 0.003 250); --dot-edge: oklch(58% 0.190 35);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="studio" aria-label="Studio"
|
||||
style="--dot: oklch(96.5% 0.014 95); --dot-edge: oklch(46% 0.140 145);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="pastel" aria-label="Pastel"
|
||||
style="--dot: oklch(96% 0.022 285); --dot-edge: oklch(56% 0.130 282);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="riso" aria-label="Riso"
|
||||
style="--dot: oklch(91% 0.034 30); --dot-edge: oklch(58% 0.170 220);"></button>
|
||||
<button class="banner__dot" type="button" data-theme-btn="quiet" aria-label="Quiet"
|
||||
style="--dot: oklch(99.5% 0.002 90); --dot-edge: oklch(16% 0.005 240);"></button>
|
||||
|
||||
</div>
|
||||
|
||||
<button class="banner__shuffle" type="button" aria-label="Random theme · T cycles · R randomises" title="Click → random · T cycles · R randomises">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<button class="banner__shuffle" type="button" aria-label="Random theme · T cycles · R randomises"
|
||||
title="Click → random · T cycles · R randomises">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M16 3h5v5" />
|
||||
<path d="M4 20 21 3" />
|
||||
<path d="M21 16v5h-5" />
|
||||
@@ -99,9 +122,12 @@
|
||||
<div class="vs">
|
||||
<div class="vs__col">
|
||||
<div class="vs__head"><span class="vs__label">Without Hallmark</span></div>
|
||||
<div class="vs__demo"><div class="viz-purple-hero"></div></div>
|
||||
<div class="vs__demo">
|
||||
<div class="viz-purple-hero"></div>
|
||||
</div>
|
||||
<p class="vs__caption">
|
||||
Centred hero. Purple-to-pink gradient. Inter display. Startup-cliché tagline. Rounded-pill CTA. A view every LLM has produced ten thousand times.
|
||||
Centred hero. Purple-to-pink gradient. Inter display. Startup-cliché tagline. Rounded-pill CTA. A view every
|
||||
LLM has produced ten thousand times.
|
||||
</p>
|
||||
</div>
|
||||
<div class="vs__col">
|
||||
@@ -111,7 +137,8 @@
|
||||
<span class="title">A quiet <em>instrument.</em></span>
|
||||
</div>
|
||||
<p class="vs__caption">
|
||||
Asymmetric, left-biased. One accent, barely visible. A distinctive display face paired with a serif for emphasis. No gradient, no pill, no slop.
|
||||
Asymmetric, left-biased. One accent, barely visible. A distinctive display face paired with a serif for
|
||||
emphasis. No gradient, no pill, no slop.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,7 +162,8 @@
|
||||
<span class="spec__name">Typography</span>
|
||||
<span class="spec__num">F / 01</span>
|
||||
</header>
|
||||
<p class="spec__rule">Banned defaults. Distinctive display paired with a refined body. Commit to weight extremes.</p>
|
||||
<p class="spec__rule">Banned defaults. Distinctive display paired with a refined body. Commit to weight
|
||||
extremes.</p>
|
||||
<div class="spec__body type-spec">
|
||||
<span class="type-spec__display">Display face</span>
|
||||
<span class="type-spec__body">paired with a <em>workhorse</em> body.</span>
|
||||
@@ -147,7 +175,8 @@
|
||||
<span class="spec__name">Colour</span>
|
||||
<span class="spec__num">F / 02</span>
|
||||
</header>
|
||||
<p class="spec__rule">OKLCH only. One accent, under 3% of any view. No pure black. No pure white. No purple gradients.</p>
|
||||
<p class="spec__rule">OKLCH only. One accent, under 3% of any view. No pure black. No pure white. No purple
|
||||
gradients.</p>
|
||||
<div class="spec__body">
|
||||
<div class="swatch-row">
|
||||
<div class="swatch swatch--light" style="background: var(--color-paper);">Paper</div>
|
||||
@@ -164,7 +193,8 @@
|
||||
<span class="spec__name">Space</span>
|
||||
<span class="spec__num">F / 03</span>
|
||||
</header>
|
||||
<p class="spec__rule">4-point scale, named semantically. <code>gap</code> for siblings. Break the grid on purpose, not by accident.</p>
|
||||
<p class="spec__rule">4-point scale, named semantically. <code>gap</code> for siblings. Break the grid on
|
||||
purpose, not by accident.</p>
|
||||
<div class="spec__body" style="display:flex; align-items:end; gap:var(--space-2xs); height:3rem;">
|
||||
<span style="width:4px; height:25%; background:var(--color-ink);"></span>
|
||||
<span style="width:4px; height:35%; background:var(--color-ink);"></span>
|
||||
@@ -181,12 +211,14 @@
|
||||
<span class="spec__name">Motion</span>
|
||||
<span class="spec__num">F / 04</span>
|
||||
</header>
|
||||
<p class="spec__rule">Animate <code>transform</code> and <code>opacity</code> only. One orchestrated entrance beats ten micro-bounces.</p>
|
||||
<p class="spec__rule">Animate <code>transform</code> and <code>opacity</code> only. One orchestrated entrance
|
||||
beats ten micro-bounces.</p>
|
||||
<div class="spec__body">
|
||||
<svg class="curve" viewBox="0 0 200 80" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M 0 80 C 32 80, 60 8, 200 0" fill="none" stroke="currentColor" stroke-width="1.2" />
|
||||
<line x1="0" y1="80" x2="200" y2="80" stroke="var(--color-rule-2)" stroke-width="0.5" />
|
||||
<line x1="0" y1="0" x2="200" y2="0" stroke="var(--color-rule-2)" stroke-width="0.5" stroke-dasharray="2 2" />
|
||||
<line x1="0" y1="0" x2="200" y2="0" stroke="var(--color-rule-2)" stroke-width="0.5"
|
||||
stroke-dasharray="2 2" />
|
||||
</svg>
|
||||
<span class="label">cubic-bezier(0.16, 1, 0.3, 1) · 420 ms</span>
|
||||
</div>
|
||||
@@ -197,7 +229,8 @@
|
||||
<span class="spec__name">States</span>
|
||||
<span class="spec__num">F / 05</span>
|
||||
</header>
|
||||
<p class="spec__rule">Eight states, not two. <code>:focus-visible</code> ring. 44 px hit target. Labels above inputs.</p>
|
||||
<p class="spec__rule">Eight states, not two. <code>:focus-visible</code> ring. 44 px hit target. Labels
|
||||
above inputs.</p>
|
||||
<div class="spec__body" style="display:flex; flex-wrap:wrap; gap:var(--space-xs) var(--space-md);">
|
||||
<span class="label">default</span>
|
||||
<span class="label">hover</span>
|
||||
@@ -215,7 +248,8 @@
|
||||
<span class="spec__name">Responsive</span>
|
||||
<span class="spec__num">F / 06</span>
|
||||
</header>
|
||||
<p class="spec__rule">Mobile-first. Breakpoints where content breaks, not where a device sits. <code>dvh</code>, not <code>vh</code>.</p>
|
||||
<p class="spec__rule">Mobile-first. Breakpoints where content breaks, not where a device sits.
|
||||
<code>dvh</code>, not <code>vh</code>.</p>
|
||||
<div class="spec__body">
|
||||
<span class="dim">40 rem 60 rem 90 rem</span>
|
||||
</div>
|
||||
@@ -226,10 +260,12 @@
|
||||
<span class="spec__name">Copy</span>
|
||||
<span class="spec__num">F / 07</span>
|
||||
</header>
|
||||
<p class="spec__rule">Specific verbs. Three-part errors. Link text that reads alone. Proper punctuation, not straight quotes.</p>
|
||||
<p class="spec__rule">Specific verbs. Three-part errors. Link text that reads alone. Proper punctuation, not
|
||||
straight quotes.</p>
|
||||
<div class="spec__body">
|
||||
<span class="serif-italic" style="font-size:var(--text-md); color:var(--color-ink-2);">
|
||||
“Save changes” beats “OK”. “View pricing plans” beats “Click here”.
|
||||
“Save changes” beats “OK”. “View pricing plans” beats “Click
|
||||
here”.
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
@@ -239,7 +275,8 @@
|
||||
<span class="spec__name">Anti-patterns</span>
|
||||
<span class="spec__num">F / 08</span>
|
||||
</header>
|
||||
<p class="spec__rule">The named tells. Flagged by <code>hallmark audit</code>, erased by <code>hallmark refine</code>.</p>
|
||||
<p class="spec__rule">The named tells. Flagged by <code>hallmark audit</code>, erased by
|
||||
<code>hallmark refine</code>.</p>
|
||||
<div class="spec__body">
|
||||
<span class="dim">21 tells catalogued</span>
|
||||
</div>
|
||||
@@ -261,18 +298,24 @@
|
||||
|
||||
<div class="rail" role="list" aria-label="Named anti-patterns">
|
||||
<article class="tell" role="listitem">
|
||||
<div class="tell__viz"><div class="viz-purple-hero"></div></div>
|
||||
<div class="tell__viz">
|
||||
<div class="viz-purple-hero"></div>
|
||||
</div>
|
||||
<div class="tell__meta">
|
||||
<span class="tell__name">Purple-gradient hero</span>
|
||||
<span class="tell__why">Diagonal gradient behind centred white display type. The most-recognised AI aesthetic.</span>
|
||||
<span class="tell__why">Diagonal gradient behind centred white display type. The most-recognised AI
|
||||
aesthetic.</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="tell" role="listitem">
|
||||
<div class="tell__viz"><div class="viz-gradient-text"></div></div>
|
||||
<div class="tell__viz">
|
||||
<div class="viz-gradient-text"></div>
|
||||
</div>
|
||||
<div class="tell__meta">
|
||||
<span class="tell__name">Gradient headline</span>
|
||||
<span class="tell__why">Headline with <code>background-clip: text</code> filled by a two-stop gradient. Decorative; carries no meaning.</span>
|
||||
<span class="tell__why">Headline with <code>background-clip: text</code> filled by a two-stop gradient.
|
||||
Decorative; carries no meaning.</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -286,7 +329,8 @@
|
||||
</div>
|
||||
<div class="tell__meta">
|
||||
<span class="tell__name">Icon-tile feature grid</span>
|
||||
<span class="tell__why">Three identical columns, icon-above-heading-above-two-lines. The universal LLM feature section.</span>
|
||||
<span class="tell__why">Three identical columns, icon-above-heading-above-two-lines. The universal LLM
|
||||
feature section.</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -300,7 +344,8 @@
|
||||
</div>
|
||||
<div class="tell__meta">
|
||||
<span class="tell__name">Centred-everything hero</span>
|
||||
<span class="tell__why">Hero, sub-head, CTA — all centred, all gradient-pilled. A template masquerading as a page.</span>
|
||||
<span class="tell__why">Hero, sub-head, CTA — all centred, all gradient-pilled. A template masquerading as a
|
||||
page.</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@@ -326,7 +371,8 @@
|
||||
<p class="step__sub">One command. Works in Claude Code, Cursor, and Codex out of the box.</p>
|
||||
</div>
|
||||
<div class="step__body">
|
||||
<pre class="code" data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
<pre class="code"
|
||||
data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -334,7 +380,8 @@
|
||||
<span class="step__num">II</span>
|
||||
<div>
|
||||
<h3 class="step__head">Ask for a UI</h3>
|
||||
<p class="step__sub">In plain language. Hallmark refuses to design without a confirmed audience, use case, and tone — so it asks first.</p>
|
||||
<p class="step__sub">In plain language. Hallmark refuses to design without a confirmed audience, use case,
|
||||
and tone — so it asks first.</p>
|
||||
</div>
|
||||
<div class="step__body">
|
||||
<pre class="code code--block">> build a landing page for a local bookshop's
|
||||
@@ -347,7 +394,10 @@
|
||||
<span class="step__num">III</span>
|
||||
<div>
|
||||
<h3 class="step__head">Five verbs: build, audit, refine, redesign, study</h3>
|
||||
<p class="step__sub">The default verb builds. <code>hallmark audit</code> scores existing code against the tells. <code>hallmark refine</code> polishes in place. <code>hallmark redesign</code> rethinks the structure. <code>hallmark study</code> reads a screenshot you admire and extracts its <em>DNA</em> — macrostructure, archetypes, type-pairing role, colour anchor — never the pixels.</p>
|
||||
<p class="step__sub">The default verb builds. <code>hallmark audit</code> scores existing code against the
|
||||
tells. <code>hallmark refine</code> polishes in place. <code>hallmark redesign</code> rethinks the
|
||||
structure. <code>hallmark study</code> reads a screenshot you admire and extracts its <em>DNA</em> —
|
||||
macrostructure, archetypes, type-pairing role, colour anchor — never the pixels.</p>
|
||||
</div>
|
||||
<div class="step__body">
|
||||
<pre class="code code--block">$ claude "hallmark study ./reference.png"
|
||||
@@ -376,7 +426,8 @@
|
||||
|
||||
<div class="install">
|
||||
<p class="install__head">npx · any harness</p>
|
||||
<pre class="install__body" data-copy-source><span data-copy-text>npx skills add hallmark</span><button class="code__copy code__copy--block" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
<pre class="install__body"
|
||||
data-copy-source><span data-copy-text>npx skills add hallmark</span><button class="code__copy code__copy--block" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
</div>
|
||||
|
||||
<div class="install">
|
||||
@@ -414,96 +465,121 @@
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<p class="examples__intro">Eight 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. Hover any thumbnail to see the page open in a new tab.</p>
|
||||
<p class="examples__intro">Eight 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. Hover any thumbnail to see the page
|
||||
open in a new tab.</p>
|
||||
|
||||
<div class="gallery" role="list">
|
||||
|
||||
<a class="gallery__item" href="_tests/01-tide-podcast/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/01-tide.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
<img src="_tests/_thumbs/01-tide.png" alt="" loading="lazy" decoding="async" width="1024" height="400" />
|
||||
</picture>
|
||||
<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>
|
||||
<div class="gallery__body">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/02-streampipe-cli/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/02-streampipe.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
<img src="_tests/_thumbs/02-streampipe.png" alt="" loading="lazy" decoding="async" width="1024"
|
||||
height="640" />
|
||||
</picture>
|
||||
<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>
|
||||
<div class="gallery__body">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/03-maple-bakery/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/03-maple.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<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>
|
||||
<div class="gallery__body">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/04-meridian-manifesto/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/04-meridian.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
<img src="_tests/_thumbs/04-meridian.png" alt="" loading="lazy" decoding="async" width="1024"
|
||||
height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">04</span>
|
||||
<span class="gallery__name">Meridian</span>
|
||||
<span class="gallery__shape">Manifesto · Manifesto · principles + practice + reading</span>
|
||||
<span class="gallery__desc">Studio manifesto. Pure black, oversized white display, eleven sections.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
<div class="gallery__body">
|
||||
<span class="gallery__num">04</span>
|
||||
<span class="gallery__name">Meridian</span>
|
||||
<span class="gallery__shape">Manifesto · Manifesto · principles + practice + reading</span>
|
||||
<span class="gallery__desc">Studio manifesto. Pure black, oversized white display, eleven sections.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/05-tracejam-saas/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/05-tracejam.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
<img src="_tests/_thumbs/05-tracejam.png" alt="" loading="lazy" decoding="async" width="1024"
|
||||
height="640" />
|
||||
</picture>
|
||||
<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>
|
||||
<div class="gallery__body">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/06-anya-portfolio/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/06-anya.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<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>
|
||||
<div class="gallery__body">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/07-foundry-compliance/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/07-foundry.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">07</span>
|
||||
<span class="gallery__name">Foundry</span>
|
||||
<span class="gallery__shape">Stat-Led · Plain (#fff) · animated counter + pricing pulse</span>
|
||||
<span class="gallery__desc">SOC2 compliance SaaS. Pure-white paper, ink-blue accent, display-heavy Inter Tight — counterweight to the warm-paper themes.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
<div class="gallery__body">
|
||||
<span class="gallery__num">07</span>
|
||||
<span class="gallery__name">Foundry</span>
|
||||
<span class="gallery__shape">Stat-Led · Plain (#fff) · animated counter + pricing pulse</span>
|
||||
<span class="gallery__desc">SOC2 compliance SaaS. Pure-white paper, ink-blue accent, display-heavy Inter
|
||||
Tight — counterweight to the warm-paper themes.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/08-cohort-courses/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/08-cohort.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">08</span>
|
||||
<span class="gallery__name">Cohort</span>
|
||||
<span class="gallery__shape">Marquee Hero · Salon · continuous-scroll + stagger reveal</span>
|
||||
<span class="gallery__desc">Cohort-based courses platform. Marquee of live courses, alternating features, salon tone.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
<div class="gallery__body">
|
||||
<span class="gallery__num">08</span>
|
||||
<span class="gallery__name">Cohort</span>
|
||||
<span class="gallery__shape">Marquee Hero · Salon · continuous-scroll + stagger reveal</span>
|
||||
<span class="gallery__desc">Cohort-based courses platform. Marquee of live courses, alternating features,
|
||||
salon tone.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -512,7 +588,8 @@
|
||||
</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>
|
||||
<footer class="slot slot--foot reveal" data-slot="footer" style="--i:7" role="contentinfo" aria-label="Colophon">
|
||||
</footer>
|
||||
|
||||
</main>
|
||||
|
||||
@@ -525,7 +602,8 @@
|
||||
<p class="hero__lede">{{lede}}</p>
|
||||
<div class="hero__cta">
|
||||
<p class="label">{{ctaLabel}}</p>
|
||||
<pre class="code" data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
<pre class="code"
|
||||
data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -603,7 +681,8 @@
|
||||
<h1 class="hero__display hero__display--small">{{title}}</h1>
|
||||
<p class="hero__lede">{{lede}}</p>
|
||||
<div class="hero__cta">
|
||||
<pre class="code" data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
<pre class="code"
|
||||
data-copy-source><span class="prompt">$</span><span data-copy-text>npx skills add hallmark</span><button class="code__copy" type="button" data-copy-btn aria-label="Copy install command"><span class="code__copy-default">Copy</span><span class="code__copy-done">Copied</span></button></pre>
|
||||
</div>
|
||||
</div>
|
||||
<figure class="hero__mock" aria-hidden="true">
|
||||
@@ -721,4 +800,5 @@
|
||||
|
||||
<script type="module" src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
+139
-5
@@ -34,6 +34,59 @@ If the user types anything that does not clearly map to `audit`, `refine`, `rede
|
||||
|
||||
## Design flow (default)
|
||||
|
||||
### 0. Pre-flight scan
|
||||
|
||||
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:**
|
||||
|
||||
1. **Font stack** — `package.json` for `next/font`, `@fontsource/*`, `expo-google-fonts`, `geist`; any `<link rel="stylesheet" href="...fonts.googleapis.com/...">` 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.
|
||||
4. **Spacing scale** — Tailwind `theme.extend.spacing`; CSS `--space-*` custom-property pattern; presence of a 4-pt or 8-pt scale.
|
||||
5. **Framework** — Next.js (`next` in deps), Astro (`astro`), Vue (`vue`), Svelte / SvelteKit (`svelte` / `@sveltejs/kit`), Remix (`@remix-run/*`), or vanilla HTML.
|
||||
|
||||
**Output format** — emit this block once, before Step 1, with file:line citations so the user can verify what you found:
|
||||
|
||||
```
|
||||
Pre-flight findings:
|
||||
· Font stack: Geist + Geist Mono (next/font, package.json L23)
|
||||
· Palette: OKLCH custom properties (app/globals.css :root)
|
||||
· Motion: framer-motion 11 installed (package.json L41)
|
||||
· Spacing: Tailwind extend.spacing (4-pt scale, tailwind.config.ts L18)
|
||||
· Framework: Next.js 15 (app router)
|
||||
|
||||
Hallmark will preserve: font stack, palette, spacing scale.
|
||||
Hallmark will introduce: macrostructure, microinteraction discipline,
|
||||
slop-test gates, hero enrichment recipe.
|
||||
|
||||
If you want Hallmark to override any preserved item, say so.
|
||||
```
|
||||
|
||||
**Persistence.** Write the findings to `.hallmark/preflight.json` once. On subsequent runs, *re-use* the cached findings unless either:
|
||||
- the user says "refresh pre-flight" (or "scan again", "re-scan"), or
|
||||
- `package.json` / `tailwind.config.*` mtimes are newer than `preflight.json`.
|
||||
|
||||
If the cache is re-used, emit a one-line note instead of the full block: *"Pre-flight cached (last scan: 2026-04-30). Say 'refresh pre-flight' to re-scan."*
|
||||
|
||||
**Edge cases:**
|
||||
|
||||
- **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.
|
||||
- **The user said "ignore the existing project"** → skip pre-flight entirely; emit *"Pre-flight skipped at user request."* and proceed to Step 1.
|
||||
|
||||
**Two more sample outputs** for the model to imitate:
|
||||
|
||||
*Vanilla HTML project, motion-cut:*
|
||||
> *Pre-flight findings: vanilla HTML, no framework detected. No motion library, no Tailwind, no design tokens. Hallmark will introduce: full token system, macrostructure, microinteraction discipline, slop-test gates. Nothing to preserve.*
|
||||
|
||||
*Astro + Tailwind + DTCG tokens already present:*
|
||||
> *Pre-flight findings: Astro 5 (astro.config.mjs L1) · Tailwind v4 with @theme inline tokens (src/styles/global.css L3) · `tokens.json` at project root (DTCG format, 12 colour tokens, 6 font tokens). No motion library detected.*
|
||||
> *Hallmark will preserve: Tailwind tokens, the `tokens.json` file (won't overwrite). Hallmark will introduce: macrostructure, microinteraction discipline, slop-test gates. Motion stance: motion-cut (no framer-motion / motion / gsap detected).*
|
||||
|
||||
The pre-flight block is the user's accountability line: *"here's what I noticed about your project before I touched anything."* Skipping it is the fastest way to lose the user's trust.
|
||||
|
||||
### 1. Design-context gate
|
||||
|
||||
Hallmark works best when you know three things before writing code:
|
||||
@@ -98,10 +151,26 @@ Use the **last 3–5 entries** to inform diversification:
|
||||
- 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 file doesn't exist, this is the first Hallmark run for this project — no constraint, but **you'll create the file in Step 6**.
|
||||
|
||||
If the project has a CSS stamp but no `log.json`, infer one entry from the stamp and proceed.
|
||||
|
||||
**State the rotation in plain text before picking.** This is the user's accountability line for diversification — picking on the page (not in your head) is what keeps the skill from drifting back into Bento-Grid-by-default. The format:
|
||||
|
||||
> *"Last 5 builds: Bento Grid (Tracejam) · Bento Grid (Foundry) · Long Document (Maple) · Manifesto (Meridian) · Quote-Led (Tide). Bento Grid used 2 of 5 — picking from {Marquee Hero, Stat-Led, Workbench, Letter} this time. I'll go with Marquee Hero."*
|
||||
|
||||
Then the theme rotation, on the next line:
|
||||
|
||||
> *"Last 3 themes: Pastel · Plain · Salon. Picking from {Newsprint, Atelier, Linen, Studio} — Newsprint differs on display style and accent hue."*
|
||||
|
||||
**Three sample shapes** to imitate:
|
||||
|
||||
- **First-time** (no `log.json`, fresh project): no rotation block at all — just the macrostructure pick. *"This is the first Hallmark run for this project. Picking Long Document — fits the Coffeebox brief's editorial tone."*
|
||||
- **Mature project** (5+ entries in `log.json`): the format above — frequency count, exclusion list, pick.
|
||||
- **User overrode last run** ("use Bento Grid again, I want the same shape"): *"Last build was Bento Grid (you requested it). You've asked for it again — I'll pick different knob values. Knob deltas: tiles=8 (was 6), accent=full-bleed (was corner-only), spans=irregular (was even). Same archetype, different fingerprint."*
|
||||
|
||||
The rotation block keeps the user inside the discipline without making them read the rules. Skip it and the user starts thinking the diversification is theatre.
|
||||
|
||||
### 3. Load the visual ruleset
|
||||
|
||||
The non-negotiables live in [`references/`](references/). Read only what you need:
|
||||
@@ -131,13 +200,78 @@ For most design work you need `macrostructures`, `component-cookbook`, `typograp
|
||||
|
||||
Most pages don't need it. The strongest hero is often a typographic one. **Reach for [`hero-enrichment.md`](references/hero-enrichment.md) only when the brief points there** — a SaaS / dev-tool brief wants a demo video or mockup; a bakery / café / atelier brief wants a hand-built illustration; a manifesto wants nothing.
|
||||
|
||||
Eyeball the brief or ask one short question. State the decision in one sentence (e.g., *"Enrichment: E1 Clipped-Edge Demo Video, Tier-A CSS-art mockup."* or *"Enrichment: none — typography only."*). The decision goes into the macrostructure stamp at Step 5.
|
||||
Eyeball the brief or ask one short question. State the decision in one sentence (e.g., *"Enrichment: E1 Clipped-Edge Demo Video, Tier-A CSS-art mockup."* or *"Enrichment: none — typography only."*). The decision goes into the macrostructure stamp at Step 6.
|
||||
|
||||
**The enrichment hierarchy is non-negotiable.** Reach for the highest tier you can ship: typography only → Tier A pure CSS art → Tier B hand-built SVG → Tier C generated still (Nanobanana / Recraft) → Tier D library + customisation → **Tier E Lottie is last resort**, only for complex character motion that hand-build can't reach. Reaching for Lottie when CSS would have built it is the new tell.
|
||||
|
||||
When an enrichment archetype requires construction, also load [`custom-craft.md`](references/custom-craft.md). When it requires an external asset, load [`assets.md`](references/assets.md).
|
||||
|
||||
### 5. Build
|
||||
### 5. Preview
|
||||
|
||||
Before emitting any code, output a tight summary of what you're about to ship. This is the user's TL;DR — they should be able to scan it in five seconds and tell you to redirect *before* you write 500 lines of CSS that don't match their intent.
|
||||
|
||||
**Format** (Markdown bullets, not ASCII boxes — they render reliably across every chat client and terminal):
|
||||
|
||||
```markdown
|
||||
**Hallmark · v0.6.0**
|
||||
|
||||
- **Macrostructure** · Stat-Led
|
||||
- **Theme** · Plain (#fff paper · cool greys · ink-blue accent)
|
||||
- **Enrichment** · none (typography only)
|
||||
- **Sections** · Hero · Logos · Stats · Features · Testimonials · Pricing · FAQ · CTA · Footer
|
||||
- **Motion** · counter · pricing-lift · pulse-once
|
||||
- **Slop test** · 38 / 38 ✓ (run after Build)
|
||||
- **Diversification** · differs from Pastel on display style + accent hue
|
||||
```
|
||||
|
||||
**Six required bullets, one optional:**
|
||||
|
||||
1. **Macrostructure** — the named pick from [`macrostructures.md`](references/macrostructures.md).
|
||||
2. **Theme** — name + one-line palette summary (paper colour band · accent hue · display style).
|
||||
3. **Enrichment** — the chosen archetype + tier, or *none (typography only)*.
|
||||
4. **Sections** — section names separated by ` · `, in DOM order.
|
||||
5. **Motion** — microinteraction primitives separated by ` · `, or *none — typography only*. Always under three primitives per the [`microinteractions.md`](references/microinteractions.md) hard rules.
|
||||
6. **Slop test** — `38 / 38 ✓` if all gates pass, or `N / 38 — fails: <gate numbers>` if any are open. Run the slop test BEFORE writing this row; the slop test is Step 7.
|
||||
7. **Diversification** *(optional, only when `.hallmark/log.json` has prior entries)* — what axes differ vs the previous run.
|
||||
|
||||
**Three more sample preview blocks** for the model to imitate, varied across macrostructure types:
|
||||
|
||||
*Long Document (editorial, motion-cut):*
|
||||
> **Hallmark · v0.6.0**
|
||||
>
|
||||
> - **Macrostructure** · Long Document
|
||||
> - **Theme** · Linen (cool slate paper · steel-blue accent · geometric sans)
|
||||
> - **Enrichment** · Tier-B hand-built SVG (a 60-line coffee bean with `@property --rise` 6 s breathing-loop)
|
||||
> - **Sections** · Masthead · Letter · Three Notes · Visit · Colophon
|
||||
> - **Motion** · breathing-loop on bean only (respects `prefers-reduced-motion`)
|
||||
> - **Slop test** · 38 / 38 ✓
|
||||
> - **Diversification** · first run for this project
|
||||
|
||||
*Bento Grid (SaaS, motion-on):*
|
||||
> **Hallmark · v0.6.0**
|
||||
>
|
||||
> - **Macrostructure** · Bento Grid
|
||||
> - **Theme** · Pastel (light cool paper · indigo accent · geometric Geist)
|
||||
> - **Enrichment** · E1 Clipped-Edge Demo Video, Tier-A CSS-art trace waterfall
|
||||
> - **Sections** · Hero · 6-tile Bento (stat · sparkline · quote · code · integrations · spotlight) · Index Footer
|
||||
> - **Motion** · counter · pricing-lift · CSS marquee on integrations strip
|
||||
> - **Slop test** · 38 / 38 ✓
|
||||
> - **Diversification** · differs from Plain on paper hue (light-cool vs pure-white) + accent (indigo vs ink-blue)
|
||||
|
||||
*Manifesto (declarative, no enrichment):*
|
||||
> **Hallmark · v0.6.0**
|
||||
>
|
||||
> - **Macrostructure** · Manifesto
|
||||
> - **Theme** · Manifesto (dark · Inter Tight 900 · single red bleed)
|
||||
> - **Enrichment** · none (typography only — voice carries the brand)
|
||||
> - **Sections** · Masthead · Title · Five Declarations · Bleed Band · What We Refuse · Working Rules · Practice · Reading · Colophon
|
||||
> - **Motion** · none — typography only
|
||||
> - **Slop test** · 38 / 38 ✓
|
||||
> - **Diversification** · differs from Linen on paper band (dark vs light) + display style (display-heavy vs geometric-sans)
|
||||
|
||||
If any slop-test gate fails when you reach Step 7, return to the relevant Build step, fix it, and **re-emit the preview block** with the corrected slop-test row. The preview is the durable summary; it's wrong to ship if it lies.
|
||||
|
||||
### 6. Build
|
||||
|
||||
Emit code that satisfies the tone and structural fingerprint. Match the complexity of the code to the ambition of the tone — a brutalist page needs raw, heavy CSS; an austere page needs restraint.
|
||||
|
||||
@@ -156,9 +290,9 @@ Always:
|
||||
- **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
|
||||
### 7. The slop test
|
||||
|
||||
Before handing back, run the output through these thirty-eight questions. Every answer must be **no**.
|
||||
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.
|
||||
|
||||
**Visual:**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user