diff --git a/frontend/e2e/helpers/contrast.ts b/frontend/e2e/helpers/contrast.ts index 56491e42..f10488e3 100644 --- a/frontend/e2e/helpers/contrast.ts +++ b/frontend/e2e/helpers/contrast.ts @@ -29,6 +29,41 @@ export const mountContrastProbes = async (page: Page, probes: Record): Promise => { + await page.evaluate( + ({ entries, hostId }) => { + document.getElementById(hostId)?.remove(); + const host = document.createElement('div'); + + host.id = hostId; + host.className = 'tiptap-content'; + host.style.cssText = 'position:fixed;inset:0 auto auto 0;z-index:2147483647;padding:12px'; + + const surface = document.createElement('div'); + + surface.className = 'ProseMirror'; + surface.style.cssText = 'background:var(--card);display:flex;gap:8px;padding:8px'; + + for (const [name, className] of entries) { + const probe = document.createElement('span'); + + probe.className = className; + probe.dataset.contrast = name; + probe.textContent = 'Sample'; + surface.append(probe); + } + + host.append(surface); + document.body.append(host); + }, + { entries: Object.entries(probes), hostId: HOST_ID }, + ); +}; + export const measureContrast = async (page: Page, probe: string): Promise => page.evaluate((name) => { const element = document.querySelector(`[data-contrast="${name}"]`); diff --git a/frontend/e2e/specs/cross/contrast.spec.ts b/frontend/e2e/specs/cross/contrast.spec.ts index 079cf522..66dec30a 100644 --- a/frontend/e2e/specs/cross/contrast.spec.ts +++ b/frontend/e2e/specs/cross/contrast.spec.ts @@ -4,9 +4,14 @@ import { badgeVariants } from '@/components/ui/badge'; import { buttonVariants } from '@/components/ui/button'; import { expect, test } from '../../fixtures/test.ts'; -import { AA_NORMAL, measureContrast, mountContrastProbes } from '../../helpers/contrast.ts'; +import { AA_NORMAL, measureContrast, mountContrastProbes, mountEditorProbes } from '../../helpers/contrast.ts'; import { flowsCassette } from '../../mocks/cassettes/flows.ts'; +const EDITOR_TOKENS = { + 'editor-tag': 'template-tag', + 'editor-variable': 'template-variable', +} as const; + const BADGE_VARIANTS = [ 'blue', 'default', @@ -67,5 +72,18 @@ for (const theme of THEMES) { .soft(await measureContrast(page, 'destructive'), 'button on hover') .toBeGreaterThanOrEqual(AA_NORMAL); }); + + test('editor highlight tokens clear AA on the editor surface', async ({ page }) => { + await page.goto('/flows'); + await expect(page.getByRole('row', { name: /E2E Alpha/ })).toBeVisible(); + + await mountEditorProbes(page, EDITOR_TOKENS); + + for (const token of Object.keys(EDITOR_TOKENS)) { + expect + .soft(await measureContrast(page, token), `${token} (${theme})`) + .toBeGreaterThanOrEqual(AA_NORMAL); + } + }); }); } diff --git a/frontend/src/styles/index.css b/frontend/src/styles/index.css index bb46353f..a304ab6e 100644 --- a/frontend/src/styles/index.css +++ b/frontend/src/styles/index.css @@ -308,11 +308,12 @@ --sidebar-ring: oklch(0.25 0.14 245); /* Editor highlight TEXT, tuned for readable contrast on the editor surface (unlike --primary, a fill color that fails WCAG AA as text on dark). Four distinct hues so adjacent tokens don't blur: - link = theme blue 245; variable = violet 300; tag = teal 155; inline code = red 25 on a visible chip. + link = theme blue 245; variable = green 155 (matches the panel's "used" badge); tag = violet 300; + inline code = red 25 on a visible chip. --editor-code-bg is the opaque inline-code + code-block chip. Don't fold these back into --primary. */ --editor-accent: oklch(0.25 0.12 245); - --editor-variable: oklch(0.4 0.18 300); - --editor-tag: oklch(0.25 0.12 155); + --editor-variable: oklch(0.4 0.18 155); + --editor-tag: oklch(0.25 0.12 300); --editor-code: oklch(0.5 0.16 25); --editor-code-bg: oklch(0.94 0.006 245); --font-sans: Inter, 'Inter Fallback', sans-serif; @@ -371,8 +372,8 @@ --sidebar-border: oklch(0.3 0.04 245); --sidebar-ring: oklch(0.5 0.16 245); --editor-accent: oklch(0.68 0.13 245); - --editor-variable: oklch(0.72 0.12 300); - --editor-tag: oklch(0.68 0.13 155); + --editor-variable: oklch(0.72 0.12 155); + --editor-tag: oklch(0.68 0.13 300); --editor-code: oklch(0.7 0.15 25); --editor-code-bg: oklch(0.27 0.03 245); --font-sans: Inter, 'Inter Fallback', sans-serif; @@ -759,7 +760,7 @@ padding: 0 1px; } -/* ``, highlighted view-only (teal --editor-tag reads distinct from the violet variables). */ +/* ``, highlighted view-only. */ .tiptap-content .ProseMirror .template-tag { color: var(--editor-tag); background-color: color-mix(in oklab, var(--editor-tag) 9%, transparent);