This monorepo carries both the spec + knowledge-base side of oxFlow and the full set of visual artefacts (landing page, workshop deck, clickable prototype, design-specs viewer, flows viewer). They already define a cohesive visual language. Do not invent a parallel one. When you build anything visual in this repo — a standalone HTML writeup, a diagram, a rendered research note — lift tokens and components from here.
If you are an LLM reading this: for any .html deliverable you produce, paste the :root block in CSS tokens verbatim and reuse the named components in Named components. Prefer diagrams over prose wherever a diagram aids comprehension — the components below exist so this is easy.
Canonical reference files
At the root of this repo, in order of seniority:
| # | File | Role | Notes |
|---|---|---|---|
| 1 | proposal/index.html | Upstream. Original visual system. | 1,299 lines. Every other file says “mirrors proposal” in its CSS comments. |
| 2 | presentation/index.html | Workshop deck. | 1,108 lines. Richest named-component library — slides use .layer-stack, .pipeline, .analogy-card, .entity-grid, .card-row, .mini-table, .pill. |
| 3 | app/index.html | Legacy design-specs viewer (being retired). | Adds sidebar + longform content conventions. |
| 4 | index.html | Static-hub landing page. | Compact expression of the system. |
| 5 | flows-app/index.html | Flows & IA viewer. | Reuses tokens; adds nothing structural. |
archive/rev1/ holds the superseded first-pass design work — ignore it.
CSS tokens
Paste this :root block verbatim at the top of any new standalone HTML in the repo. Every current file uses exactly this block.
:root {
--bg: #FAFAFA;
--white: #FFFFFF;
--surface: #FFFFFF;
--surface-raised: #F5F5F5;
--surface-hover: #F0F0F0;
--border: rgba(0,0,0,0.06);
--border-strong: rgba(0,0,0,0.12);
--ink: #111111;
--ink-90: rgba(0,0,0,0.90);
--ink-80: rgba(0,0,0,0.80);
--ink-60: rgba(0,0,0,0.55);
--ink-40: rgba(0,0,0,0.38);
--ink-20: rgba(0,0,0,0.20);
--ink-10: rgba(0,0,0,0.08);
--ink-05: rgba(0,0,0,0.04);
--accent: #10B981;
--accent-dark: #059669;
--accent-bg: rgba(16, 185, 129, 0.06);
--accent-border: rgba(16, 185, 129, 0.18);
--char-light: #78716C;
--char-mid: #57534E;
--char-dark: #44403C;
--char-bg: rgba(120, 113, 108, 0.06);
--char-border: rgba(120, 113, 108, 0.15);
--green: #10B981;
--green-bg: rgba(16, 185, 129, 0.08);
--amber: #D97706;
--amber-bg: rgba(217, 119, 6, 0.08);
--red: #DC2626;
--red-bg: rgba(220, 38, 38, 0.08);
--blue: #2563EB;
--blue-bg: rgba(37, 99, 235, 0.08);
--purple: #7C3AED;
--purple-bg: rgba(124, 58, 237, 0.08);
}The ink ladder
--ink-* is the text colour system. Rgba opacity steps, not hex. Always use a token, never a raw #111 or rgba(0,0,0,…). Use:
| Token | Use for |
|---|---|
--ink (solid #111) | Headlines, strong emphasis |
--ink-90 | Rare — special emphasis |
--ink-80 | Body text (default body colour) |
--ink-60 | Secondary body, muted paragraphs, dek |
--ink-40 | Labels, eyebrows, metadata, captions |
--ink-20 | Very muted, separator labels |
--ink-10 | Hairlines, grid background stroke, dividers |
--ink-05 | Subtle surface tint |
Status colours — meaning is fixed
Carry the workshop convention across every document and diagram:
- Green (
--accent/--green— same value): locked / done / confirmed / recommended. - Amber (
--amber): maturing / working / in-progress / room for improvement. - Red (
--red): needs input / unresolved / blocker. - Blue (
--blue): information / reference. - Purple (
--purple): supplementary / alternate path.
Do not use red as a “highlight” colour. Do not use amber decoratively. These colours carry semantic weight — respect it.
Typography
- Body font:
'Inter', -apple-system, BlinkMacSystemFont, sans-serif. Load weights 100, 200, 300, 400, 500, 600, 700. - Mono font:
'JetBrains Mono', ui-monospace, monospace. Load 400, 500. - Body weight:
300. - Body line-height:
1.7for longform,1.6for slides. - Body colour:
var(--ink-80). - Anti-aliasing:
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;. - Tabular numerics on any displayed number:
font-variant-numeric: tabular-nums.
Scale
Display sizes use clamp(). Weight is always 100–200 for display, 300 for body, 500 for labels and strong. Never 400 for display.
| Role | Example class in repo | Size | Weight | Letter-spacing |
|---|---|---|---|---|
| Hero (cover) | .cover h1 | clamp(3.4rem, 8vw, 5.6rem) | 200 | -0.03em |
| Section title | .slide-title, .entity-head h2 | clamp(2.2rem, 4vw, 3rem) | 200 | -0.02em |
| Card big number | .card .v | 1.8rem | 200 | -0.02em |
| Body | default | 1rem | 300 | 0 |
| Dek / lede | .slide-lede, .hero-sub | 1.1–1.3rem | 300 | 0 |
| Label / eyebrow | .eyebrow, .col-title, .card .k | 0.65–0.72rem | 500 | 0.2–0.3em (uppercase) |
| Metadata | .cover .meta, .doc-meta-item | 0.8rem | 300 | 0.04em |
Named components
Every component below is defined in the canonical files. Copy the class name. Don’t rename.
.geo-bg — the grid background
Sits fixed behind everything, pointer-events: none;. This is the single strongest brand cue — every page should have it.
<div class="geo-bg">
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="grid" width="80" height="80" patternUnits="userSpaceOnUse">
<path d="M 80 0 L 0 0 0 80" fill="none" class="grid-line"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)"/>
</svg>
</div>.geo-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.geo-bg svg { width: 100%; height: 100%; opacity: 0.35; }
.geo-bg .grid-line { stroke: var(--ink-10); stroke-width: 0.5; }.eyebrow — uppercase label
Uppercase kicker that precedes section titles or page headers. Optional 6px accent dot.
<div class="eyebrow"><span class="dot"></span>Research</div>.eyebrow {
font-size: 0.68rem;
font-weight: 500;
letter-spacing: 0.3em;
text-transform: uppercase;
color: var(--ink-40);
margin-bottom: 20px;
}
.eyebrow .dot {
display: inline-block; width: 6px; height: 6px; border-radius: 50%;
background: var(--accent); margin-right: 10px; vertical-align: middle;
transform: translateY(-1px);
}.divider-line — 48px hairline
Use between title block and content, or between thought units. Never an <hr>.
<div class="divider-line"></div>.divider-line { width: 48px; height: 1px; background: var(--ink-10); margin: 28px 0; }.pill — status / tag
<span class="pill">Draft</span>
<span class="pill amber">Working</span>
<span class="pill red">Needs input</span>.pill {
font-size: 0.68rem; font-weight: 500; letter-spacing: 0.18em;
text-transform: uppercase; padding: 4px 10px; border-radius: 99px;
background: var(--accent-bg); color: var(--accent-dark); border: 1px solid var(--accent-border);
}
.pill.amber { background: var(--amber-bg); color: var(--amber); border-color: rgba(217,119,6,0.18); }
.pill.red { background: var(--red-bg); color: var(--red); border-color: rgba(220,38,38,0.18); }
.pill.blue { background: var(--blue-bg); color: var(--blue); border-color: rgba(37,99,235,0.18); }
.pill.purple{ background: var(--purple-bg);color: var(--purple);border-color: rgba(124,58,237,0.18); }.card-row — 2/3/4/5-up cards with a 1px rule between them
The 1px gap + background trick produces a clean hairline between cards without doubled borders.
<div class="card-row cols-3">
<div class="card">
<div class="k">Label</div>
<div class="v">12</div>
<div class="d">Explanatory caption.</div>
</div>
<!-- more cards -->
</div>.card-row {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 1px; background: var(--border); border: 1px solid var(--border);
border-radius: 14px; overflow: hidden; margin-top: 28px;
box-shadow: 0 1px 3px rgba(0,0,0,0.02);
}
.card-row.cols-2 { grid-template-columns: repeat(2, 1fr); }
.card-row.cols-4 { grid-template-columns: repeat(4, 1fr); }
.card-row.cols-5 { grid-template-columns: repeat(5, 1fr); }
.card-row .card { background: var(--surface); padding: 28px 24px; }
.card .k { font-size: 0.65rem; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-40); margin-bottom: 12px; }
.card .v { font-size: 1.8rem; font-weight: 200; color: var(--ink); letter-spacing: -0.02em; margin-bottom: 8px; font-variant-numeric: tabular-nums; }
.card .d { font-size: 0.8rem; color: var(--ink-60); font-weight: 300; line-height: 1.5; }.layer-stack — numbered ranked rows
Perfect for shortlists, ranked options, layered concepts. Top row can be highlighted with accent border + background.
<div class="layer-stack">
<div class="layer-row">
<div class="num">1</div>
<div class="name">Neon</div>
<div class="desc">Serverless Postgres with branch-per-PR.</div>
</div>
<!-- more rows -->
</div>.layer-stack { display: flex; flex-direction: column; gap: 10px; margin-top: 28px; }
.layer-row {
display: grid; grid-template-columns: 56px 220px 1fr; gap: 20px;
align-items: center; padding: 18px 24px;
background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
transition: border-color 0.2s, background 0.2s;
}
.layer-row:hover { border-color: var(--accent-border); background: var(--accent-bg); }
.layer-row .num { font-size: 1.8rem; font-weight: 100; color: var(--accent); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; line-height: 1; }
.layer-row .name { font-size: 1.05rem; font-weight: 400; color: var(--ink); }
.layer-row .desc { font-size: 0.88rem; color: var(--ink-60); font-weight: 300; }.pipeline — connected steps, recommended step highlighted
<div class="pipeline">
<div class="step"><div class="n">01</div><div class="v">Extract</div><div class="d">From Workbench</div></div>
<div class="step"><div class="n">02</div><div class="v">Transform</div><div class="d">Into entity model</div></div>
<div class="step accent"><div class="n">03</div><div class="v">Load</div><div class="d">Into Postgres</div></div>
</div>.pipeline { display: flex; align-items: stretch; gap: 0; margin-top: 28px; }
.pipeline .step {
flex: 1; padding: 22px 18px; background: var(--surface);
border: 1px solid var(--border); text-align: center; position: relative;
}
.pipeline .step:first-child { border-radius: 10px 0 0 10px; }
.pipeline .step:last-child { border-radius: 0 10px 10px 0; }
.pipeline .step + .step { border-left: none; }
.pipeline .step .n { font-size: 0.65rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-40); font-weight: 500; margin-bottom: 8px; }
.pipeline .step .v { font-size: 1rem; font-weight: 500; color: var(--ink); }
.pipeline .step .d { font-size: 0.78rem; color: var(--ink-60); font-weight: 300; margin-top: 6px; line-height: 1.5; }
.pipeline .step.accent { background: var(--accent-bg); border-color: var(--accent-border); }
.pipeline .step.accent .v { color: var(--accent-dark); }.mini-table — comparison table
Upper-case uppercase headers, bottom hairline only, no vertical rules. This is the default comparison matrix.
<table class="mini-table">
<thead><tr><th>Candidate</th><th>Pricing</th><th>Branching</th><th>Fit</th></tr></thead>
<tbody>
<tr><td><strong>Neon</strong></td><td>…</td><td>…</td><td><span class="pill">Recommended</span></td></tr>
</tbody>
</table>.mini-table { width: 100%; margin-top: 20px; border-collapse: collapse; font-size: 0.9rem; }
.mini-table th {
text-align: left; font-size: 0.68rem; font-weight: 500;
letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-40);
padding: 10px 14px; border-bottom: 1px solid var(--border-strong);
}
.mini-table td { padding: 12px 14px; border-bottom: 1px solid var(--border); color: var(--ink-80); font-weight: 300; }
.mini-table td strong { color: var(--ink); font-weight: 500; }
.mini-table td code { background: var(--surface-raised); padding: 1px 6px; border-radius: 4px; font-size: 0.82rem; }.bullets — accent-dot list
Override the default <ul> disc with a 6px accent dot. Makes lists feel on-brand.
.bullets { list-style: none; padding: 0; margin-top: 12px; }
.bullets li { font-size: 1.05rem; color: var(--ink-80); padding: 14px 0 14px 28px; border-bottom: 1px solid var(--border); position: relative; font-weight: 300; }
.bullets li:last-child { border-bottom: none; }
.bullets li::before { content: ''; position: absolute; left: 4px; top: 22px; width: 6px; height: 6px; background: var(--accent); border-radius: 50%; }
.bullets li strong { color: var(--ink); font-weight: 500; margin-right: 4px; }
.bullets li .note { font-size: 0.85rem; color: var(--ink-40); display: block; margin-top: 4px; }Animation — entrance only
Fade-up entrance with staggered delays. Never animate on hover beyond colour/border changes — the aesthetic is calm.
@keyframes fadeUp {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-up { opacity: 0; animation: fadeUp 0.6s ease-out forwards; }
.delay-1 { animation-delay: 80ms; }
.delay-2 { animation-delay: 160ms; }
.delay-3 { animation-delay: 240ms; }
.delay-4 { animation-delay: 320ms; }
.delay-5 { animation-delay: 400ms; }
.delay-6 { animation-delay: 480ms; }
.delay-7 { animation-delay: 560ms; }
.delay-8 { animation-delay: 640ms; }Hover on cards: border colour → --accent-border, transform: translateY(-3px), box-shadow: 0 12px 32px rgba(0,0,0,0.05). 200ms ease.
Voice
- Sentence case in body copy. UPPERCASE is reserved for labels (
.eyebrow,.card .k,.col-title, table headers). - Short dek, not a paragraph. The workshop deck’s
.slide-lederule — “max 760px width” — is a voice rule as much as a layout one. - Be direct. “This is the recommended option.” not “We believe this may be the most suitable choice.”
- Avoid decorative words. The visual system earns the calm — don’t undercut it with “sleek / seamless / powerful”.
Quick-start boilerplate
Start every new standalone HTML document from this shell. Drop in the .geo-bg SVG and the page-specific content — nothing else.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>oxFlow — <!-- title --></title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
/* paste :root block from BRANDING.md */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--ink-80);
line-height: 1.7;
font-weight: 300;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
}
/* paste .geo-bg, animations, and named components you need */
.wrap {
position: relative; z-index: 1;
max-width: 1100px; margin: 0 auto;
padding: 80px 32px 120px;
}
</style>
</head>
<body>
<div class="geo-bg"><!-- SVG from BRANDING.md --></div>
<div class="wrap">
<header class="animate-up">
<div class="eyebrow"><span class="dot"></span>Your eyebrow</div>
<h1>Your title</h1>
<p class="lede">Your dek.</p>
<div class="divider-line"></div>
</header>
<!-- content -->
</div>
</body>
</html>Diagrams — prefer them to prose
When a matrix, hierarchy, pipeline, or flow is easier to grok than a paragraph, use a diagram. Options in order of preference:
- The named components above —
.mini-table,.layer-stack,.pipeline,.card-row. These cover 80% of cases and keep the look consistent. - Inline SVG — for custom system shapes and architecture maps. Use
--ink/--accent/ status tokens for strokes and fills. Seeoxflow/docs/foundation/oxflow-concept-map.svgfor reference voice. - Mermaid — github renders Mermaid in markdown natively; include it in standalone HTML via CDN only when you need to. Use for sequence/flow/state diagrams. Override the default theme with oxFlow tokens where possible.
Do not use screenshot-only diagrams — they rot and don’t respect the colour tokens. If you must paste an image, keep the source file next to it.
Do not
- Do not invent new colour values. If you need a shade that isn’t in the token block, pick the nearest existing one or propose an addition in a PR.
- Do not use default browser
<ul>bullets in prominent content. Use.bullets. - Do not use emojis decoratively. The accent dot and pills carry that weight.
- Do not load external UI frameworks (Bootstrap, Tailwind, shadcn) for one-off docs. The token block is the framework.