diff --git a/plugins/voyage/playground/voyage-playground.html b/plugins/voyage/playground/voyage-playground.html index fb0314d..a90130a 100644 --- a/plugins/voyage/playground/voyage-playground.html +++ b/plugins/voyage/playground/voyage-playground.html @@ -843,6 +843,14 @@ font: inherit; } .voyage-back-btn:hover { background: var(--color-bg-soft); } + + /* v4.3 remediation: color-contrast fix for finding 09132940. + The vendored DS sets `.key-stat__label` to var(--color-text-tertiary) + which is #6E7781 in light theme — borderline 4.5:1 WCAG-AA contrast + and flagged by axe-core on the small (11px) label text. Override + scoped to playground (no DS file changes) using --color-text-secondary + (#4D5663, 7.4:1) which clears WCAG-AA comfortably for labels. */ + .key-stat__label { color: var(--color-text-secondary); } diff --git a/plugins/voyage/tests/e2e/voyage-playground-a11y.spec.mjs b/plugins/voyage/tests/e2e/voyage-playground-a11y.spec.mjs index ab359bb..067e14d 100644 --- a/plugins/voyage/tests/e2e/voyage-playground-a11y.spec.mjs +++ b/plugins/voyage/tests/e2e/voyage-playground-a11y.spec.mjs @@ -120,6 +120,31 @@ test.describe('voyage-playground a11y (axe-core)', () => { expect(grownRules, `GROWN rule-counts vs baseline: ${JSON.stringify(grownRules, null, 2)}`).toEqual([]); }); + // v4.3 Step 4 — DIAGNOSTIC test (removed in Step 5/Wave 3). Prints the + // node selectors flagged by color-contrast so we can target scoped CSS + // overrides at exactly those nodes (finding 09132940). + // Asserts ZERO color-contrast violations after the inline-style override + // is applied — passes only when remediation is complete. + test('DIAGNOSTIC — print color-contrast node selectors (09132940)', async ({ page }) => { + await page.goto('voyage-playground.html'); + await page.evaluate(() => { + window.localStorage.setItem('voyage-theme', 'light'); + document.documentElement.setAttribute('data-theme', 'light'); + document.documentElement.style.colorScheme = 'light'; + }); + await page.reload(); + await page.waitForLoadState('domcontentloaded'); + const results = await new AxeBuilder({ page }) + .options({ runOnly: ['color-contrast'] }) + .analyze(); + const nodes = results.violations.flatMap((v) => + v.nodes.map((n) => ({ rule: v.id, target: n.target, html: (n.html || '').slice(0, 80) })), + ); + // Diagnostic emission — visible via --reporter=line + console.log('[DIAGNOSTIC color-contrast nodes]', JSON.stringify(nodes)); + expect(nodes, `color-contrast violations remain: ${JSON.stringify(nodes, null, 2)}`).toEqual([]); + }); + test('pixel-diff smoke 1280×900 — light + dark within 2% threshold (SC1 backup)', async ({ page }) => { await page.setViewportSize({ width: 1280, height: 900 }); // Light theme baseline