From f9b16cac8bcebff0e72e49790f11172f13dd24cd Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sun, 19 Jul 2026 01:43:23 +0800
Subject: [PATCH 1/5] fix(ui): complete component library remediation
---
docs/design-system.md | 17 +-
package.json | 3 +-
scripts/check-design-system-contract.mjs | 301 ++
scripts/design-system-contract-baseline.json | 5 +
src/app/globals.css | 194 +-
src/app/layout.tsx | 4 +-
src/app/therapy-compass/layout.tsx | 1 +
src/components/AccessibleTable.tsx | 2 +-
src/components/ClinicalDashboard.tsx | 2 +-
src/components/DocumentManagementActions.tsx | 33 +-
src/components/DocumentViewer.tsx | 26 +-
src/components/applications-launcher-page.tsx | 4 +-
.../clinical-dashboard/ClinicalSidebar.tsx | 8 +-
.../account-setup-dialog.tsx | 29 +-
.../clinical-dashboard/auth-panel.tsx | 20 +-
.../clinical-dashboard/cross-mode-links.tsx | 8 +-
.../clinical-dashboard/differentials-home.tsx | 6 +-
.../clinical-dashboard/document-admin.tsx | 4 +-
.../clinical-dashboard/document-results.tsx | 4 +-
.../document-search-results.tsx | 24 +-
.../clinical-dashboard/evidence-panels.tsx | 8 +-
.../favourites-command-library-page.tsx | 2 +-
.../clinical-dashboard/favourites-hub.tsx | 22 +-
.../favourites-library-nav.tsx | 8 +-
.../clinical-dashboard/image-lightbox.tsx | 4 +-
.../master-search-header.tsx | 26 +-
.../clinical-dashboard/mode-action-popup.tsx | 5 +-
.../clinical-dashboard/settings-dialog.tsx | 14 +-
.../clinical-dashboard/signed-image.tsx | 2 +-
.../universal-search-command-surface.tsx | 2 +-
.../clinical-dashboard/visual-evidence.tsx | 8 +-
.../differentials/diagnosis-map-panel.tsx | 12 +-
.../differential-detail-page.tsx | 18 +-
.../differential-presentation-actions.tsx | 2 +-
...ifferential-presentation-workflow-page.tsx | 18 +-
.../differential-stream-page.tsx | 4 +-
.../non-pdf-source-preview.tsx | 8 +-
.../document-viewer/pdf-canvas-viewer.tsx | 8 +-
src/components/dsm/dsm-comparison-page.tsx | 2 +-
src/components/dsm/dsm-diagnosis-page.tsx | 2 +-
.../dsm-differential-considerations-page.tsx | 4 +-
src/components/dsm/dsm-search-page.tsx | 2 +-
.../factsheets/factsheet-detail-page.tsx | 8 +-
src/components/forms/form-detail-page.tsx | 10 +-
src/components/pwa-lifecycle.tsx | 4 +-
.../services/service-detail-page.tsx | 6 +-
.../services/services-navigator-page.tsx | 2 +-
src/components/therapy-compass/bindings.tsx | 109 +-
src/components/therapy-compass/controls.ts | 36 +-
src/components/therapy-compass/nav.tsx | 147 +-
.../therapy-compass/screens/brief-screen.tsx | 274 +-
.../screens/compare-screen.tsx | 221 +-
.../therapy-compass/screens/detail-screen.tsx | 243 +-
.../therapy-compass/screens/other-screen.tsx | 70 +-
.../screens/pathways-screen.tsx | 173 +-
.../screens/recommend-screen.tsx | 193 +-
.../therapy-compass/screens/search-screen.tsx | 69 +-
.../therapy-compass/screens/sheets-screen.tsx | 327 +-
src/components/therapy-compass/style-utils.ts | 29 -
src/components/therapy-compass/styles.tsx | 83 -
.../therapy-compass/therapy-card.tsx | 97 +-
.../therapy-compass/therapy-compass.css | 3433 +++++++++++++++++
src/components/therapy-compass/ui.tsx | 113 +-
src/components/therapy-compass/workspace.tsx | 57 +-
src/components/ui-primitives.tsx | 66 +-
src/lib/theme.ts | 6 +
tests/theme.test.ts | 32 +-
...herapy-compass-responsive-contract.test.ts | 41 +-
tests/ui-accessibility.spec.ts | 66 +
tests/ui-overlay-css-contract.test.ts | 4 +-
tests/ui-primitives.dom.test.tsx | 28 +-
71 files changed, 4944 insertions(+), 1879 deletions(-)
create mode 100644 scripts/check-design-system-contract.mjs
create mode 100644 scripts/design-system-contract-baseline.json
delete mode 100644 src/components/therapy-compass/style-utils.ts
delete mode 100644 src/components/therapy-compass/styles.tsx
create mode 100644 src/components/therapy-compass/therapy-compass.css
diff --git a/docs/design-system.md b/docs/design-system.md
index d77766b42..b8c0cbee4 100644
--- a/docs/design-system.md
+++ b/docs/design-system.md
@@ -18,7 +18,10 @@ contract and the code — not reinvention. If a change genuinely needs a new dir
`src/app/globals.css` (`:root` + `.dark`). No raw Tailwind palette classes (`red-50`,
`slate-200`, `bg-white`) and no hex values in components. **If you typed a hex or a Tailwind
colour name in a component, you broke dark mode** — those values have no `.dark` override.
- The only sanctioned exception: third-party brand marks (Microsoft/Google OAuth tiles).
+ Sanctioned raw-colour exceptions are explicit and narrow: brand artwork, diagnostic-only
+ visualizations, generated OpenGraph artwork, emergency error fallbacks, and the scoped
+ fixed-white Therapy patient sheet. `scripts/check-design-system-contract.mjs` owns the
+ path allowlist; adding a category requires documenting why semantic app tokens are wrong.
- **Semantic vs categorical vs brand.** Three token families, never interchangeable:
- Semantic triads (`--info/-soft/-border`, `--success-*`, `--warning-*`, `--danger-*`) mean
something happened or matters clinically. Green is success-only; red is safety/danger-only.
@@ -167,12 +170,14 @@ image"}` — never a possibly-empty variable alone.
1. `npm run verify:cheap` — lint, typecheck, unit tests, runtime + sitemap checks (offline-safe).
2. `npm run ensure` then `npm run verify:ui` — Chromium Playwright (smoke, stress,
accessibility, tools, overlap). Required for any UI/styling/routing change.
-3. `node scripts/check-type-scale.mjs` — the count must not exceed the recorded baseline.
-4. Manual dark-mode pass on every screen you touched (theme toggle in the sidebar).
-5. Reduced-motion + forced-colors spot check on touched surfaces
+3. `npm run check:design-system-contract` — production-only raw colours, literal shadows,
+ Therapy inline-parser/style debt, and tap-token drift must not exceed the recorded baseline.
+4. `node scripts/check-type-scale.mjs` — the count must not exceed the recorded baseline.
+5. Manual dark-mode pass on every screen you touched (theme toggle in the sidebar).
+6. Reduced-motion + forced-colors spot check on touched surfaces
(`ui-accessibility.spec.ts` covers the automated slice; emulate in devtools for the rest).
-6. `npm run format:check`.
-7. Fill the PR template; the clinical-governance preflight applies only if you touched
+7. `npm run format:check`.
+8. Fill the PR template; the clinical-governance preflight applies only if you touched
ingestion/answer/search/source-access surfaces — pure UI work states that explicitly.
## 10. File conventions
diff --git a/package.json b/package.json
index a2f67ddb8..68251c959 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"test:e2e:visual": "node scripts/run-playwright.mjs --config=playwright.visual.config.ts",
"test:cross-tenant:staging": "node scripts/run-tsx.mjs scripts/test-cross-tenant-staging.ts",
"verify:cheap": "node scripts/run-heavy.mjs --npm-script verify:cheap:internal",
- "verify:cheap:internal": "npm run check:runtime && npm run check:github-actions && npm run check:ci-scope && npm run check:ci-triage && npm run check:pr-policy && npm run sitemap:check && npm run brand:check && npm run check:type-scale && npm run check:icon-scale && npm run check:function-grants && npm run check:owner-scope && npm run lint && npm run typecheck && npm run test",
+ "verify:cheap:internal": "npm run check:runtime && npm run check:github-actions && npm run check:ci-scope && npm run check:ci-triage && npm run check:pr-policy && npm run sitemap:check && npm run brand:check && npm run check:type-scale && npm run check:icon-scale && npm run check:design-system-contract && npm run check:function-grants && npm run check:owner-scope && npm run lint && npm run typecheck && npm run test",
"verify:pr-local": "node scripts/verify-pr-local.mjs",
"verify:ui": "npm run check:runtime && npm run test:e2e:pr",
"verify:release": "npm run check:runtime && npm run lint && npm run typecheck && npm run test && npm run build && npm run test:e2e && npm run check:production-readiness && npm run governance:release && npm run eval:quality:release",
@@ -103,6 +103,7 @@
"check:july8-live-batch": "node scripts/run-tsx.mjs scripts/check-july8-live-batch.ts",
"check:type-scale": "node scripts/check-type-scale.mjs --strict",
"check:icon-scale": "node scripts/check-icon-scale.mjs --strict",
+ "check:design-system-contract": "node scripts/check-design-system-contract.mjs",
"check:function-grants": "node scripts/check-function-grants.mjs",
"check:owner-scope": "node scripts/check-owner-scope-api.mjs",
"recover:ingestion": "node scripts/run-tsx.mjs scripts/recover-ingestion-queue.ts",
diff --git a/scripts/check-design-system-contract.mjs b/scripts/check-design-system-contract.mjs
new file mode 100644
index 000000000..97fc6c98b
--- /dev/null
+++ b/scripts/check-design-system-contract.mjs
@@ -0,0 +1,301 @@
+import fs from "node:fs";
+import path from "node:path";
+import process from "node:process";
+import ts from "typescript";
+
+const ROOT = process.cwd();
+const SRC_ROOT = path.join(ROOT, "src");
+const BASELINE_PATH = path.join(ROOT, "scripts", "design-system-contract-baseline.json");
+const PRINT_METRICS = process.argv.includes("--print-metrics");
+const SOURCE_EXTENSIONS = new Set([".css", ".ts", ".tsx"]);
+const LEGACY_TAP_CLASS = /(?:^|\s)(?:h|w|min-h|min-w|size)-11(?=\s|$)/g;
+const LEGACY_TAP_CLASS_TEST = /(?:^|\s)(?:h|w|min-h|min-w|size)-11(?=\s|$)/;
+const RAW_COLOR = /#[0-9a-f]{3,8}\b|\b(?:rgb|rgba|hsl|hsla|oklch)\(/gi;
+const LITERAL_SHADOW_CLASS = /shadow-\[(?!var\()[^\]]+\]/g;
+const CUSTOM_CONTROL_CLASS_PROP =
+ /(?:closeButtonClassName|sheetCloseButtonClassName|buttonClassName|triggerClassName)\s*=\s*(?:"[^"]*|`[^`]*)\b(?:h|w|min-h|min-w|size)-11\b/g;
+
+// Raw literals are permitted only at the source-of-truth or where a fixed
+// external/rendering contract makes semantic app tokens inappropriate.
+const RAW_COLOR_EXEMPTIONS = [
+ { category: "global theme tokens", pattern: /^src\/app\/globals\.css$/ },
+ {
+ category: "brand artwork",
+ pattern:
+ /^src\/(?:lib\/brand-(?:mark\.ts|image\.tsx)|components\/clinical-dashboard\/(?:brand|provider-brand-icons)\.tsx)$/,
+ },
+ {
+ category: "diagnostic visualizations",
+ pattern: /^src\/components\/(?:web-vitals-reporter|clinical-dashboard\/visual-evidence)\.tsx$/,
+ },
+ { category: "OpenGraph artwork", pattern: /^src\/app\/opengraph-image\.tsx$/ },
+ { category: "error fallbacks", pattern: /^src\/(?:app\/global-error|components\/route-error-boundary)\.tsx$/ },
+ { category: "printable Therapy paper", pattern: /^src\/components\/therapy-compass\/therapy-compass\.css$/ },
+ {
+ category: "printable factsheet paper",
+ pattern: /^src\/components\/factsheets\/factsheet-detail-page\.tsx$/,
+ },
+];
+
+const toPosix = (value) => value.split(path.sep).join("/");
+
+function isPrototype(relativePath) {
+ return (
+ relativePath.includes("/mockups/") ||
+ relativePath.includes("-mockup") ||
+ relativePath.includes("/favourites-page-mockups/") ||
+ relativePath.includes("/calculator-mockups/")
+ );
+}
+
+function walk(directory) {
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
+ const absolutePath = path.join(directory, entry.name);
+ if (entry.isDirectory()) return walk(absolutePath);
+ if (!SOURCE_EXTENSIONS.has(path.extname(entry.name))) return [];
+ const relativePath = toPosix(path.relative(ROOT, absolutePath));
+ return isPrototype(relativePath) ? [] : [{ absolutePath, relativePath }];
+ });
+}
+
+function countMatches(text, pattern) {
+ return [...text.matchAll(pattern)].length;
+}
+
+function withoutComments(text) {
+ return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
+}
+
+function rawColorIsExempt(relativePath) {
+ return RAW_COLOR_EXEMPTIONS.some(({ pattern }) => pattern.test(relativePath));
+}
+
+function jsxClassText(attribute) {
+ const initializer = attribute.initializer;
+ if (!initializer) return "";
+ if (ts.isStringLiteral(initializer)) return initializer.text;
+ if (!ts.isJsxExpression(initializer) || !initializer.expression) return "";
+ if (ts.isStringLiteralLike(initializer.expression) || ts.isNoSubstitutionTemplateLiteral(initializer.expression)) {
+ return initializer.expression.text;
+ }
+ if (ts.isTemplateExpression(initializer.expression)) {
+ return [
+ initializer.expression.head.text,
+ ...initializer.expression.templateSpans.map((span) => span.literal.text),
+ ].join(" ");
+ }
+ return initializer.expression.getText();
+}
+
+function findInteractiveTapLiterals(file) {
+ if (!file.relativePath.endsWith(".tsx")) return [];
+ const sourceText = fs.readFileSync(file.absolutePath, "utf8");
+ const source = ts.createSourceFile(file.relativePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
+ const findings = [];
+ const interactiveTags = new Set(["a", "button", "input", "select", "summary", "textarea"]);
+
+ function inspectOpeningElement(node) {
+ const tag = node.tagName.getText(source);
+ if (!interactiveTags.has(tag)) return;
+ const classAttribute = node.attributes.properties.find(
+ (attribute) => ts.isJsxAttribute(attribute) && attribute.name.getText(source) === "className",
+ );
+ if (!classAttribute || !ts.isJsxAttribute(classAttribute)) return;
+ const classText = jsxClassText(classAttribute);
+ if (!LEGACY_TAP_CLASS_TEST.test(classText)) return;
+ const line = source.getLineAndCharacterOfPosition(classAttribute.getStart(source)).line + 1;
+ findings.push(`${file.relativePath}:${line}`);
+ }
+
+ function visit(node) {
+ if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) inspectOpeningElement(node);
+ ts.forEachChild(node, visit);
+ }
+
+ visit(source);
+ return findings;
+}
+
+function findTherapyButtonsWithoutBaseClass(file) {
+ if (!file.relativePath.startsWith("src/components/therapy-compass/") || !file.relativePath.endsWith(".tsx"))
+ return [];
+ const sourceText = fs.readFileSync(file.absolutePath, "utf8");
+ const source = ts.createSourceFile(file.relativePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
+ const findings = [];
+
+ function visit(node) {
+ if (
+ (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) &&
+ node.tagName.getText(source) === "button"
+ ) {
+ const classAttribute = node.attributes.properties.find(
+ (attribute) => ts.isJsxAttribute(attribute) && attribute.name.getText(source) === "className",
+ );
+ const classText = classAttribute && ts.isJsxAttribute(classAttribute) ? jsxClassText(classAttribute) : "";
+ if (!classText.includes("tc-btn")) {
+ const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
+ findings.push(`${file.relativePath}:${line}`);
+ }
+ }
+ ts.forEachChild(node, visit);
+ }
+
+ visit(source);
+ return findings;
+}
+
+const files = walk(SRC_ROOT);
+const contents = new Map(files.map((file) => [file.relativePath, fs.readFileSync(file.absolutePath, "utf8")]));
+const textAt = (relativePath) => contents.get(relativePath) ?? "";
+const failures = [];
+const assert = (condition, message) => {
+ if (!condition) failures.push(message);
+};
+
+const metrics = {
+ rawColorLiterals: 0,
+ literalShadowClasses: 0,
+ legacyTapClasses: 0,
+};
+
+for (const file of files) {
+ const source = textAt(file.relativePath);
+ const contractSource = withoutComments(source);
+ if (!rawColorIsExempt(file.relativePath)) metrics.rawColorLiterals += countMatches(contractSource, RAW_COLOR);
+ metrics.literalShadowClasses += countMatches(contractSource, LITERAL_SHADOW_CLASS);
+ metrics.legacyTapClasses += countMatches(contractSource, LEGACY_TAP_CLASS);
+ assert(
+ !CUSTOM_CONTROL_CLASS_PROP.test(source),
+ `${file.relativePath} contains a legacy 44px class in a control class prop`,
+ );
+ CUSTOM_CONTROL_CLASS_PROP.lastIndex = 0;
+}
+
+const interactiveTapFindings = files.flatMap(findInteractiveTapLiterals);
+assert(
+ interactiveTapFindings.length === 0,
+ `interactive elements still use literal *-11 tap classes: ${interactiveTapFindings.join(", ")}`,
+);
+
+const therapyFiles = files.filter(({ relativePath }) => relativePath.startsWith("src/components/therapy-compass/"));
+const therapyButtonsWithoutBaseClass = therapyFiles.flatMap(findTherapyButtonsWithoutBaseClass);
+const therapySource = therapyFiles.map(({ relativePath }) => textAt(relativePath)).join("\n");
+const therapyInlineStyleFindings = therapyFiles.flatMap(({ relativePath }) => {
+ const source = textAt(relativePath);
+ return source
+ .split(/\r?\n/)
+ .map((line, index) => ({ line, index: index + 1 }))
+ .filter(({ line }) => /style=\{/.test(line))
+ .filter(({ line }) => {
+ if (relativePath.endsWith("/icons.tsx")) return !/style=\{style\}/.test(line);
+ if (relativePath.endsWith("/ui.tsx")) return !/--tc-meter-width/.test(line);
+ if (relativePath.endsWith("/screens/compare-screen.tsx")) return !/--tc-compare-columns/.test(line);
+ return true;
+ })
+ .map(({ index }) => `${relativePath}:${index}`);
+});
+
+assert(!therapySource.includes("style={s("), "Therapy Compass still invokes the runtime style parser");
+assert(!therapySource.includes("style-utils"), "Therapy Compass still imports the runtime style parser");
+assert(
+ therapyButtonsWithoutBaseClass.length === 0,
+ `Therapy buttons bypass the shared interaction states: ${therapyButtonsWithoutBaseClass.join(", ")}`,
+);
+assert(
+ !fs.existsSync(path.join(ROOT, "src/components/therapy-compass/style-utils.ts")),
+ "style-utils.ts must stay retired",
+);
+assert(
+ !fs.existsSync(path.join(ROOT, "src/components/therapy-compass/styles.tsx")),
+ "the runtime Therapy style island must stay retired",
+);
+assert(
+ therapyInlineStyleFindings.length === 0,
+ `unscoped Therapy inline styles found: ${therapyInlineStyleFindings.join(", ")}`,
+);
+assert(!/outline\s*:\s*none/i.test(therapySource), "Therapy Compass suppresses a focus outline");
+assert(!therapySource.toLowerCase().includes("#8a94a3"), "the low-contrast patient-sheet gray returned");
+
+const therapyCss = textAt("src/components/therapy-compass/therapy-compass.css");
+assert(!/(?:^|[^0-9])44px/.test(therapyCss), "Therapy Compass CSS contains a literal 44px tap target");
+assert(therapyCss.includes("--tc-paper-muted: #5b6472"), "the fixed paper palette must keep its accessible muted ink");
+assert(
+ therapyCss.includes('.tc-paper [contenteditable="true"]:focus-visible'),
+ "patient-sheet editing needs a visible focus state",
+);
+assert(therapyCss.includes(".tc-btn:hover:not(:disabled)"), "Therapy buttons need a hover state");
+assert(therapyCss.includes(".tc-btn:disabled"), "Therapy buttons need a disabled state");
+
+const paperRules = therapyCss.slice(
+ therapyCss.indexOf(".tc-root .tc-screens-sheets-screen-023"),
+ therapyCss.indexOf(".tc-root .tc-screens-sheets-screen-050"),
+);
+assert(
+ !/var\(--(?:background|surface|border|text|clinical|command|focus)/.test(paperRules),
+ "patient-sheet paper rules leaked theme-reactive application tokens",
+);
+
+const semanticSources = [
+ "src/components/therapy-compass/nav.tsx",
+ "src/components/therapy-compass/therapy-card.tsx",
+ "src/components/therapy-compass/screens/brief-screen.tsx",
+ "src/components/therapy-compass/screens/compare-screen.tsx",
+ "src/components/therapy-compass/screens/search-screen.tsx",
+ "src/components/therapy-compass/screens/sheets-screen.tsx",
+]
+ .map(textAt)
+ .join("\n");
+assert(semanticSources.includes("aria-current"), "Therapy navigation needs aria-current");
+assert(semanticSources.includes("aria-pressed"), "Therapy toggles need aria-pressed");
+assert(semanticSources.includes("aria-selected"), "Therapy tabs need aria-selected");
+assert(
+ /disabled=\{items\.length === 0\}/.test(textAt("src/components/therapy-compass/screens/compare-screen.tsx")),
+ "empty compare Clear must be disabled",
+);
+
+const globals = textAt("src/app/globals.css");
+assert(!/^\s*--space-\d+\s*:/m.test(globals), "unused --space-* tokens returned");
+const primitives = textAt("src/components/ui-primitives.tsx");
+assert(
+ primitives.includes('export const chatComposerInput = "chat-composer-input"'),
+ "composer input chrome must have one CSS owner",
+);
+assert(primitives.includes("aria-[invalid=true]"), "shared fields need an invalid state");
+assert(primitives.includes("read-only:"), "shared fields need a read-only state");
+assert(primitives.includes("export function AsyncButton"), "shared async button semantics are missing");
+
+for (const target of [
+ "src/components/DocumentViewer.tsx",
+ "src/components/clinical-dashboard/favourites-hub.tsx",
+ "src/components/clinical-dashboard/master-search-header.tsx",
+ "src/components/clinical-dashboard/mode-action-popup.tsx",
+ "src/components/clinical-dashboard/settings-dialog.tsx",
+]) {
+ assert(!/\bring-white\b|\bbg-white\b/.test(textAt(target)), `${target} bypasses the shared glass/toggle recipes`);
+}
+
+if (!PRINT_METRICS) {
+ assert(fs.existsSync(BASELINE_PATH), "design-system contract baseline is missing");
+ if (fs.existsSync(BASELINE_PATH)) {
+ const baseline = JSON.parse(fs.readFileSync(BASELINE_PATH, "utf8"));
+ for (const [metric, value] of Object.entries(metrics)) {
+ assert(value <= baseline[metric], `${metric} increased from ${baseline[metric]} to ${value}`);
+ }
+ }
+}
+
+if (PRINT_METRICS) {
+ console.log(JSON.stringify(metrics, null, 2));
+ process.exit(failures.length === 0 ? 0 : 1);
+}
+
+if (failures.length > 0) {
+ console.error("Design-system contract failed:\n");
+ for (const failure of failures) console.error(`- ${failure}`);
+ process.exit(1);
+}
+
+console.log(
+ `Design-system contract passed (${files.length} production files; raw colors ${metrics.rawColorLiterals}; literal shadows ${metrics.literalShadowClasses}; legacy tap classes ${metrics.legacyTapClasses}).`,
+);
+console.log(`Raw-color exemptions: ${RAW_COLOR_EXEMPTIONS.map(({ category }) => category).join(", ")}.`);
diff --git a/scripts/design-system-contract-baseline.json b/scripts/design-system-contract-baseline.json
new file mode 100644
index 000000000..3ac12017b
--- /dev/null
+++ b/scripts/design-system-contract-baseline.json
@@ -0,0 +1,5 @@
+{
+ "rawColorLiterals": 9,
+ "literalShadowClasses": 6,
+ "legacyTapClasses": 22
+}
diff --git a/src/app/globals.css b/src/app/globals.css
index 48dc21e36..3a1e28002 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -263,14 +263,6 @@
--shadow-lux: inset 0 1px 0 rgb(255 255 255 / 64%), 0 12px 34px rgb(8 16 24 / 7%);
--shadow-inset: inset 0 1px 0 rgb(255 255 255 / 58%);
--shadow-card: var(--shadow-tight);
- --space-1: 0.25rem;
- --space-2: 0.5rem;
- --space-3: 0.75rem;
- --space-4: 1rem;
- --space-6: 1.5rem;
- --space-8: 2rem;
- --space-12: 3rem;
- --space-16: 4rem;
--safe-area-top: env(safe-area-inset-top, 0px);
--safe-area-right: env(safe-area-inset-right, 0px);
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
@@ -553,14 +545,9 @@ summary::-webkit-details-marker {
* scripts/capture-chrome-parity.ts). Responsive padding overrides are nested
* here so the whole class stays in one layer.
*
- * The COMPOSER PILL surface below (answer-footer-search-pill, a
)}
diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx
index 05f21b300..b50f41bd6 100644
--- a/src/components/DocumentViewer.tsx
+++ b/src/components/DocumentViewer.tsx
@@ -54,6 +54,7 @@ import {
fieldControl,
fieldLabel,
floatingControl,
+ glassOverlaySurface,
InlineNotice,
LoadingPanel,
panel,
@@ -1262,7 +1263,7 @@ function DocumentManualTagEditor({