Skip to content

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, --accent colour).

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 above
  • src/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.