diff --git a/apps/web/src/index.css b/apps/web/src/index.css index ca8b815..3211a4b 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -116,6 +116,39 @@ html.workspace-viewport-lock body { color: var(--foreground); } +/* + * The chat always renders on the light workspace surface + * (`bg-[#f2f0e7]`, near-black text), but the app forces the dark theme + * app-wide (`forcedTheme="dark"`). Streamdown's code/table/mermaid chrome and + * the rules below read theme tokens, which under `.dark` resolve to near-black + * values — producing unreadable dark-on-dark blocks. Re-declare the relevant + * tokens to warm-light values inside the markdown context so every token-driven + * chrome (headers, copy/download/fullscreen controls, table borders/cells, + * inline code) reads correctly on the light surface without touching the + * global theme or unrelated UI. + */ +.workspace-surface .chat-markdown { + --background: #ffffff; + --foreground: #232321; + --muted: #f4f4f2; + --muted-foreground: #69675f; + --card: #ffffff; + --card-foreground: #232321; + --popover: #ffffff; + --popover-foreground: #232321; + --secondary: #f4f4f2; + --secondary-foreground: #232321; + --accent: #efece4; + --accent-foreground: #232321; + --sidebar: #ffffff; + --sidebar-foreground: #232321; + --sidebar-accent: #f4f4f2; + --sidebar-accent-foreground: #232321; + --border: #e2e0d6; + --input: #e2e0d6; + --ring: #b6b7b4; +} + .workspace-surface .chat-markdown, .workspace-surface .chat-reasoning, .workspace-surface .thinking-line { @@ -223,6 +256,16 @@ html.workspace-viewport-lock body { text-align: start; } +/* Readable header band + zebra rows so tables stand out on the surface. */ +.chat-markdown thead th { + background: var(--muted); + font-weight: 600; +} + +.chat-markdown tbody tr:nth-child(even) { + background: color-mix(in oklch, var(--muted) 55%, transparent); +} + @media (prefers-reduced-motion: reduce) { .chat-markdown *, .chat-message, diff --git a/apps/web/src/lib/workspace/frontend-regressions.test.ts b/apps/web/src/lib/workspace/frontend-regressions.test.ts index 1aad4d4..609dba5 100644 --- a/apps/web/src/lib/workspace/frontend-regressions.test.ts +++ b/apps/web/src/lib/workspace/frontend-regressions.test.ts @@ -59,4 +59,20 @@ describe("Workspace frontend regression contracts", () => { expect(messageRenderer).toContain("mermaid"); expect(messageRenderer).toContain("plugins={streamdownPlugins}"); }); + + test("keeps markdown readable on the forced-dark workspace surface", () => { + const styles = source("../../index.css"); + + // The workspace surface is light, but the app forces the dark theme, so + // the markdown context must re-declare the theme tokens streamdown and the + // table/code rules read from. Without these the dark tokens (~22-28% + // lightness) produce near-black text, borders, and backgrounds. + expect(styles).toContain(".workspace-surface .chat-markdown"); + expect(styles).toContain("--muted: #f4f4f2"); + expect(styles).toContain("--border: #e2e0d6"); + expect(styles).toContain("--sidebar: #ffffff"); + // Table headers and zebra rows must stand out on the surface. + expect(styles).toContain(".chat-markdown thead th"); + expect(styles).toContain(".chat-markdown tbody tr:nth-child(even)"); + }); });