Design System
The visual language of the product UI. Per ADR-0002 and ADR-0004.
This page is the standing reference. The actual CSS tokens live in
src/ui/public/base.css and src/config/rag.js in the main repo.
Palette
Base palette
Cream-paper background, dark text, minimal chrome. The look is "considered judgement," not "system alarm."
| Token | Hex | Use |
|---|---|---|
--bg-page |
#f4f1ea |
Page background (warm cream) |
--bg-panel |
#ffffff |
Panels, cards, table rows |
--bg-secondary-zone |
#ede8db |
Financial Institutions Supplement zone (warmer) |
--text-primary |
#1a1815 |
Body text |
--text-secondary |
#5c5751 |
Labels, metadata |
--text-muted |
#928d85 |
Helper text, placeholders |
--border-subtle |
#e8e3d6 |
Panel borders, table dividers |
--border-strong |
#c4bcab |
Section dividers |
--accent |
#3a2e1c |
Interactive elements, links |
--accent-hover |
#5c4a32 |
Hover states |
RAG palette
Dustier than pure RAG. Bright red/amber/green look like dashboard alarms; the chosen palette reads as "considered judgement."
| Token | Hex | Use |
|---|---|---|
--rag-red |
#b8392c |
Text and accent for red state |
--rag-red-bg |
rgba(184, 57, 44, 0.08) |
Background tint |
--rag-amber |
#c47a1f |
Text and accent for amber state |
--rag-amber-bg |
rgba(196, 122, 31, 0.08) |
Background tint |
--rag-green |
#3a6b56 |
Text and accent for green state |
--rag-green-bg |
rgba(58, 107, 86, 0.08) |
Background tint |
Applied via modifier classes: .rag-red, .rag-amber, .rag-green on
the score number, with -bg variant for hero-panel backgrounds.
RAG thresholds
Composite ESG score and pillar scores
| Band | Range | Meaning |
|---|---|---|
| Red | < 40 | Negative signals dominate; institution materially flagged |
| Amber | 40 – 69 | Mixed or neutral; further investigation warranted |
| Green | ≥ 70 | Positive signals dominate |
Coverage
| Band | Range | Meaning |
|---|---|---|
| Red | < 25% | Score is interpretable only with strong caveats |
| Amber | 25 – 59% | Score is informative but partial |
| Green | ≥ 60% | Coverage sufficient for confident interpretation |
Red flags
| State | Colour | Meaning |
|---|---|---|
| Exposed | Red | Institution has exposure to the flagged area |
| Clear | Green | No direct exposure on file |
No amber state for red flags — they're binary at intake. If a flag is uncertain, the watchlist is the right surface.
Typography
| Use | Family | Weight | Size | Notes |
|---|---|---|---|---|
| Body text | Inter | 400 / 500 | 15px / 22px line-height | Default everywhere |
| Headings | Inter | 600 | Scale (28/22/18/16) | Tighter line-height for h1/h2 |
| Hero numbers | Inter | 700 | 56px | Tabular numerics enabled |
| Codes/labels | JetBrains Mono | 500 | 13px | Rule IDs, source codes, LEIs |
| Page metadata | Inter | 400 | 13px | Run stamps, breadcrumbs |
No serifs. No italics. Both proved visually noisy in mock iteration.
Tabular numerics: font-variant-numeric: tabular-nums on all numeric
displays (scores, percentages, counts). Numbers align in columns; one
number's width = another number's width.
Spacing
A simple 4px-base scale:
| Token | Value | Use |
|---|---|---|
--space-1 |
4px | Hair-line gaps |
--space-2 |
8px | Tight clustering |
--space-3 |
12px | Default within a panel |
--space-4 |
16px | Between sub-sections |
--space-6 |
24px | Between panels |
--space-8 |
32px | Section dividers |
--space-12 |
48px | Major zone transitions (e.g. hero → rule table) |
Layout
Page width
Container max-width: 1280px. Centred. Padding at narrower viewports. Below 1024px the layout doesn't degrade gracefully — desktop only per ADR-0002.
Grid
The detail page uses a two-column layout: main content (768px-ish) + sidebar (288px-ish). Sidebar holds watchlist, shareholders, peer table, score history. Index and methodology pages are single-column full-width.
Sticky elements
- Top nav: sticky, 56px tall
- Filter pills on rule table: sticky to top of rule section when scrolled
Components
Inventory of recurring UI components, kept consistent across pages.
Hero panel (institution detail)
Two equally-weighted numbers side by side: composite score, coverage. 56px Inter bold, tabular numerics, RAG colour. Three secondary metrics (confidence, peer rank, delta) below in 13px metadata style. Coverage explainer paragraph (body text, ~50 words) under the numbers.
Pillar box
Three boxes (E / S / G) in a row. Each shows pillar score (large number), weight (small label), coverage (small label), covered-rule count (small label). RAG-coloured score number.
Rule row
Single row in the rule evaluation table: - Rule ID (mono, 13px) - Description (body text) - Source (mono small, link to upstream) - Value (the actual finding) - Score contribution (RAG-coloured if relevant) - Confidence indicator (visual: dot or bar) - Coverage indicator (visual: filled circle if covered, empty if not)
Filter pills
Above the rule table: All / E / S / G / Covered only / Uncovered only.
Pill style: rounded rectangle, current selection filled in --accent,
others outlined.
Theme group header
In the rule table, themes group rules visually with a subtle divider and theme name label. Per ADR-0005.
Watchlist card (sidebar)
One per watchlist finding. Source badge + summary line + observed-at timestamp. Click expands to full finding detail.
Peer table (sidebar)
Compact table of peer institutions: name, composite score, coverage. Highlights current institution's row. Per-row click navigates to that institution's detail page.
Accessibility
Per ADR-0004 and WCAG 2.1:
- Red/green colour pairing is supplemented by tonal contrast: red is darker than green at standard luminance. ~8% of male users have colour-vision deficiency; relying on hue alone fails them.
- Numbers are always visible alongside RAG colours. The score is "11%" not just a red dot.
- In tables where colour alone differentiates, an icon or text label accompanies the colour. The rule row's covered/uncovered status uses an icon (filled / empty circle) plus the colour.
- Keyboard navigation works through all interactive elements. Filter pills, sort headers, table rows are all reachable via Tab.
- Focus styles are visible (2px solid outline,
--accentcolour).
Anti-patterns
Things tried in mock iteration that didn't work:
- Italics. Looked dated and noisy. Removed.
- Bright RAG (pure #FF0000 etc.). Felt like a dashboard alarm. Replaced with dustier shades.
- Multiple accent colours. The cream-paper palette with one accent reads as considered; multiple accents read as busy.
- Decorative use of RAG. RAG is reserved for status. Using red or amber decoratively dilutes the status signal.
- Mobile-responsive design. Tried, looked awful, abandoned. Desktop-only per ADR-0002.
- Serif headings. Tried for the "considered judgement" feel; in context with dense data tables, looked old-fashioned. All-Inter proved cleaner.
Tokens file
The canonical tokens live in two places:
src/ui/public/base.css— CSS custom properties for all the abovesrc/config/rag.js— JS thresholds + colour-mapping logic referenced in templates (e.g.ragBand(score)returns'red' | 'amber' | 'green')
No magic numbers in templates. Always reference the token.