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:

#FileRoleNotes
1proposal/index.htmlUpstream. Original visual system.1,299 lines. Every other file says “mirrors proposal” in its CSS comments.
2presentation/index.htmlWorkshop deck.1,108 lines. Richest named-component library — slides use .layer-stack, .pipeline, .analogy-card, .entity-grid, .card-row, .mini-table, .pill.
3app/index.htmlLegacy design-specs viewer (being retired).Adds sidebar + longform content conventions.
4index.htmlStatic-hub landing page.Compact expression of the system.
5flows-app/index.htmlFlows & 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:

TokenUse for
--ink (solid #111)Headlines, strong emphasis
--ink-90Rare — special emphasis
--ink-80Body text (default body colour)
--ink-60Secondary body, muted paragraphs, dek
--ink-40Labels, eyebrows, metadata, captions
--ink-20Very muted, separator labels
--ink-10Hairlines, grid background stroke, dividers
--ink-05Subtle 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.7 for longform, 1.6 for 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.

RoleExample class in repoSizeWeightLetter-spacing
Hero (cover).cover h1clamp(3.4rem, 8vw, 5.6rem)200-0.03em
Section title.slide-title, .entity-head h2clamp(2.2rem, 4vw, 3rem)200-0.02em
Card big number.card .v1.8rem200-0.02em
Bodydefault1rem3000
Dek / lede.slide-lede, .hero-sub1.1–1.3rem3000
Label / eyebrow.eyebrow, .col-title, .card .k0.65–0.72rem5000.2–0.3em (uppercase)
Metadata.cover .meta, .doc-meta-item0.8rem3000.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; }
<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-lede rule — “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:

  1. The named components above.mini-table, .layer-stack, .pipeline, .card-row. These cover 80% of cases and keep the look consistent.
  2. Inline SVG — for custom system shapes and architecture maps. Use --ink / --accent / status tokens for strokes and fills. See oxflow/docs/foundation/oxflow-concept-map.svg for reference voice.
  3. 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.