From 527dbcd2b3ac64febaa9850323f5d0f5fdba081c Mon Sep 17 00:00:00 2001 From: FND Date: Mon, 13 Jul 2026 18:03:10 +0200 Subject: [PATCH] fix: use Mermaid-compatible theme colors --- .../SKILL.test.ts | 45 +++++++++++++++++++ .../references/theme-override.md | 43 +++++++++++++++--- 2 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 apps/skills/extra/plannotator-visual-explainer/SKILL.test.ts diff --git a/apps/skills/extra/plannotator-visual-explainer/SKILL.test.ts b/apps/skills/extra/plannotator-visual-explainer/SKILL.test.ts new file mode 100644 index 000000000..2786a55b2 --- /dev/null +++ b/apps/skills/extra/plannotator-visual-explainer/SKILL.test.ts @@ -0,0 +1,45 @@ +/// +/// + +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const themeOverride = readFileSync( + join(import.meta.dir, "references/theme-override.md"), + "utf-8", +); +const mermaidStart = themeOverride.indexOf("## Mermaid theming"); +const mermaidEnd = themeOverride.indexOf("\n## ", mermaidStart + 1); +const mermaidSection = themeOverride.slice(mermaidStart, mermaidEnd); +const exampleStart = mermaidSection.indexOf("```javascript"); +const exampleEnd = mermaidSection.indexOf("```", exampleStart + 3); +const mermaidExample = mermaidSection.slice(exampleStart, exampleEnd); + +describe("plannotator-visual-explainer Mermaid theming", () => { + test("keeps the Mermaid theming section", () => { + expect(mermaidStart).toBeGreaterThan(-1); + expect(mermaidEnd).toBeGreaterThan(mermaidStart); + expect(exampleStart).toBeGreaterThan(-1); + expect(exampleEnd).toBeGreaterThan(exampleStart); + }); + + test("uses Mermaid-compatible literal colors", () => { + const literalColors = mermaidExample.match(/#[0-9a-f]{6}\b/gi) ?? []; + expect(mermaidExample).toContain("themeVariables"); + expect(literalColors.length).toBeGreaterThanOrEqual(10); + }); + + test("keeps CSS color processing outside Mermaid themeVariables", () => { + expect(mermaidSection).not.toMatch(/\boklch\(\s*[^)]/i); + expect(mermaidSection).not.toMatch(/\bvar\(\s*[^)]/i); + expect(mermaidSection).not.toMatch(/\bcolor-mix\(\s*[^)]/i); + }); + + test("preserves OKLCH for ordinary page CSS", () => { + expect(themeOverride).toMatch(/--background:\s+oklch\(/); + expect(themeOverride).toMatch( + /@media \(prefers-color-scheme: dark\)[\s\S]*--background:\s+oklch\(/, + ); + }); +}); diff --git a/apps/skills/extra/plannotator-visual-explainer/references/theme-override.md b/apps/skills/extra/plannotator-visual-explainer/references/theme-override.md index f2d74cf53..4652ad449 100644 --- a/apps/skills/extra/plannotator-visual-explainer/references/theme-override.md +++ b/apps/skills/extra/plannotator-visual-explainer/references/theme-override.md @@ -81,18 +81,47 @@ The `--font-display` (serif) is still used for headings to create visual contras ## Mermaid theming -When visual-explainer instructs you to set `themeVariables` in Mermaid config, use Plannotator tokens: +Mermaid processes `themeVariables` itself and derives additional colors from them. That color-processing boundary does not accept every color syntax that browsers accept in CSS. Use Mermaid-compatible literal hex colors here instead of copying the semantic CSS token declarations above. + +Do not pass OKLCH color functions, CSS custom-property references such as `var()`, or `color-mix()` values directly into `themeVariables`. This restriction applies only to Mermaid's color-processing boundary. Continue using Plannotator's OKLCH custom properties and other modern color functions for ordinary page CSS. + +Use the same light/dark state as the page, but keep both Mermaid palettes literal. With the host-theme opt-in, Plannotator synchronizes `color-scheme`; standalone documents fall back to the operating-system preference: ```javascript +const colorScheme = getComputedStyle(document.documentElement).colorScheme; +const isDark = colorScheme === 'dark' + || (colorScheme === 'normal' + && window.matchMedia('(prefers-color-scheme: dark)').matches); + +const mermaidThemeVariables = isDark + ? { + darkMode: true, + primaryColor: '#9a9dff', + primaryTextColor: '#070b14', + primaryBorderColor: '#343b45', + lineColor: '#9da5b2', + secondaryColor: '#1e242e', + secondaryTextColor: '#dadee5', + tertiaryColor: '#1e242e', + tertiaryTextColor: '#dadee5', + background: '#070b14', + } + : { + primaryColor: '#5537eb', + primaryTextColor: '#ffffff', + primaryBorderColor: '#d4d8de', + lineColor: '#414853', + secondaryColor: '#e1e5eb', + secondaryTextColor: '#414853', + tertiaryColor: '#e1e5eb', + tertiaryTextColor: '#414853', + background: '#f3f5f9', + }; + mermaid.initialize({ theme: 'base', themeVariables: { - primaryColor: 'oklch(0.50 0.25 280)', // --primary - primaryTextColor: 'oklch(1 0 0)', // --primary-foreground - primaryBorderColor: 'oklch(0.88 0.01 260)', // --border - lineColor: 'oklch(0.40 0.02 260)', // --muted-foreground - secondaryColor: 'oklch(0.92 0.01 260)', // --muted - tertiaryColor: 'oklch(0.92 0.01 260)', // --muted + ...mermaidThemeVariables, fontFamily: "'Inter', system-ui, sans-serif", fontSize: '14px', }