Foundations
The rule sets every language, chart, and page builds on. Numbered, permanent, cited where perception research backs a law.
FN-01 . Token contract
The rule that makes the whole catalogue themeable and deterministic:
Chart and component specs reference color ROLES, never hex values. Design languages (DL files) bind roles to hex. Rebranding = swapping the DL token file. No spec changes, no re-authoring.
Required roles (every DL must bind all of these)
| Role | Meaning |
|---|---|
background | page/canvas fill |
ink | headings, key numbers, strongest text |
body | supporting text, labels, sources |
primary | the one accent; highlights, key series |
muted | context series, de-emphasized marks |
gridline | chart gridlines |
hairline | rules, baselines, separators |
positive | good deltas (always paired with a word, never alone) |
negative | bad deltas (same) |
Optional roles a DL may add: panel, benchmark, warning, primaryDark, and others. Specs that want an optional role declare a fallback chain, for example "context": ["benchmark", "muted"]: first role present in the palette wins. The renderer resolves the chain, so a spec never breaks on a DL that lacks an optional role.
Rules
- Hex values live in
tokens/DL-xx.jsononly. A hex value inside a chart spec or component spec fails validation on sight. - All hex is
#RRGGBBuppercase. Enforced byschema/dl.schema.jsonandtools/validate.py. inkandbodymust reach 4.5:1 contrast onbackground(WCAG AA). Enforced bytools/validate.py.- Fonts follow the same contract: specs reference
displayorbody, the DL binds each to a family stack whose last entries are installed-everywhere fallbacks.
FN-02 . Canvas and spacing grid
Canvases
| Target | Size | Use |
|---|---|---|
| Chart entry golden render | 960 x 540 px | every CH-xx golden |
| Full slide (16:9) | 1280 x 720 px | recipes (DL/CH/CP combinations) |
All positions and sizes in specs are exact pixels on these canvases. Adjectives ("comfortable margin", "medium bar") are banned in specs; a value the renderer cannot consume is not a spec.
The 8px grid
Every structural distance is a multiple of 8: margins, offsets, bar heights, gaps, plot bounds.
| Distance | Value |
|---|---|
| Canvas edge to content | 32 px |
| Title block to plot area | >= 40 px |
| Bar height / bar gap (CH-01) | 32 / 16 px |
| Label offset from mark | 8 or 16 px |
Text sizes are exempt from the 8-multiple (they follow FN-03) but their anchor positions are not.
Fixed furniture on every chart canvas
- Title: x=32, baseline y=56 (y=88 when the DL chrome adds an eyebrow).
- Source line: x=32, baseline y=516, always present, always bottom-left.
- Kicker + eyebrow: only when the DL's
chromeblock asks for them; kicker 24x4 px at (32, 28), eyebrow 11 px caps at (32, 52).
FN-03 . Type scale
Sizes are fixed per text role on the 960x540 chart canvas. A DL changes the family and weight, never the size or position (those belong to the chart spec).
| Text role | Size | Font slot | Notes |
|---|---|---|---|
| Title | 24 px | display | full sentence, states the finding with a number |
| Eyebrow | 11 px | body, weight 600 | CAPS, letter-spacing 2, only if DL chrome |
| Category / series label | 13 px | body | weight 600 only on the highlighted item |
| Value label | 13 px | body | weight 600 only on the highlighted item |
| Tick label | 11 px | body | |
| X-axis label | 11 px | body | |
| Annotation | 12 px | body, italic | |
| Source line | 11 px | body |
Rules
- Two font slots only:
display(titles) andbody(everything else). A third slot (monofor IDs and timestamps) is reserved for DLs that declare it; no spec may require it. - Every DL family stack ends in an installed-everywhere fallback (Segoe UI, Arial, Georgia, Consolas). Silent substitution with a different metric font is the failure mode this prevents.
- Nothing below 11 px on the 960x540 canvas. On the 1280x720 slide canvas the floor is 12 px.
- Emphasis is weight (400 to 600), never a size bump, never underline.
FN-04 . Contrast and color use
Hard limits (validated, not advisory)
inkandbodyreach 4.5:1 onbackground(WCAG AA body text).tools/validate.pycomputes this from the DL token file and blocks on failure.- Meaning is never carried by color alone. Status, deltas, and exceptions always pair color with a word or a number.
- One saturated highlight per chart. The
primaryrole appears on the one mark the title talks about; everything else usesmutedorbenchmark.
Discipline
- Series count before color count: if a chart needs more than 4 colors, the chart is wrong (split into small multiples), not the palette.
positiveandnegativemark deltas and status, never decorate.- Charts must survive grayscale: the highlighted mark must still read as the highlight through weight, position, or label emphasis.
The banned list (applies to every DL, every chart)
- Purple/blue gradient backgrounds, "hero" gradient bands
- Rainbow categorical palettes (one hue per bar)
- Color-only status dots
- Anything below AA contrast, including gray-on-gray captions
- 3D depth, glassmorphism, heavy drop shadows
FN-05 . Numbers, units, and scales
Numbers
- Tabular (monospaced-figure) rendering wherever numbers stack in columns; right-aligned in tables.
- Units appear ONCE per chart, not per value: - bar charts: on the first (largest) value label ("34 items", then "28", "21", ...) - line charts: on the top y tick label ("80%", then "60", "40", ...)
- Missing value = "-" (hyphen nil marker), never zero, never a guess.
- Titles state the finding with a number. "Backlog overview" fails QA; "Electrical holds 34 of 109 open items" passes.
Scales (deterministic by construction)
Bars start at zero, always, no exceptions. Line charts start at zero by default; a non-zero baseline requires a written justification in the entry that uses it.
Tick generation is an algorithm, not taste, so two renders can never disagree:
raw = max_value / target_ticks (target_ticks = 5)
mag = 10 ^ floor(log10(raw))
step = first of {1, 2, 5, 10} * mag that is >= raw
top = ceil(max_value / step) * step
ticks = 0, step, 2*step, ... top
Direct-label bar charts skip the axis entirely and scale bars to the max value; the labels carry the numbers.
Float formatting in renders
All coordinates format as max-2-decimal, trailing zeros stripped, "-0" normalized to "0". This is what makes byte-identical re-renders possible across machines.
FN-06 . Perceptual foundations
Every OVP rule traces to a named finding in visual perception research, not to taste. This entry is the trace, one line per rule family. It is deliberately one page: OVP cites the science, it does not rewrite it.
| OVP rule | Perceptual basis |
|---|---|
One primary highlight per chart (FN-04) | Preattentive pop-out: one distinct hue is found in constant time; two or more force serial search (Treisman; Ware) |
| Bars and positions over pies and areas (CH family design) | Graphical perception hierarchy: position and length are decoded far more accurately than angle and area (Cleveland & McGill) |
Pie capped at 3 slices, warn status (CH-PTW-03/04) | Angle judgment error grows with slice count; under ~15% differences angles are unreadable (Cleveland & McGill) |
| Max 4 stacked segments, max 5 KPI cards | Working-memory span: ~4 chunks held reliably (Cowan; Miller) |
| Direct labels and line-end labels, never legends | Every legend lookup is a saccade round-trip that reloads working memory (Ware; Few) |
| Annotation anchored at the data (CH-TIM rules) | Attention follows pointers; unanchored commentary is ignored under load (Ware) |
| 8px grid, gaps group related elements (FN-02) | Gestalt proximity and common region: spacing IS structure |
| Status word + color, never color alone (FN-04) | ~8% of men have deficient color vision; hue is not a channel you can require (accessibility canon) |
| Zero baselines on bars and areas (FN-05) | Length encoding lies when truncated; the lie compounds with fill (Tufte's lie factor) |
| Muted context + one saturated series | Contrast drives salience; salience should follow the message, not the palette (Ware; Few) |
| Action titles stating the finding (CORE rule 1) | Assertion-evidence structure improves recall and decision agreement versus topic titles (presentation research canon) |
| Tabular figures, right-aligned numbers (FN-05) | Digit alignment enables column scanning as a single visual object |
Canon referenced: Bertin (Semiologie graphique), Cleveland & McGill (graphical perception experiments), Ware (Information Visualization), Munzner (Visualization Analysis and Design), Tufte (VDQI), Few (Show Me the Numbers), Treisman (preattentive processing), Cowan/Miller (working memory), Kahneman (attention and effort).
Rule for extending OVP: a new rule that cannot cite a row here, or add one, is a preference, and preferences do not enter the protocol.
<!-- Generated by tools/build_blocks.py from decision/voice.json. Do not hand-edit. -->
FN-07 . Voice and prose
Design languages control how a page looks. This contract controls how it reads. It bans the patterns that make machine-written prose identical everywhere, and each design language layers its own register on top.
A design language that controls the palette but leaves the words to chance ships half an identity. Every generated word now resolves the same way a color does: the universal laws below first, then the voice block of the design language in use.
The universal laws
| # | Law | Why | How it is checked |
|---|---|---|---|
| V1 | No em-dashes. | The single strongest machine-writing tell. A comma, a period, a colon, or a parenthesis always fits. | no U+2014 or U+2013 in any generated prose |
| V2 | Numbers replace intensity adjectives. | An intensity word without a quantity is an opinion wearing a fact's clothes. If it cannot be quantified, it cannot be claimed. | a vague_words hit with no digit in the same sentence fails |
| V3 | The banned phrase list is closed and greppable. | These phrases carry no information and appear in every model's output. Deleting them costs nothing and removes the accent. | no banned_phrases substring, case-insensitive |
| V4 | Active voice with a named subject. | Hollow constructions hide who acts. Someone decides, ships, blocks, approves. The exception is legal register, where the document is the actor. | no hollow_patterns substring, case-insensitive |
| V5 | One idea per sentence, under the language's cap. | Stacked clauses are how a model pads. Each design language sets its own sentence_max_words. | every sentence within the design language's sentence_max_words |
| V6 | No throat-clearing. | The first sentence of a block carries content. An opener that restates the context is deleted, not shortened. | human review; the banned list covers the common openers |
| V7 | No decorative triads. | Three adjectives in a row are rhythm, not information. A list exists when the items differ in content. | human review |
| V8 | No exclamation marks. | Emphasis comes from the fact, not the punctuation. This holds in every design language. | no exclamation mark outside quoted material |
Exemptions
- Material inside straight double quotes: a rule may name the phrase it bans, and legal or press registers may quote a source verbatim.
- The generic side of a contrast example: it exists to show the failure and MUST trip at least one law.
The closed lists
Machine-checkable, case-insensitive, substring match. Canonical source: decision/voice.json.
Banned phrases (33): it's worth noting, it is worth noting, worth noting that, delve, dive into, in today's, fast-paced, seamless, holistic, cutting-edge, game-changer, unlock the, elevate your, streamline, navigate the complexities, at the end of the day, when it comes to, plays a crucial role, not just, but rather, the landscape of, leveraging, leverage the, testament to, a myriad of, in conclusion, furthermore, moreover, interesting, notably, importantly, clearly, obviously.
Words that need a number in the same sentence (23): significant, major, substantial, considerable, dramatic, notable, remarkable, robust, various, several, properly, appropriately, as needed, efficiently, effectively, potential, potentially, carefully, stunning, exceptional, finest, world-class, passion.
Hollow constructions (14): is being made, are being made, progress is being, it was decided, there are several, there is a need, we believe, we would like to, we are pleased to, it might be, may need to, going forward, a good idea, in order to.
Per-language voice
Each design language carries a voice block in its token file: stance, register, mood, hedging policy, a sentence cap, a title cap, the title grammar, three precepts to follow, three to avoid, and one contrast pair. The validator checks that the block's own prose obeys the laws above, that the examples fit the declared caps, and that the generic counter-example trips at least one law.
| Language | Register | Sentence cap | Title cap | Title example |
|---|---|---|---|---|
| DL-01 AGRAW | measured, formal-plain | 18 words | 12 words | Capex holds at plan; the schedule does not. |
| DL-02 KATA | clipped, telegraphic | 12 words | 12 words | Clearance: 82% complete, up 5 points on last week. |
| DL-03 LOGOS | analytic prose | 25 words | 14 words | Freight cost peaked in March because the joints shipped by air |
| DL-04 SHILPA | technical, impersonal | 16 words | 12 words | JB-4102 termination sequence, revision C |
| DL-05 OBEYA | spoken, direct | 8 words | 8 words | Who unblocks the north loops this week? |
| DL-06 LEX | legal-neutral | 22 words | 14 words | Contractor notified of differing site conditions on 14 March 2025 (Letter C-1042) |
| DL-07 SAGA | journalistic | 22 words | 12 words | First joint clears customs after a three-week hold |
| DL-08 SENTINEL | label vocabulary, closed set | 10 words | 10 words | STATION 4 PRESSURE: WATCH. TRENDING +2%/H SINCE 06:00. |
| DL-09 ATLAS | positional, kinetic | 18 words | 12 words | KP 812 to 840: three crossings pending, corridor clear |
| DL-10 BASIRA | spare, documentary | 20 words | 14 words | Station 7, 28 July. First permanent power, eleven weeks early. |
| DL-11 SUTRA | formal, dense | 20 words | 12 words | Recommendation to defer the station tie-in to Q2 |
| DL-12 NOROSHI | urgent-terse | 12 words | 10 words | Diesel stock: 6 days left. Decision needed Thursday. |
| DL-13 MIZAN | warm, literary-plain | 24 words | 12 words | Cedar, brass, and forty years of the same hands |
| DL-14 EVIDENTIA | quantified, footnoted | 20 words | 14 words | Logistics drives 62% of the overrun; three orders carry half |
| DL-15 ABRID | structural, relational | 20 words | 12 words | Three levers, one constraint: clearance capacity gates them all |
| DL-16 TELOS | direct, conclusive | 18 words | 12 words | Yes: the Q4 target holds if trenching restarts by 1 October |
Runtime cost
The prose above is the handbook surface and never enters an agent context. The runtime payload is the voice block inside the one design language a request resolves to, plus the laws above in compact form. Section 2.1 of PROTOCOL.md governs: an agent loads one language, not the catalogue.