Skip to content
This repository was archived by the owner on Sep 14, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .agents/skills/structured-liquidity/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Follow these literally — they are what keep generated output on-brand:
4. **Exactly one accent.** `--accent` carries all emphasis. Never add a competing hue.
5. **Liquid is movement, not a gradient treatment.** Default information surfaces are flat. Express liquidity through moving selection markers, menu and popover settling, tab changes, scroll continuity, and transitions between areas. Use neutral translucency or `backdrop-filter` only at moving state boundaries such as navigation, menus, and overlays—not on every card.
6. **Destructive is gray, not red.** Destructive _containers_ use `--neg`. Error _text_ may warn in color.
7. **Fixed type roles.** display (`--display`, Inter) = headings/buttons/brand; body (`--body`, Inter) = reading copy; mono (`--mono`, Space Mono) = labels/data/captions/eyebrows.
7. **Fixed visual vocabulary.** Use no more than two actual typefaces across three roles: display (`--display`, Inter) for headings/buttons/brand; body (`--body`, Inter) for reading copy; mono (`--mono`, Space Mono) for labels/data/captions/eyebrows. Use the seven semantic size tiers and four weight tiers; do not add local font sizes or weights. Consume semantic color tokens instead of raw component colors.
8. **Leading icons.** Nav links and buttons take a leading Lucide icon, then the label.
9. **Motion proves mass.** On press/hover, nudge the element ~1px toward its shadow and grow the offset.
10. **Communicate with scientific modern clarity.** Use a rational grid, exact alignment, asymmetric balance, measured line lengths, purposeful whitespace, and compact mono captions for sources, state, and evidence. Swiss modernism and the International Typographic Style are the reference tradition.
Expand Down Expand Up @@ -55,8 +55,17 @@ Apply these as CSS custom properties on `:root` (light-mode defaults):
--display: "Inter", "Helvetica Neue", Arial, system-ui, sans-serif;
--body: "Inter", "Helvetica Neue", Arial, system-ui, sans-serif;
--mono: "Space Mono", "SFMono-Regular", ui-monospace, monospace;
--text-detail: 0.72rem;
--text-body: 0.95rem;
--text-lead: clamp(1rem, 1.3vw, 1.18rem);
--text-section: 1.25rem;
--text-title: 1.75rem;
--text-display-xl: clamp(4.2rem, 10.5vw, 9.5rem);
--text-display-lg: clamp(2.6rem, 5.5vw, 5.25rem);
--weight-regular: 500;
--weight-medium: 600;
--weight-bold: 700;
--weight-strong: 800;
--measure-reading: 62ch;
--measure-caption: 48ch;
--grid-gutter: clamp(1rem, 2.5vw, 2.5rem);
Expand All @@ -70,7 +79,7 @@ The canonical machine-readable set is `design-tokens.json` (W3C Design Tokens fo

## Type layout

Three roles — header / body / detail (display / body / mono). Inter carries both sans-serif roles; use scale, weight, measure, and alignment for hierarchy. Use Space Mono only for labels, data, code, captions, and eyebrows. Prefer one dominant statement, a 12-column or similarly disciplined grid, short reading measures, and figure captions that identify evidence and state.
Three roles — header / body / detail (display / body / mono) — resolve to at most two actual typefaces. Inter carries both sans-serif roles; use scale, weight, measure, and alignment for hierarchy. Use Space Mono only for labels, data, code, captions, and eyebrows. The seven allowed size tiers are detail, body, lead, section, title, display large, and display extra-large. Prefer one dominant statement, a 12-column or similarly disciplined grid, short reading measures, and figure captions that identify evidence and state. Existing raw declarations may be captured as migration debt, but a visual-vocabulary ratchet must prevent new raw type, size, weight, or color declarations.

## Motion contract

Expand Down
2 changes: 2 additions & 0 deletions artifacts/structured-liquidity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"typecheck": "tsc -p tsconfig.json --noEmit",
"registry": "node scripts/build-registry.mjs",
"registry:check": "node scripts/check-registry.mjs",
"visual-vocabulary:check": "node scripts/check-visual-vocabulary.mjs",
"visual-vocabulary:baseline": "node scripts/check-visual-vocabulary.mjs --write-baseline",
"adopters:check": "node scripts/check-adopters.mjs"
},
"devDependencies": {
Expand Down
86 changes: 64 additions & 22 deletions artifacts/structured-liquidity/public/design-tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,39 +109,52 @@
"fontFamily": {
"$type": "fontFamily",
"display": {
"$value": [
"Inter",
"Helvetica Neue",
"Arial",
"system-ui",
"sans-serif"
],
"$value": ["Inter", "Helvetica Neue", "Arial", "system-ui", "sans-serif"],
"$description": "Headings, buttons, and brand. Use weight, scale, and alignment to create hierarchy.",
"$extensions": { "cssVar": "--display" }
},
"body": {
"$value": [
"Inter",
"Helvetica Neue",
"Arial",
"system-ui",
"sans-serif"
],
"$value": ["Inter", "Helvetica Neue", "Arial", "system-ui", "sans-serif"],
"$description": "Paragraph and reading copy. Neutral and legible.",
"$extensions": { "cssVar": "--body" }
},
"mono": {
"$value": [
"Space Mono",
"SFMono-Regular",
"ui-monospace",
"monospace"
],
"$value": ["Space Mono", "SFMono-Regular", "ui-monospace", "monospace"],
"$description": "Labels, captions, data, eyebrows. Never long paragraphs.",
"$extensions": { "cssVar": "--mono" }
}
},
"scientificClarity": {
"detail": {
"$type": "dimension",
"$value": "0.72rem",
"$description": "Compact evidence labels, captions, eyebrows, and metadata.",
"$extensions": { "cssVar": "--text-detail" }
},
"body": {
"$type": "dimension",
"$value": "0.95rem",
"$description": "Default interface and reading copy.",
"$extensions": { "cssVar": "--text-body" }
},
"lead": {
"$type": "dimension",
"$value": "clamp(1rem, 1.3vw, 1.18rem)",
"$description": "Introductory copy that leads a section without becoming a heading.",
"$extensions": { "cssVar": "--text-lead" }
},
"section": {
"$type": "dimension",
"$value": "1.25rem",
"$description": "Compact section headings inside applications and surfaces.",
"$extensions": { "cssVar": "--text-section" }
},
"title": {
"$type": "dimension",
"$value": "1.75rem",
"$description": "Surface and panel titles below page-display scale.",
"$extensions": { "cssVar": "--text-title" }
},
"displayXL": {
"$type": "dimension",
"$value": "clamp(4.2rem, 10.5vw, 9.5rem)",
Expand Down Expand Up @@ -179,6 +192,29 @@
"$extensions": { "cssVar": "--grid-gutter" }
}
},
"fontWeight": {
"$type": "fontWeight",
"regular": {
"$value": 500,
"$description": "Default text weight.",
"$extensions": { "cssVar": "--weight-regular" }
},
"medium": {
"$value": 600,
"$description": "Quiet emphasis and titles.",
"$extensions": { "cssVar": "--weight-medium" }
},
"bold": {
"$value": 700,
"$description": "Labels and actionable emphasis.",
"$extensions": { "cssVar": "--weight-bold" }
},
"strong": {
"$value": 800,
"$description": "Dominant headings and high-contrast hierarchy.",
"$extensions": { "cssVar": "--weight-strong" }
}
},
"motion": {
"fast": {
"$type": "duration",
Expand All @@ -204,8 +240,14 @@
"$description": "Color-responsive dark-mode overrides applied when <html data-mode=\"dark\">. The page field is derived from the active accent; there is no separate black mode.",
"color": {
"$type": "color",
"bg": { "$value": "color-mix(in srgb, var(--accent) 18%, #100b16)", "$extensions": { "cssVar": "--bg" } },
"bg2": { "$value": "color-mix(in srgb, var(--accent) 27%, #15101c)", "$extensions": { "cssVar": "--bg-2" } },
"bg": {
"$value": "color-mix(in srgb, var(--accent) 18%, #100b16)",
"$extensions": { "cssVar": "--bg" }
},
"bg2": {
"$value": "color-mix(in srgb, var(--accent) 27%, #15101c)",
"$extensions": { "cssVar": "--bg-2" }
},
"ink": { "$value": "#f6f2f8", "$extensions": { "cssVar": "--ink" } },
"inkDim": {
"$value": "#b9b0bf",
Expand Down
1 change: 1 addition & 0 deletions artifacts/structured-liquidity/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ This installs the theme and `.agents/skills/structured-liquidity/SKILL.md`. Veri
All visuals read from CSS custom properties on `:root`. The machine-readable token set (W3C Design Tokens Community Group format, with the CSS variable name in each `$extensions.cssVar`) is at:

- [design-tokens.json](design-tokens.json): the full token set, covering color, glass, border, radius, flat shadow, font families, plus light-mode overrides.
- Typography is a controlled visual vocabulary: at most two actual typefaces across display/body/detail roles, seven semantic size tiers, and four semantic weights. New raw type or color declarations should fail a checked-in ratchet rather than silently expanding the system.

Core defaults:

Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions artifacts/structured-liquidity/public/structured-liquidity.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,17 @@
--floating-ui-clearance: 96px;

/* ---- Scientific Modern Clarity, informed by Swiss modernism and the International Typographic Style ---- */
--text-detail: 0.72rem;
--text-body: 0.95rem;
--text-lead: clamp(1rem, 1.3vw, 1.18rem);
--text-section: 1.25rem;
--text-title: 1.75rem;
--text-display-xl: clamp(4.2rem, 10.5vw, 9.5rem);
--text-display-lg: clamp(2.6rem, 5.5vw, 5.25rem);
--weight-regular: 500;
--weight-medium: 600;
--weight-bold: 700;
--weight-strong: 800;
--measure-reading: 62ch;
--measure-caption: 48ch;
--grid-gutter: clamp(1rem, 2.5vw, 2.5rem);
Expand Down Expand Up @@ -179,7 +188,7 @@ body {

.eyebrow {
font-family: var(--mono);
font-size: 0.72rem;
font-size: var(--text-detail);
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--ink-dim);
Expand Down Expand Up @@ -212,7 +221,7 @@ h3 {
margin: 0.25em 0 0.3em;
}
.lead {
font-size: clamp(1rem, 1.3vw, 1.18rem);
font-size: var(--text-lead);
color: var(--ink-dim);
max-width: 60ch;
text-wrap: pretty;
Expand Down
9 changes: 9 additions & 0 deletions artifacts/structured-liquidity/scripts/build-registry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,17 @@ const cssVars = {
display: '"Inter","Helvetica Neue",Arial,system-ui,sans-serif',
body: '"Inter","Helvetica Neue",Arial,system-ui,sans-serif',
mono: '"Space Mono","SFMono-Regular",ui-monospace,monospace',
"text-detail": "0.72rem",
"text-body": "0.95rem",
"text-lead": "clamp(1rem, 1.3vw, 1.18rem)",
"text-section": "1.25rem",
"text-title": "1.75rem",
"text-display-xl": "clamp(4.2rem, 10.5vw, 9.5rem)",
"text-display-lg": "clamp(2.6rem, 5.5vw, 5.25rem)",
"weight-regular": "500",
"weight-medium": "600",
"weight-bold": "700",
"weight-strong": "800",
"measure-reading": "62ch",
"measure-caption": "48ch",
"grid-gutter": "clamp(1rem, 2.5vw, 2.5rem)",
Expand Down
171 changes: 171 additions & 0 deletions artifacts/structured-liquidity/scripts/check-visual-vocabulary.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import assert from "node:assert/strict";
import { readFile, readdir, writeFile } from "node:fs/promises";
import { extname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const packageRoot = resolve(fileURLToPath(new URL("../", import.meta.url)));
const configPath = resolve(packageRoot, "visual-vocabulary.json");
const config = JSON.parse(await readFile(configPath, "utf8"));
const baselinePath = resolve(packageRoot, config.baseline);

const normalize = (value) =>
value
.replace(/\s+/g, " ")
.trim()
.replace(/\s*!important$/, "");
const escapePattern = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

async function walk(path) {
const entries = await readdir(path, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const child = resolve(path, entry.name);
if (entry.isDirectory()) files.push(...(await walk(child)));
else if (config.extensions.includes(extname(entry.name))) files.push(child);
}
return files;
}

const tokenSource = await readFile(
resolve(packageRoot, config.tokenSource),
"utf8",
);
for (const [token, value] of Object.entries({
...config.typeSizes,
...config.fontWeights,
})) {
const declaration = new RegExp(
`${escapePattern(token)}\\s*:\\s*${escapePattern(value)}\\s*;`,
"i",
);
assert.match(
tokenSource,
declaration,
`Missing visual-vocabulary token ${token}: ${value}`,
);
}
for (const [token, family] of Object.entries(config.typefaceRoles)) {
const declaration = new RegExp(
`${escapePattern(token)}\\s*:\\s*([^;]+);`,
"i",
).exec(tokenSource);
assert(declaration, `Missing visual-vocabulary typeface role ${token}`);
assert.match(
declaration[1],
new RegExp(escapePattern(family), "i"),
`${token} must resolve to ${family}`,
);
}
for (const token of config.colorRoles) {
assert.match(
tokenSource,
new RegExp(`${escapePattern(token)}\\s*:`, "i"),
`Missing semantic color role ${token}`,
);
}
assert(
new Set(
Object.values(config.typefaceRoles).map((family) => family.toLowerCase()),
).size <= config.maxTypefaceFamilies,
`Visual vocabulary exceeds ${config.maxTypefaceFamilies} actual typefaces`,
);

const declarations =
/(^|[;{])\s*(--[\w-]+|font|font-family|font-size|font-weight|color|background(?:-color)?|border(?:-(?:top|right|bottom|left))?-color|outline-color|fill|stroke)\s*:\s*([^;{}]+)/gim;
const literalColor =
/#[0-9a-f]{3,8}\b|(?:rgb|hsl)a?\([^)]*\)|(?:oklch|lab|lch)\([^)]*\)|\b(?:black|white|red|orange|yellow|green|blue|purple|pink|gray|grey)\b/gi;
const allowedFamilyVars = Object.keys(config.typefaceRoles)
.map((token) => `var(${token}`)
.concat(config.additionalTypefaceVariables ?? []);
const findings = {};
const add = (file, kind, value) => {
const key = `${file}::${kind}::${normalize(value)}`;
findings[key] = (findings[key] ?? 0) + 1;
};

for (const root of config.scanRoots) {
for (const file of await walk(resolve(packageRoot, root))) {
const source = (await readFile(file, "utf8")).replace(
/\/\*[\s\S]*?\*\//g,
"",
);
const name = relative(packageRoot, file);
for (const match of source.matchAll(declarations)) {
const property = match[2].toLowerCase();
const value = normalize(match[3]);
if (property.startsWith("--text-") && !(property in config.typeSizes))
add(name, "unapproved-size-token", value);
if (property.startsWith("--weight-") && !(property in config.fontWeights))
add(name, "unapproved-weight-token", value);
if (
property.startsWith("--") &&
/(font|display|body|mono)/.test(property) &&
!(property in config.typefaceRoles)
)
add(name, "additional-typeface-token", value);
if (
property === "font-family" &&
!allowedFamilyVars.some((role) => value.includes(role)) &&
!/^(inherit|unset)$/.test(value)
)
add(name, "font-family", value);
if (
property === "font-size" &&
!/var\(--text-[\w-]+\)/.test(value) &&
!/^(0|inherit|unset)$/.test(value)
)
add(name, "font-size", value);
if (
property === "font-weight" &&
!/var\(--weight-[\w-]+\)/.test(value) &&
!/^(inherit|unset)$/.test(value)
)
add(name, "font-weight", value);
if (
property === "font" &&
(!/var\(--text-[\w-]+\)/.test(value) ||
!allowedFamilyVars.some((role) => value.includes(role)))
)
add(name, "font-shorthand", value);
for (const color of value.match(literalColor) ?? [])
add(name, "literal-color", color.toLowerCase());
}
}
}

const sorted = Object.fromEntries(
Object.entries(findings).sort(([a], [b]) => a.localeCompare(b)),
);
if (process.argv.includes("--write-baseline")) {
await writeFile(
baselinePath,
`${JSON.stringify({ version: 1, findings: sorted }, null, 2)}\n`,
);
console.log(
`Visual-vocabulary baseline recorded with ${Object.values(sorted).reduce((sum, count) => sum + count, 0)} legacy declarations.`,
);
process.exit(0);
}

const baseline = JSON.parse(await readFile(baselinePath, "utf8"));
const allKeys = new Set([
...Object.keys(baseline.findings),
...Object.keys(sorted),
]);
const changes = [...allKeys].filter(
(key) => (baseline.findings[key] ?? 0) !== (sorted[key] ?? 0),
);
if (changes.length) {
console.error(
"Visual-vocabulary baseline changed. New raw declarations are blocked; intentional cleanup must refresh the baseline.",
);
for (const key of changes.slice(0, 30))
console.error(
` ${baseline.findings[key] ?? 0} -> ${sorted[key] ?? 0} ${key}`,
);
process.exit(1);
}

console.log(
`Visual vocabulary passed: ${config.maxTypefaceFamilies} base typefaces, ${Object.keys(config.typeSizes).length} size tiers, ${Object.keys(config.fontWeights).length} weights, ${config.colorRoles.length} color roles; legacy debt did not grow.`,
);
Loading
Loading