Reference notes. Source retained; historical claims may need rechecking.
/* ==========================================================================
documentation-kit.css
Ready material for didactic pages - teaching pages, one-pagers, explainers
read calmly by partners and teams. Hand-written on house-token variables.
NO framework, NO dependency, NO @import. One file, paste and go.
Source: documentation-design.md (9 - System/ui-ux/), captured 2026-07-19.
Under the same two lenses as the rest of the ui-ux folder:
- Teletubbies: the page speaks to the eye before it is read.
- Kennedy: one job, hierarchy by de-emphasis, grayscale first, colour last.
LIGHT THEME ONLY - Frank's house rule. No dark mode, no
prefers-color-scheme, anywhere in this file. Do not add one.
HOW TO USE
1. <link rel="stylesheet" data-local="documentation-kit.css"> in <head>.
2. Keep §0 ROOT TOKENS and §1 RESET - every block below depends on them.
3. Delete whole numbered blocks in §4 BLOCKS you don't need on this page.
Each block is self-contained; none depend on each other.
4. Swap --accent (and, if the page needs its own identity, the callout
tint backgrounds) to the PROJECT's own palette hex. Never a CSS named
colour. Keep it to one accent - that is the whole point of §0.
5. Grayscale review before shipping: add class "review-grayscale" to
<body> for a moment (see §5). Anything that goes flat and unreadable
was leaning on colour to do a job that size/weight/space should do.
========================================================================== */
/* ==========================================================================
§0 · ROOT TOKENS - the only place colour, type and space are named.
Everything else in this file references these with var(). That is what
makes "one accent colour" or "double the whitespace" a two-line edit.
========================================================================== */
:root {
/* --- colour: one ink, one soft ink, one paper, one line, one accent --- */
--ink: #1a1a1a; /* body text */
--ink-soft: #666666; /* meta, captions, de-emphasised text */
--paper: #fdfdfb; /* page background */
--surface: #ffffff; /* raised block background (segment, code figure) */
--line: #e4e4e0; /* hairline borders, dividers, table rules */
--accent: #b3261e; /* the ONE scarce colour: links, current state, emphasis */
/* the house contract (tokens.css): the kit's own names above stay, and
the contract's names sit beside them so a page that links this file
already holds its tokens. Every painted colour names its partner. */
--bg: var(--paper);
--fg: var(--ink);
--on-surface: var(--ink);
--on-accent: #ffffff;
--muted: var(--ink-soft);
--border: var(--line);
--font-display: 'DM Serif Display', Georgia, serif;
--font-body: 'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* --- semantic tints for callouts only - 3 meanings max, never a rainbow --- */
--info-ink: #2b6cb0; --info-bg: #eef4fb;
--warning-ink: #b7791f; --warning-bg: #fbf3e6;
--success-ink: #2f855a; --success-bg: #eaf7ef;
/* --- three fonts, three jobs - set once, referenced everywhere --- */
--font-body: Georgia, 'Iowan Old Style', 'Palatino Linotype', serif; /* prose */
--font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; /* labels, nav, controls */
--font-mono: 'SF Mono', 'Menlo', 'Consolas', monospace; /* code, IDs, literal values */
/* --- type scale: house modular scale, 1.25 ratio, 5 steps + extension --- */
--scale-0: 0.8rem; /* meta, captions, labels */
--scale-1: 1rem; /* UI base, dense reference base */
--scale-2: 1.25rem; /* lead paragraph, h4 */
--scale-3: 1.563rem; /* h3 / h2 in a short page */
--scale-4: 2.441rem; /* h1 / page title */
--scale-5: 3.052rem; /* extension - hero figures, the one big statistic */
/* --- the two reading regimes named in the digest, as their own tokens --- */
--text-prose: 1.125rem; /* 18px */
--leading-prose: 1.6;
--text-dense: 0.9375rem; /* 15px */
--leading-dense: 1.4;
/* --- other line-heights and small rhythm constants, all tokenised so
nothing in §1-§4 below is a bare number --- */
--leading-ui: 1.5; /* body / UI default */
--leading-heading: 1.25; /* h1-h4 */
--leading-tight: 1; /* one-line figures: statistic value */
--rhythm-inline: 0.9em; /* inline code, slightly under body size */
--rhythm-para: 1.6em; /* paragraph / list bottom margin, prose */
--rhythm-indent: 1.4em; /* list indent, prose */
--label-pad-y: 0.2em;
--label-pad-x: 0.6em;
/* --- space by duplication: .25 / .5 / 1 / 2 / 4rem - group gaps double element gaps --- */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
--space-4: 2rem;
--space-5: 4rem;
/* --- measures --- */
--measure-container: 1120px; /* page width cap */
--measure-prose: 68ch; /* reading column cap, 66-72ch */
/* --- shared geometry --- */
--radius: 4px;
--shadow-raised: 0 2px 6px rgba(0, 0, 0, .08);
}
/* ==========================================================================
§1 · RESET & BASE - minimal, not a full reset. Do not delete: everything
below assumes box-sizing, paper background and the UI font are already set.
========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
body {
background: var(--paper);
color: var(--ink);
font-family: var(--font-ui);
font-size: var(--scale-1);
line-height: var(--leading-ui);
margin: 0;
}
h1, h2, h3, h4 {
font-family: var(--font-body);
font-weight: 700;
color: var(--ink);
line-height: var(--leading-heading);
margin: 0 0 var(--space-3) 0;
}
h1 { font-size: var(--scale-4); }
h2 { font-size: var(--scale-3); }
h3 { font-size: var(--scale-2); }
h4 { font-size: var(--scale-1); font-family: var(--font-ui); font-weight: 700; }
a { color: var(--accent); }
code, kbd, samp {
font-family: var(--font-mono);
font-size: var(--rhythm-inline);
}
/* ==========================================================================
§2 · READING - how prose itself reads well (digest A.1–A.4)
Two body regimes that must never mix in one block: .prose for a teaching
paragraph, .dense for tables/specs/definitions.
========================================================================== */
/* A.1 - reading measure: cap at 66-72ch, never full viewport width */
.prose {
max-width: var(--measure-prose);
margin-inline: auto;
}
/* A.2 - prose regime: comfortable reading size and line height */
.prose {
font-family: var(--font-body);
font-size: var(--text-prose);
line-height: var(--leading-prose);
color: var(--ink);
}
/* A.2 - dense regime: reference material, tighter and smaller */
.dense {
font-family: var(--font-ui);
font-size: var(--text-dense);
line-height: var(--leading-dense);
color: var(--ink);
}
/* A.3 - generous paragraph rhythm: silence between ideas reads as organised */
.prose p { margin: 0 0 var(--rhythm-para) 0; }
.prose h2 { margin-top: var(--space-5); }
.prose h3 { margin-top: var(--space-4); }
.prose ul, .prose ol { margin: 0 0 var(--rhythm-para) 0; padding-left: var(--rhythm-indent); }
.prose li { margin-bottom: var(--space-2); }
.prose li:last-child { margin-bottom: 0; }
/* ==========================================================================
§3 · ORIENTATION - never let a long page lose the reader (digest B.5–B.8)
========================================================================== */
/* B.8 - container: cap the page width. Padding shrinks one step on mobile
so text doesn't sit flush against the edge of a phone screen. */
.container {
max-width: var(--measure-container);
margin: 0 auto;
padding: 0 var(--space-4);
}
@media (max-width: 600px) {
.container { padding: 0 var(--space-3); }
}
/* B.8 - text column: cap the prose column tighter, inside the container */
.text-column {
max-width: var(--measure-prose);
margin: 0 auto;
}
/* B.7 - sticky page header: one slim line, no logo clutter */
.page-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--paper);
border-bottom: 1px solid var(--line);
padding: var(--space-2) 0;
}
.page-header .container {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-3);
}
.page-header .title {
font-family: var(--font-ui);
font-size: var(--scale-1);
font-weight: 700;
color: var(--ink);
}
.page-header .meta {
font-family: var(--font-ui);
font-size: var(--scale-0);
color: var(--ink-soft);
}
/* B.7-bis — a TALL header (title + prose) must never freeze: it eats the screen and
the reader cannot consult what is below (Frank, 15-08-2026). Use .page-intro for it;
.page-header stays sticky only for the slim one-line bar it was written for. */
.page-intro {
position: static;
background: var(--paper);
border-bottom: 1px solid var(--line);
padding: var(--space-4) 0 var(--space-3);
}
/* B.5 - on-this-page rail: sticky beside the content, collapses on narrow
screens. Markup once as <details class="on-this-page" open>, no JS:
<details class="on-this-page" open>
<summary>On this page</summary>
<nav><a href="#id">Heading</a> ...</nav>
</details>
The summary is hidden at desktop width (rail is always open, no toggle
needed); it reappears as a real toggle once the layout goes narrow. */
.on-this-page {
position: sticky;
top: var(--space-5);
align-self: start;
font-family: var(--font-ui);
font-size: var(--scale-0);
border: none;
}
.on-this-page > summary {
display: none;
}
.on-this-page .rail-label {
font-weight: 700;
color: var(--ink-soft);
text-transform: uppercase;
letter-spacing: .06em;
margin-bottom: var(--space-2);
}
.on-this-page nav { display: flex; flex-direction: column; }
.on-this-page a {
display: block;
padding: var(--space-1) 0;
color: var(--ink-soft);
text-decoration: none;
border-left: 2px solid transparent;
padding-left: var(--space-2);
}
.on-this-page a:hover,
.on-this-page a.current {
color: var(--accent);
border-left-color: var(--accent);
}
@media (max-width: 800px) {
.on-this-page {
position: static;
border: 1px solid var(--line);
border-radius: var(--radius);
padding: var(--space-3);
margin-bottom: var(--space-4);
}
.on-this-page > summary {
display: block;
cursor: pointer;
font-weight: 700;
color: var(--ink);
}
.on-this-page nav { margin-top: var(--space-2); }
}
/* B.6 - anchor links on headings, revealed on hover. Markup:
<h2 id="topic">Heading <a class="anchor" href="#topic">#</a></h2> */
h2 .anchor, h3 .anchor {
opacity: 0;
margin-left: var(--space-2);
color: var(--ink-soft);
text-decoration: none;
font-family: var(--font-ui);
font-weight: 400;
}
h2:hover .anchor, h2:focus-within .anchor,
h3:hover .anchor, h3:focus-within .anchor {
opacity: 1;
}
/* ==========================================================================
§4 · BLOCKS - the document's vocabulary (digest C.9–C.18)
Every block below is independent. Delete the ones a given page doesn't use.
========================================================================== */
/* --- C.9 · segment: the topic block, turns a wall of text into topics --- */
.segment {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: var(--space-4) var(--space-4);
margin: var(--space-4) 0;
}
.segment.raised { box-shadow: var(--shadow-raised); border-color: transparent; }
.segment > *:first-child { margin-top: 0; }
.segment > *:last-child { margin-bottom: 0; }
/* --- C.10 · callout: the one place colour earns its keep, 3 meanings max --- */
.callout {
border-left: 4px solid var(--ink-soft);
background: #f7f7f7;
padding: var(--space-3) var(--space-4);
margin: var(--space-4) 0;
border-radius: 0 var(--radius) var(--radius) 0;
}
.callout > *:first-child { margin-top: 0; }
.callout > *:last-child { margin-bottom: 0; }
.callout.info { border-left-color: var(--info-ink); background: var(--info-bg); }
.callout.warning { border-left-color: var(--warning-ink); background: var(--warning-bg); }
.callout.success { border-left-color: var(--success-ink); background: var(--success-bg); }
/* --- C.11 · steps: the procedure row, "first... then... finally" at a glance --- */
.steps {
display: flex;
border: 1px solid var(--line);
border-radius: var(--radius);
overflow: hidden;
margin: var(--space-4) 0;
}
.step {
flex: 1;
padding: var(--space-3) var(--space-4);
border-right: 1px solid var(--line);
}
.step:last-child { border-right: none; }
.step .num {
font-family: var(--font-ui);
font-size: var(--scale-2);
font-weight: 700;
color: var(--ink-soft);
}
.step.active .num { color: var(--accent); }
.step .title {
font-family: var(--font-ui);
font-weight: 700;
margin-top: var(--space-1);
}
.step .desc {
font-family: var(--font-ui);
color: var(--ink-soft);
font-size: var(--scale-0);
margin-top: var(--space-1);
}
@media (max-width: 600px) {
.steps { flex-direction: column; }
.step { border-right: none; border-bottom: 1px solid var(--line); }
.step:last-child { border-bottom: none; }
}
/* --- C.12 · statistic: the big number a page wants remembered --- */
.stat { margin: var(--space-4) 0; }
.stat .value {
font-family: var(--font-body);
font-size: var(--scale-4); /* trimmed from scale-5 so a row of figures fits one line */
font-weight: 700;
line-height: var(--leading-tight);
color: var(--ink);
}
.stat .label {
font-family: var(--font-ui);
font-size: var(--scale-0);
text-transform: uppercase;
letter-spacing: .06em;
color: var(--ink-soft);
margin-top: var(--space-2);
}
/* even grid: all figures share one row when there's room, wrap 2-up on narrow
screens instead of leaving one orphan on a second line (auto-fit + minmax) */
.stat-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--space-4) var(--space-4);
align-items: end;
}
.stat-row .stat { margin: 0; }
/* --- C.13 · description list: term + one-line meaning, structurally paired --- */
dl.desc-list { margin: var(--space-4) 0; }
dl.desc-list dt {
font-family: var(--font-ui);
font-weight: 700;
color: var(--ink);
margin-top: var(--space-3);
}
dl.desc-list dt:first-child { margin-top: 0; }
dl.desc-list dd {
margin: var(--space-1) 0 0 0;
color: var(--ink-soft);
}
/* --- C.14 · divider: named section break without a full heading --- */
.divider {
display: flex;
align-items: center;
color: var(--ink-soft);
margin: var(--space-5) 0;
font-family: var(--font-ui);
}
.divider::before, .divider::after {
content: "";
flex: 1;
border-bottom: 1px solid var(--line);
}
.divider span { padding: 0 var(--space-3); font-size: var(--scale-0); }
/* --- C.15 · label: the status tag, one grey + one accent, nothing more --- */
.label {
display: inline-block;
font-family: var(--font-ui);
padding: var(--label-pad-y) var(--label-pad-x);
border-radius: 3px;
font-size: var(--scale-0);
background: #eeeeee;
color: var(--ink-soft);
}
.label.accent {
background: color-mix(in srgb, var(--accent) 15%, white);
color: var(--accent);
}
/* --- C.16 · labelled code block: a filename/language tag above the block,
horizontal scroll, never wrap (wrapping destroys the code's own structure) --- */
figure.code {
margin: var(--space-4) 0;
}
figure.code figcaption {
font-family: var(--font-mono);
font-size: var(--scale-0);
color: var(--ink-soft);
background: #f2f2f0;
padding: var(--space-1) var(--space-3);
border: 1px solid var(--line);
border-bottom: none;
border-radius: var(--radius) var(--radius) 0 0;
}
figure.code pre {
margin: 0;
overflow-x: auto;
padding: var(--space-3);
background: #fafafa;
border: 1px solid var(--line);
border-radius: 0 0 var(--radius) var(--radius);
font-family: var(--font-mono);
font-size: var(--scale-0);
line-height: var(--leading-dense);
white-space: pre;
}
/* --- C.17 · tables: sticky header, zebra rows, scroll inside their own box --- */
.table-wrap {
overflow-x: auto;
margin: var(--space-4) 0;
border: 1px solid var(--line);
border-radius: var(--radius);
}
.table-wrap table {
border-collapse: collapse;
width: 100%;
font-family: var(--font-ui);
font-size: var(--text-dense);
line-height: var(--leading-dense);
}
.table-wrap th, .table-wrap td {
text-align: left;
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--line);
}
.table-wrap thead th {
position: sticky;
top: 0;
background: var(--paper);
font-weight: 700;
color: var(--ink);
border-bottom: 1px solid var(--line);
}
.table-wrap tbody tr:nth-child(even) { background: #faf9f7; }
.table-wrap tbody tr:last-child td { border-bottom: none; }
/* --- C.18 · before/after & two-column comparison: matched columns, equal weight --- */
.compare {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-4);
margin: var(--space-4) 0;
}
.compare > * { margin: 0; }
.compare .compare-label {
font-family: var(--font-ui);
font-size: var(--scale-0);
font-weight: 700;
text-transform: uppercase;
letter-spacing: .06em;
color: var(--ink-soft);
margin-bottom: var(--space-2);
}
@media (max-width: 700px) {
.compare { grid-template-columns: 1fr; }
}
/* ==========================================================================
§5 · UTILITIES - small helpers that support the two meta-rules (digest D)
========================================================================== */
/* D.20 - grayscale-first review: add to <body> for a moment before shipping.
Anything that loses its hierarchy here was leaning on colour to do work
that size, weight and space should have done. Remove before shipping. */
.review-grayscale { filter: grayscale(1); }
/* Light styling for <details> - the house rule requires collapsible sections
on any long content. Kept minimal so it never fights a page's own blocks. */
details.collapsible {
border: 1px solid var(--line);
border-radius: var(--radius);
padding: var(--space-3) var(--space-4);
margin: var(--space-3) 0;
}
details.collapsible summary {
cursor: pointer;
font-family: var(--font-ui);
font-weight: 700;
color: var(--ink);
}
details.collapsible[open] summary { margin-bottom: var(--space-2); }
/* Top-level page section, collapsible, styled as the section's own h2 -
for pages built entirely of <details open> sections (house rule: every
major section folds). Markup:
<details class="doc-section" id="topic" open>
<summary>Section title</summary>
...content...
</details>
A subtle chevron via CSS border-triangle, not a text character; rotates
90deg open. Native <details> marker is removed on both engines. */
details.doc-section {
border: none;
border-top: 1px solid var(--line);
padding-top: var(--space-4);
margin: 0 0 var(--space-5) 0;
}
details.doc-section > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
gap: var(--space-2);
font-family: var(--font-body);
font-weight: 700;
font-size: var(--scale-3);
color: var(--ink);
margin-bottom: var(--space-4);
}
details.doc-section > summary::-webkit-details-marker { display: none; }
details.doc-section > summary::before {
content: "";
flex: none;
width: 0;
height: 0;
border-left: .5em solid var(--ink-soft);
border-top: .35em solid transparent;
border-bottom: .35em solid transparent;
transition: transform .15s ease;
}
details.doc-section[open] > summary::before { transform: rotate(90deg); }
details.doc-section h3 { margin-top: var(--space-4); }
details.doc-section h3:first-of-type { margin-top: 0; }
/* ---- topnav: the booklet top menu (rule 7, the Studio pattern) ---- */
.topnav { position: sticky; top: 0; z-index: 60; background: var(--paper); border-bottom: 1px solid var(--line); }
.topnav-inner { max-width: var(--measure-container); margin: 0 auto; padding: var(--space-2) var(--space-4);
display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.topnav .brand { font-weight: 700; text-decoration: none; color: var(--ink); font-size: var(--scale-0);
letter-spacing: .08em; text-transform: uppercase; font-family: var(--font-ui); }
.topnav .brand .home { color: var(--accent); }
.topnav-links { display: flex; gap: var(--space-3); flex-wrap: wrap; font-size: var(--scale-0); font-family: var(--font-ui); }
.topnav-links a { color: var(--ink-soft); text-decoration: none; }
.topnav-links a:hover { color: var(--accent); }