Warm Meridian Design System Audit Report
Warm Meridian Design System Audit Report
Section titled “Warm Meridian Design System Audit Report”Date: 7 March 2026 (Session 63) Scope: All 9 functional areas of Knowledge Hub Design system: Warm Meridian (OKLCH, Instrument Sans, stone/amber palette) Method: Parallel code review agents scanning 50+ component and page files
Executive Summary
Section titled “Executive Summary”The Warm Meridian migration (Session 62) was exceptionally thorough. Across all 9 functional areas and 50+ files audited:
- Zero raw Tailwind colour class violations (e.g.
text-green-600,bg-amber-50) — the semantic token system is fully adopted - Zero
dark:colour prefixes — dark mode is handled automatically by the CSS variable layer - Comprehensive accessibility patterns — keyboard navigation, ARIA labels, multi-signal indicators (colour + icon + text)
- Consistent 4pt spacing grid and typography hierarchy
Overall grade: B+ → A- (after quick wins applied in this session)
Issues by Severity
Section titled “Issues by Severity”| Severity | Found | Fixed (This Session) | Remaining |
|---|---|---|---|
| Critical | 0 | — | 0 |
| Major | 4 | 4 | 0 |
| Minor | 29 | 10 | 19 |
Issues Fixed This Session
Section titled “Issues Fixed This Session”Major Issues (All Resolved)
Section titled “Major Issues (All Resolved)”| # | Area | Issue | Fix |
|---|---|---|---|
| 1 | Codebase-wide | text-white used on 8 components + 2 UI primitives | Replaced with text-primary-foreground everywhere |
| 2 | Codebase-wide | Missing --destructive-foreground token in globals.css | Added to :root, .dark, and @theme inline |
| 3 | Login | alert() used for password reset confirmation | Replaced with toast.success() |
| 4 | Item Detail | Raw <button> in error.tsx instead of <Button> component | Replaced with <Button> |
Minor Issues (10 Resolved)
Section titled “Minor Issues (10 Resolved)”| # | Area | Issue | Fix |
|---|---|---|---|
| 1 | Settings | Sidebar width w-[220px] not on 4pt grid | Changed to w-56 (224px) |
| 2 | Review | Emoji icons (✅ ✨) in empty states | Replaced with Lucide CheckCircle2 / Sparkles |
| 3 | Review | Panel size props using string values | Converted to numbers |
| 4 | Review | text-destructive for flagged items (error semantic) | Changed to text-status-warning (governance) |
| 5 | Review | Missing overscroll-contain on main panel | Added |
| 6 | Bid | ConfidenceDot using pale *-bg token for dot | Added dot field using vivid foreground token |
| 7 | Login | ”Forgot password?” button missing focus ring | Added focus-visible:ring-2 styles |
| 8 | UI primitives | dark: prefixes on destructive button/badge variants | Removed (CSS variables handle dark mode) |
| 9 | Settings | text-white on team deactivate button | → text-primary-foreground |
| 10 | Settings | text-white on taxonomy deactivate button | → text-primary-foreground |
Remaining Issues (Not Fixed — Complete Inventory)
Section titled “Remaining Issues (Not Fixed — Complete Inventory)”Tier 1: Accessibility Quick Wins (Agent-Safe — No Investigation Needed)
Section titled “Tier 1: Accessibility Quick Wins (Agent-Safe — No Investigation Needed)”All items below have specific file locations, line numbers, and exact fixes
documented in the raw batch reports (/tmp/audit-batch-{1,2,3}.md). Agents can
execute these directly.
| # | Area | Issue | File | Fix | Effort |
|---|---|---|---|---|---|
| 1 | Q&A Library | Bulk operation progress lacks aria-live region | bulk-action-toolbar.tsx:161 | Add aria-live="polite" to progress text container | 5 min |
| 2 | Q&A Library | Filter count change not announced to screen readers | library-content.tsx:162 | Wrap count text in aria-live="polite" region | 5 min |
| 3 | Q&A Library | Badge filter chips not keyboard-accessible | content-library-drawer.tsx:204 | Use <button> styled as badges, or add role="button" tabIndex={0} onKeyDown | 15 min |
| 4 | Item Detail | ContentTabs textarea lacks aria-label | content-tabs.tsx:269 | Add aria-label={Edit ${field}} | 5 min |
| 5 | Item Detail | QAAnswerDisplay textareas lack aria-label | qa-answer-display.tsx:51,83 | Add aria-label="Standard answer" / aria-label="Advanced answer" | 5 min |
| 6 | Coverage | StatCard icons missing aria-hidden | coverage-summary-cards.tsx | Add aria-hidden="true" to Icon component | 5 min |
| 7 | Coverage | RefreshCw icon missing aria-hidden | coverage-content.tsx:182 | Add aria-hidden="true" | 2 min |
| 8 | Coverage | Gap cells lack contextual aria-label | coverage-gap-cell.tsx | Add aria-label="[subtopic name] — no content" | 10 min |
| 9 | Home | Activity feed missing role="feed" / role="article" | dashboard-activity-feed.tsx | Add role="feed" to container, role="article" to items | 10 min |
| 10 | Home | AttentionCard link text not descriptive enough for AT | attention-card.tsx | Make actionLabel include count context for screen readers | 15 min |
| 11 | Settings | Governance config list items lack semantic structure | governance-section.tsx | Add role="list" and role="listitem" | 15 min |
| Total | ~92 min |
Tier 2: Visual/Semantic Quick Wins (Agent-Safe)
Section titled “Tier 2: Visual/Semantic Quick Wins (Agent-Safe)”| # | Area | Issue | File | Fix | Effort |
|---|---|---|---|---|---|
| 12 | Home | QuickStatsStrip freshness values lack colour-coded dots | quick-stats-strip.tsx | Add coloured dots using --color-freshness-* tokens | 15 min |
| 13 | Home | Rollback activity icon could use text-status-warning | dashboard-activity-feed.tsx | Change rollback icon class | 5 min |
| 14 | Home | ”Knowledge Hub” heading could use text-fluid-2xl | app/page.tsx | Change text-xl to text-fluid-2xl | 2 min |
| 15 | Settings | Orphaned <Label> on read-only role display | profile-section.tsx | Replace <Label> with <p> or <span> | 5 min |
| 16 | Item Detail | Editing banner could add pencil icon | item-detail-client.tsx:532 | Add <Pencil className="size-3.5" /> before text | 5 min |
| 17 | Item Detail | Loading skeleton max-w-4xl vs content max-w-7xl | app/item/[id]/loading.tsx | Change to max-w-7xl | 2 min |
| 18 | Bid | Two raw <a> elements should use Button asChild + Link | app/bid/[id]/page.tsx:165,270 | Replace with <Button asChild><Link>...</Link></Button> | 10 min |
| 19 | Browse | Date input format hint may conflict with browser locale | filter-panel.tsx | Add helper text clarifying format | 10 min |
| 20 | Browse | Grid vs list view missing domain-coloured left border | content-row.tsx | Add borderLeftColor inline style matching ContentCard pattern | 20 min |
| 21 | Review | Inline shadow should be extracted to CSS variable | review-action-bar.tsx:91 | Define --shadow-review-bar in globals.css | 15 min |
| 22 | Login | Share email state between password and magic link forms | app/login/page.tsx | Lift email state to share between both forms | 10 min |
| Total | ~99 min |
Tier 3: Larger Improvements (Agent-Safe With Coordination)
Section titled “Tier 3: Larger Improvements (Agent-Safe With Coordination)”Reconcile status (S397, ID-124): all 6 items already satisfied or gone — 0 executors dispatched. #23
QualityFlagBadgealready extracted (content-card.tsx:66); #24 FilterPanel already uses shadcnCheckbox; #26 governance list alreadyrole=list/listitem; #27BidContextSyncremoved (bid relocated toapp/procurement/); #28 CopilotKit retired. Lone optional residual: #25 — the team-member “grid divs” gap is gone (now an accessiblerole=listwith per-control aria-labels); converting to a true semantic<table>is an optional design judgment, not a defect.
| # | Area | Issue | File(s) | Effort |
|---|---|---|---|---|
| 23 | Browse | Extract QualityFlagBadge from 3x duplication in ContentCard | content-card.tsx | 20 min |
| 24 | Browse | Replace native checkboxes in FilterPanel with shadcn Checkbox | filter-panel.tsx | 20 min |
| 25 | Settings | Team member list: grid divs → semantic <table> | team-section.tsx | 45 min |
| 26 | Settings | Governance config list add semantic role structure | governance-section.tsx | 15 min |
| 27 | Bid | Replace BidContextSync setInterval(500ms) with callback ref | app/bid/[id]/session/page.tsx | 30 min |
| 28 | Bid | CopilotKit sidebar Warm Meridian theming overrides | globals.css + bid-copilot-sidebar.tsx | 60-90 min |
| Total | ~3-4 hrs |
Tier 4: Design Decisions (Need User Input)
Section titled “Tier 4: Design Decisions (Need User Input)”Status (S397): not yet actioned — needs Liam design input. #29 login brand identity is blocked on the Canonical wordmark asset (Thread C
/design-sync). #32 (item-detail 874-line refactor) is likely stale — item-detail was already split into ReaderView/EditorView; reconcile before any work.
These require design direction, not just code changes.
| # | Area | Issue | Notes | Effort |
|---|---|---|---|---|
| 29 | Login | Page lacks brand personality (generic template look) | Needs logo/brand mark asset, warm background treatment | 1-2 hrs |
| 30 | Q&A Library | Six filter dropdowns create visual density overload | Design decision: overflow pattern vs current inline layout | 1-2 hrs |
| 31 | Browse | ContentCard 448 lines with duplication — refactor? | Decompose into CardHeader/CardFooter/QACard sub-components | 60+ min |
| 32 | Item Detail | item-detail-client.tsx 874 lines (mostly JSX) | Extract provenance + draft toggle sections | 60+ min |
| 33 | Login | Email-first authentication flow (enter once, choose method) | UX decision: restructure login flow | 2-3 hrs |
Tier 5: Observations (No Action Needed)
Section titled “Tier 5: Observations (No Action Needed)”These were noted during audit but require no fix.
| # | Area | Observation |
|---|---|---|
| A | Q&A Library | QARow expand/collapse: full-row button is correct, chevron visual affordance is subtle but functional |
| B | Item Detail | Metadata sidebar hide-on-desktop edit pencils: acceptable power-user pattern, keyboard-discoverable |
| C | Review | Action bar flash animation briefly competes with focus ring (150ms, minimal impact) |
| D | Q&A Library | Select triggers rely on placeholder for screen reader identification (acceptable per ARIA patterns) |
Per-Page Findings
Section titled “Per-Page Findings”1. Home Dashboard
Section titled “1. Home Dashboard”Grade: A-
Semantic tokens: 100% compliant. Dark mode: fully ready. Accessibility: strong
(section landmarks, heading hierarchy, icon labelling). Layout: well-balanced
two-column dashboard. The Active Bids section now uses BidListCard (updated
this session).
Remaining: QuickStatsStrip freshness colour-coding, activity feed ARIA roles.
2. Browse Page
Section titled “2. Browse Page”Grade: A-
Semantic tokens: 100% compliant. Dark mode: fully ready. Grid/list view consistency is strong. Filter controls have proper ARIA. Infinite scroll with IntersectionObserver works well. ContentCard at 448 lines is the largest component but functions correctly.
Remaining: Quality flag badge extraction, native checkbox styling.
3. Q&A Library
Section titled “3. Q&A Library”Grade: B+
Semantic tokens: 100% compliant. Dark mode: fully ready. Keyboard navigation with arrow keys is excellent. Six filter dropdowns create visual density on narrow screens. Badge filter chips in content library drawer need keyboard accessibility.
Remaining: aria-live regions, keyboard-accessible badge filters.
4. Item Detail
Section titled “4. Item Detail”Grade: A- (after error.tsx fix)
Semantic tokens: 100% compliant. Dark mode: fully ready. Content layout and
readability are strong. Editing mode transitions are well-handled with
beforeunload guard. Metadata sidebar has good show-on-hover edit pattern.
Remaining: Textarea aria-label attributes, editing banner icon.
5. Bid Workflow
Section titled “5. Bid Workflow”Grade: A
Semantic tokens: 100% compliant (60+ bid-specific tokens). Dark mode: fully ready. Accessibility is comprehensive — state badge uses colour + text + dot (triple signal), confidence badge has WCAG comment. Keyboard focus on cards is proper. ConfidenceDot now uses vivid foreground token.
Remaining: Two raw <a> elements, CopilotKit sidebar theming.
6. Coverage Dashboard
Section titled “6. Coverage Dashboard”Grade: A- (after text-white fix)
Semantic tokens: 100% compliant. Dark mode: fully ready. Data visualisation is accessible (coloured dots + text labels, not colour-only). Information density is well-balanced with collapsible domain sections.
Remaining: Icon aria-hidden attributes, gap cell context labels.
7. Review Queue
Section titled “7. Review Queue”Grade: A (after emoji/panel/semantic fixes)
Semantic tokens: 100% compliant. Dark mode: fully ready. Accessibility is the
strongest of all areas — full keyboard shortcuts (Enter, F, arrows, E, L, Esc,
?), role="toolbar", role="article", aria-live announcements, screen
reader-friendly action labels. Speed-review UX is polished.
Remaining: Inline shadow extraction to CSS variable.
8. Settings Page
Section titled “8. Settings Page”Grade: B+ (after text-white and sidebar fixes)
Semantic tokens: 100% compliant (after fixes). Dark mode: fully ready. Tab
navigation is well-implemented with URL persistence. Forms are consistently
structured. Team member list using grid divs instead of semantic <table> is
the main accessibility gap.
Remaining: Semantic table, governance list structure.
9. Login Page
Section titled “9. Login Page”Grade: B (after alert() and focus ring fixes)
Semantic tokens: 100% compliant. Dark mode: fully ready. Form accessibility is
correct (labels, htmlFor, autoComplete, role="alert"). The main gap is
brand personality — the page uses correct tokens but looks generic. This is a
design decision rather than a code issue.
Remaining: Brand mark/logo, warm background treatment.
Cross-Cutting Themes
Section titled “Cross-Cutting Themes”1. Semantic Token System — Fully Adopted
Section titled “1. Semantic Token System — Fully Adopted”The @theme inline registration pattern in Tailwind v4 works well. All 60+
semantic tokens (freshness, confidence, bid state, governance, streaming phases,
template review, quality scoring) are consistently used. No raw Tailwind colour
classes were found in any audited file.
2. Dark Mode — Automatic and Complete
Section titled “2. Dark Mode — Automatic and Complete”The CSS variable approach means zero dark: prefixes are needed in components.
Light and dark modes are handled entirely by the :root / .dark token
redefinitions. This is a significant maintenance win.
3. Accessibility — Strong Foundation
Section titled “3. Accessibility — Strong Foundation”WCAG 2.1 AA compliance is strong across the platform. Key patterns:
- Section landmarks with
aria-label - Heading hierarchy (
h1→h2→h3) - Focus indicators (
ring-2 ring-ring ring-offset-2) - Multi-signal status indicators (never colour alone)
- 44x44px minimum touch targets
- Keyboard navigation where appropriate
Gaps are minor (aria-live regions, a few missing aria-label attributes).
4. Component Quality
Section titled “4. Component Quality”Components are well-structured with consistent patterns. The largest files (ContentCard 448 lines, item-detail-client 874 lines) have been partially decomposed with extracted hooks and sub-components. Further extraction is possible but not urgent.
5. Design System Principle Adherence
Section titled “5. Design System Principle Adherence”| Principle | Score | Notes |
|---|---|---|
| Signal, not noise | 9/10 | Amber reserved for CTAs. Status colours are semantic. |
| Orientation without instruction | 8/10 | Section headers and spatial structure guide well. |
| Semantic over arbitrary | 10/10 | Zero violations after fixes. |
| Invisible craft | 9/10 | Clean, undecorated. Cards and typography do structural work. |
| Accessible by default | 8/10 | Strong foundation. Minor gaps in live regions and labels. |
Recommendations for Next Session
Section titled “Recommendations for Next Session”Priority 1: Quick Accessibility Wins (30 min total)
Section titled “Priority 1: Quick Accessibility Wins (30 min total)”Apply the 7 “High Value” remaining items above — all are aria-live,
aria-label, or aria-hidden additions. No visual changes, purely assistive
technology improvements.
Priority 2: Login Page Brand Identity (1-2 hrs)
Section titled “Priority 2: Login Page Brand Identity (1-2 hrs)”The login page is the first impression. Adding a brand mark and warm background treatment would significantly improve the perceived quality of the product.
Priority 3: CopilotKit Sidebar Theming (1-2 hrs)
Section titled “Priority 3: CopilotKit Sidebar Theming (1-2 hrs)”The CopilotKit sidebar in the bid session likely renders with default theming
that may clash with the Warm Meridian palette. CSS overrides targeting the
.bid-copilot-sidebar class should align it.
Audit completed 7 March 2026, Session 63. All findings verified against codebase. Quick wins applied and verified with build, test, and lint.
Audit Closeout — Session 390 (22/06/2026)
Section titled “Audit Closeout — Session 390 (22/06/2026)”A reconciliation pass against the live codebase ahead of ID-122 found that roughly 90% of the Tier 1 + Tier 2 remainder had already been satisfied by intervening work — most notably the ID-119 design-system config-readiness wave (functional ramps, core/domain token split, naming normalisation) and the S198 follow-up refactors. The three items below were the only live gaps; all are resolved in the source-code PR that triggered this update.
Tier 1 — Resolved
Section titled “Tier 1 — Resolved”| # | Original area | Original entry | Fix landed (ID-122) |
|---|---|---|---|
| 8 | Coverage | Gap cells lack contextual aria-label | components/coverage/coverage-gap-cell.tsx — gap-cell root carries role="group" plus a contextual aria-label so the cell announces “{subtopic} — no content” as a single unit to assistive technology. |
| 10 | Home | AttentionCard link text not descriptive enough for AT | components/dashboard/unified-attention-section.tsx — action-link aria-label is now count-aware and action-led, e.g. “Review items (1 item) — {title}”. (The audit’s attention-card.tsx is now this file.) |
Tier 2 — Resolved
Section titled “Tier 2 — Resolved”| # | Original area | Original entry | Fix landed (ID-122) |
|---|---|---|---|
| 19 | Browse | Date input format hint may conflict with browser locale | components/browse/filter-panel.tsx — a <p> hint with id filter-date-format-hint now reads “Format follows your browser locale (e.g. DD/MM/YYYY)” and both date inputs reference it via aria-describedby, surfacing the format to AT users. |
Tier 1 + Tier 2 — Already-satisfied (no diff)
Section titled “Tier 1 + Tier 2 — Already-satisfied (no diff)”The remaining items in the Tier 1 + Tier 2 tables above were verified already satisfied by prior work (ID-119, S198, and later refactors) at the time of the ID-122 reconciliation pass. No further code changes were required for those rows.
Out of scope for this closeout
Section titled “Out of scope for this closeout”- Tier 3, Tier 4 and Tier 5 items remain tracked in their respective tables. ID-122 was scoped to the Tier 1 + Tier 2 remainder only.
- The Canonical product-name flip (default branding routed through
BRANDING.productName) shipped alongside ID-122 on the source side and the live-prose sweep on the docs-site side landed in a separate commit onmain. The audit-table heading reference at row #14 is intentionally not rewritten here — it is preserved as the historical record of the audited surface.