RESEARCH — id-119: Design-system config-readiness (Warm Meridian audit)
RESEARCH — id-119: Design-system config-readiness
Section titled “RESEARCH — id-119: Design-system config-readiness”- Task:
ID-119(bun scripts/ledger-cli.ts show task 119). - Status: DECISION-SUPPORT RESEARCH — verbatim design-system audit; the {119.3} TECH spec acts on it.
- Origin: Claude Code web session, branch
claude/design-sync-cum9t6, filewarm-meridian-rebrand-audit.md(commitbdd659ec). Review-only pass, no code changed. - Scope of ID-119: the config-readiness ENABLERS only — F1 functional colour ramps, F2 core-vs-domain token split, F3 token-naming normalisation, and brand-string consolidation. Design decisions (brand direction A/B, typography identity, logo, live exploration) are explicitly OUT — deferred to
/design-sync.
Audit (verbatim — warm-meridian-rebrand-audit.md)
Section titled “Audit (verbatim — warm-meridian-rebrand-audit.md)”Warm Meridian — design-system audit & Canonical rebrand notes
Section titled “Warm Meridian — design-system audit & Canonical rebrand notes”Review-only pass. Grounded in app/globals.css (952 lines), components/ui/*,
app/layout.tsx, and the branding layer (lib/client-config.ts,
lib/branding/clients/default.json). No code changed.
Verdict in one line
Section titled “Verdict in one line”The foundations are genuinely good — OKLCH, a real two-layer intent, thorough dark mode, above-average accessibility infrastructure — but the semantic layer has sprawled into ~150 hand-maintained colour literals with no shared ramp, and application-domain concepts (bid/governance/entity/domain) are baked into the core token file. Those two things are the main drag on quality and the main friction for a rebrand. The brand-swap mechanics themselves are clean and centralised.
What’s strong (keep / showcase)
Section titled “What’s strong (keep / showcase)”- OKLCH throughout, perceptually-uniform, with a coherent warm story: stone neutrals at hue 48, amber signal at hue 55. Consistent and modern.
- Build-time brand injection is excellent.
--primary/--ringare overridden from a Zod-validated JSON (brandPrimaryColour), with the foreground and dark variant auto-derived (derivePrimaryForeground,deriveDarkVariantinlib/client-config.ts). Changing the brand colour is a one-value change that ripples correctly. This is the best-engineered part of the system. - Primitives are clean shadcn.
button.tsx/badge.tsxconsume only semantic tokens (bg-primary,text-secondary-foreground…), good variant/size APIs,data-slothooks. Reskinning via tokens works as intended — no hardcoded colour in component code. - Accessibility infra is above average: skip link,
prefers-reduced-motion, print styles,data-a11y-modevariants (dyslexia / high-contrast / large-text), a11y CSS vars for letter/word/line-height/weight/font-scale,lang="en-GB". Most design systems don’t ship this. Keep it front-and-centre in the Canonical story.
Findings & recommendations (highest leverage first)
Section titled “Findings & recommendations (highest leverage first)”1. Collapse ~150 status literals onto functional colour ramps — biggest win
Section titled “1. Collapse ~150 status literals onto functional colour ramps — biggest win”There is exactly one primitive ladder: --stone-50…900. Every coloured semantic
token hardcodes a raw OKLCH literal instead of referencing a primitive. The same “success
green” oklch(0.55 0.15 160) is independently re-typed across --color-freshness-fresh,
--color-confidence-strong, --color-governance-approved, --color-bid-won,
--color-quality-good, --color-verified, --color-tag-core,
--color-template-confirmed… and then again for dark mode. Multiply across green /
amber / red / blue / violet families and you get the ~150-literal, light-+-dark wall that
dominates the file.
Cost today: to reskin or tune one status colour you hand-edit ~8 places × 2 modes, by eye, with nothing keeping them in sync.
Fix: add a functional ramp primitive layer — --green-{...}, --amber-{...},
--red-{...}, --blue-{...}, --violet-{...} at a small set of L/C steps — and make every
status token reference a ramp step (--color-bid-won: var(--green-600)). Hundreds of
literals collapse to ~5 ramps; dark mode becomes “swap 5 ramps,” not “redefine 150
values.” This single change both raises quality and makes the whole rebrand a
config-level operation instead of a literal-hunt.
2. Separate core design tokens from application-domain tokens
Section titled “2. Separate core design tokens from application-domain tokens”The core file currently carries product taxonomy: --color-bid-* (8 statuses ×4
sub-tokens), --color-governance-*, --domain-security/compliance/methodology/…,
--color-entity-* (10 entity types), --color-relevance-*, --color-template-*,
--color-phase-*. These are Knowledge-Hub / procurement application semantics, not
design-system primitives.
Fix: keep the core token file to neutrals + brand + functional status
(success/warning/danger/info) + chart + radius/spacing/type. Move the domain tokens to a
feature stylesheet (@imported). A reusable “Canonical design system” should not know what
a bid or an entity type is — and a rebrand shouldn’t have to wade through them.
3. Normalise token naming
Section titled “3. Normalise token naming”Two conventions coexist: the base shadcn tokens are bare (--background, --primary,
--border) while everything added later is prefixed --color-*. A rebrand is the natural
moment to pick one (recommend keeping bare semantic names, scoping ramps under a clear
prefix).
4. Typography is the least-developed axis — and the biggest identity opportunity
Section titled “4. Typography is the least-developed axis — and the biggest identity opportunity”- One family: Instrument Sans (Google, loaded in
app/layout.tsx). Primitives use onlyfont-medium(500) andfont-semibold(600); body is 400. - No type-scale tokens beyond two ad-hoc fluid sizes (
--text-fluid-2xl/xl). No heading font, no editorial scale, no weight/tracking tokens.
For a product literally named Canonical (authority, the definitive record), typography is where identity is currently thinnest. A distinctive heading face (or a deliberate serif for editorial/“canonical document” feel) plus a real type-scale token set would add more brand character than any colour tweak. Recommend treating type as a primary rebrand lever, not an afterthought.
5. Verify primary-on-foreground contrast (AA)
Section titled “5. Verify primary-on-foreground contrast (AA)”--primary is amber oklch(0.65 0.16 55) and --primary-foreground is near-white
oklch(0.99 …). White text on a 0.65-lightness amber is the classic borderline case for
WCAG AA on small text (often lands ~3:1, under the 4.5:1 bar). Default buttons/badges use
exactly this pair. Action: measure it; if it fails, either darken primary for text
contexts or switch primary-foreground to a dark ink. (Easy to get right now that the
foreground is auto-derived.)
6. Minor
Section titled “6. Minor”- Elevation: only one custom shadow token (
--shadow-review-bar); everything else is Tailwind defaults. Fine, but there’s no elevation scale if the rebrand wants a distinctive depth language. - Radius:
--radius: 0.5rem(soft) with sm/md/lg/xl derived;rounded-mddominates. Conventional and pleasant. A sharper radius would read more “editorial/authoritative” if that’s the Canonical direction — strategic, not a defect. - Spacing: 4pt-grid tokens 1–16, but sparse (no 5/7/9/10/11). Harmless; primitives mostly use Tailwind scale directly.
Rebrand mechanics: Knowledge Hub → Canonical
Section titled “Rebrand mechanics: Knowledge Hub → Canonical”The swap is well-centralised — most of it is one file:
lib/branding/clients/default.json— the single brand record. Today every field is literally “Knowledge Hub”:productName,productShortName,organisationName,tagline(“Knowledge base platform for bid management”),supportEmail,logoAlt, plusbrandPrimaryColour: oklch(0.65 0.16 55)(mirrors--primary) andlogoUrl/faviconSvgUrl. Change these → product name, tagline, brand colour, and favicon all update.lib/client-config.ts—client_name: 'Knowledge Hub'(and feature labels).- ~55 hardcoded “Knowledge Hub” strings across
app/ components/ lib/. Two clusters matter most because they’re user-facing output: the AI skill prompts (lib/ai/skills/*.md— classification, governance) and the procurement export files (lib/procurement/procurement-export-{xlsx,docx,types}.ts). The rest are incidental. - Logo asset — current
logoUrlis/favicon.svg, 32px square: a placeholder mark, not a wordmark. A real Canonical logo/wordmark is the one genuinely new asset needed.
So the mechanical rebrand is small. The meaningful rebrand is the design decisions below.
Two brand directions to choose between
Section titled “Two brand directions to choose between”The current identity is warm + approachable (amber on warm stone). “Canonical” connotes authority, truth, the definitive source — worth deciding whether the visual language should reflect that.
- A. Evolve the warmth. Keep amber equity; refine it (fix the contrast, tighten the status ramps, develop typography). Lowest risk, keeps continuity. Good if Canonical should feel human and inviting.
- B. Shift to authoritative. Move the brand hue toward ink/indigo or deep teal, sharpen radius, introduce an editorial/serif heading face. Higher effort, stronger “system-of-record” gravitas. Good if Canonical should feel definitive and serious.
Because brand colour is a single derived value and typography is one next/font import, you
can prototype either direction cheaply — exactly the kind of exploration the Claude
Design sync (when you’re ready) is built to make interactive.
Suggested order of work
Section titled “Suggested order of work”- Pick a direction (A/B) — colour + type intent. Everything else follows.
- Refactor colour to functional ramps (finding 1) — do this before reskinning; it’s what makes reskinning a config change.
- Split core vs domain tokens (finding 2).
- Develop the typography axis (finding 4) — the highest-identity, lowest-touched lever.
- Execute the brand swap (default.json + string sweep + new logo).
- Verify contrast & a11y (finding 5) on the new palette.
When you want to see directions rendered live and iterate on real components, that’s the
point to run /design-sync (UI primitives + tokens) and explore in Claude Design.