From 6c2081d34c68a9f8edc4a2e55ada46d04a992c3a Mon Sep 17 00:00:00 2001
From: Jimmy CrakCrn <128385879+jccidc@users.noreply.github.com>
Date: Fri, 10 Apr 2026 18:23:35 -0500
Subject: [PATCH 1/9] feat: theme switcher, background patterns, share button,
page breaks, print/PDF
Theme Switcher (11 presets):
- Teal Slate, Terracotta Sage, Editorial Rose, Deep Navy Gold, Amber Emerald
- Dracula, Nord, Catppuccin Mocha (warm mauve), Solarized Light, Rose Pine Dawn, GitHub Light
- Runtime CSS variable swaps + Google Fonts hot-reload
- Mermaid diagrams re-render with updated themeVariables on every switch
- Light/dark mode toggle with mode-aware color adjustments
Background Patterns (15 options):
- Dot Grid, Hex Grid (jimmycc.com SVG), Diagonal Lines, Cross Hatch
- Radial Glow, Dual Glow, Blueprint Grid, Noise Texture
- Honeycomb, Isometric, Topography, Parchment, Sketchbook, Graph Paper, Linen
- All mode-aware: stronger opacity in light mode, auto-reapply on theme change
Share Button:
- Copy HTML (strips chrome, bakes theme into inline styles)
- Download .html (same clean output as file)
- Save as PDF (bakes computed colors for print, toast with instructions)
Page Break System:
- Toolbar toggle enters page break mode
- Click between sections to place visual break markers
- Markers become break-before:page in print, hidden in output
- Click X to remove any marker
Print/PDF:
- @page margin:0 suppresses browser headers/footers
- print-color-adjust:exact on all elements preserves active theme
- Background colors baked as inline styles before print
Other:
- Windows start command in SKILL.md
- marketplace.json version sync (0.5.1 -> 0.6.3)
- share.sh checks Claude Code skill paths
- Accessibility: focus-visible, ARIA landmarks, contrast guidance
- Component borders upgraded to --border-bright for visibility
---
.claude-plugin/marketplace.json | 2 +-
CHANGELOG.md | 39 +
plugins/visual-explainer/SKILL.md | 1 +
.../references/css-patterns.md | 744 +++++-
plugins/visual-explainer/scripts/share.sh | 8 +-
.../templates/theme-switcher-demo.html | 2015 +++++++++++++++++
6 files changed, 2763 insertions(+), 46 deletions(-)
create mode 100644 plugins/visual-explainer/templates/theme-switcher-demo.html
diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index dc048ee..f7e33d6 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -13,7 +13,7 @@
"name": "visual-explainer",
"source": "./plugins/visual-explainer",
"description": "Generate beautiful HTML pages for diagrams, diff reviews, plan reviews, slides, and data tables",
- "version": "0.5.1",
+ "version": "0.6.3",
"author": {
"name": "nicobailon"
},
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73c5071..ebe58b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,44 @@
# Changelog
+## [0.7.0] - 2026-04-10
+
+### Theme Switcher
+- Runtime palette and font switching via dropdown — 8 curated presets: Teal Slate, Terracotta Sage, Editorial Rose, Deep Navy Gold, Amber Emerald, Dracula, Nord, Catppuccin Mocha
+- Each preset defines full light and dark palettes with all CSS custom properties (bg, surface, border, text, accent, node colors, status colors)
+- Google Fonts hot-reload via ` ` element swap — fonts change instantly without page refresh
+- Mermaid diagrams re-render automatically on theme change with updated `themeVariables`
+- Light/dark mode toggle button alongside preset dropdown
+- Random initial preset on page load for variety
+- New `theme-switcher-demo.html` template showcasing all components with live switching
+- Keyboard accessible: Escape closes dropdown, Tab navigation through options
+
+### Share Button
+- In-page share dropdown with three options: Copy HTML, Download .html, Save as PDF
+- Copy/download strips all interactive chrome (theme switcher, share button, background selector) and bakes current theme into the HTML — recipients see exactly what you see
+- Save as PDF opens browser print dialog with print-optimized CSS (light theme forced, chrome hidden, page breaks between sections)
+- Toast notification confirms each action
+
+### Background Switcher
+- Runtime background pattern selector with 9 options: Dot Grid, Fine Dots, Diagonal Lines, Cross Hatch, Radial Glow, Dual Glow, Blueprint Grid, Noise Texture, None
+- Patterns use CSS custom properties so they adapt automatically when the theme changes
+- Background choice persists through theme switches
+
+### Windows Support
+- Added Windows `start` command to browser-open instructions in SKILL.md
+
+### Print Styles
+- New `@media print` rules in `css-patterns.md`: force light colors, hide interactive chrome, page breaks between sections, expand collapsibles, show link URLs
+
+### Accessibility
+- Color contrast guidance (WCAG 2.1 AA ratios)
+- Focus-visible keyboard indicators
+- ARIA landmarks guidance (nav, main, article)
+- Screen reader considerations for Mermaid diagrams, status indicators, images, tables
+
+### Bug Fixes
+- Synced `marketplace.json` version from 0.5.1 to 0.6.3 (was stale)
+- Added Claude Code skill paths to `share.sh` (previously only checked Pi paths)
+
## [0.6.3] - 2026-03-09
### Documentation
diff --git a/plugins/visual-explainer/SKILL.md b/plugins/visual-explainer/SKILL.md
index a0e2b10..6c13055 100644
--- a/plugins/visual-explainer/SKILL.md
+++ b/plugins/visual-explainer/SKILL.md
@@ -196,6 +196,7 @@ Keep animations purposeful: entrance reveals, hover feedback, and user-initiated
**Open in browser:**
- macOS: `open ~/.agent/diagrams/filename.html`
- Linux: `xdg-open ~/.agent/diagrams/filename.html`
+- Windows: `start ~/.agent/diagrams/filename.html`
**Tell the user** the file path so they can re-open or share it.
diff --git a/plugins/visual-explainer/references/css-patterns.md b/plugins/visual-explainer/references/css-patterns.md
index d20e004..d8e65e8 100644
--- a/plugins/visual-explainer/references/css-patterns.md
+++ b/plugins/visual-explainer/references/css-patterns.md
@@ -1633,69 +1633,605 @@ For warnings, tips, notes, and key takeaways.
}
```
-### Theme Toggle
+### Theme Switcher
-Use `data-theme` attribute for user-controllable light/dark modes. Random initial theme adds variety.
+A dropdown that swaps the full visual identity at runtime: color palette, fonts, and light/dark mode. Every generated page should include this. The switcher uses `data-theme` attributes on `` and hot-reloads Google Fonts via link element replacement.
-```css
-:root, [data-theme="light"] {
- --bg: #fafaf9;
- --surface: #ffffff;
- --text: #1c1917;
- --text-dim: #78716c;
- --border: #e7e5e4;
- --accent: #0d9488;
-}
+**How it works:** Each preset defines CSS custom properties (colors) and font families. Switching a preset updates `--font-body`, `--font-mono`, and all color variables via a `data-theme` attribute. Google Fonts are loaded on demand by swapping the ` ` element's `href`. Mermaid diagrams are re-rendered with updated `themeVariables` so they stay in sync.
-[data-theme="dark"] {
- --bg: #0c0a09;
- --surface: #1c1917;
- --text: #fafaf9;
- --text-dim: #a8a29e;
- --border: #292524;
- --accent: #14b8a6;
-}
-```
+#### Preset Definitions (JavaScript)
+
+Each preset has a `name`, `fonts` (Google Fonts families), `vars` (CSS custom properties for both light and dark), and `fontFamily` values.
```javascript
-// Random initial theme
-const themes = ['light', 'dark'];
-document.documentElement.setAttribute('data-theme', themes[Math.floor(Math.random() * 2)]);
+const THEME_PRESETS = [
+ {
+ name: 'Teal Slate',
+ fonts: 'Outfit:wght@400;500;600;700&family=Space+Mono:wght@400;700',
+ fontBody: "'Outfit', system-ui, sans-serif",
+ fontMono: "'Space Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#f8f9fa', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.08)', '--border-bright': 'rgba(0,0,0,0.15)',
+ '--text': '#1a1a2e', '--text-dim': '#6b7280',
+ '--accent': '#0891b2', '--accent-dim': 'rgba(8,145,178,0.1)',
+ '--node-a': '#0891b2', '--node-a-dim': 'rgba(8,145,178,0.1)',
+ '--node-b': '#059669', '--node-b-dim': 'rgba(5,150,105,0.1)',
+ '--node-c': '#d97706', '--node-c-dim': 'rgba(217,119,6,0.1)',
+ '--green': '#059669', '--green-dim': 'rgba(5,150,105,0.1)',
+ '--red': '#ef4444', '--red-dim': 'rgba(239,68,68,0.1)',
+ '--orange': '#d97706', '--orange-dim': 'rgba(217,119,6,0.1)',
+ },
+ dark: {
+ '--bg': '#0d1117', '--surface': '#161b22', '--surface-elevated': '#1c2333',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.12)',
+ '--text': '#e6edf3', '--text-dim': '#8b949e',
+ '--accent': '#22d3ee', '--accent-dim': 'rgba(34,211,238,0.12)',
+ '--node-a': '#22d3ee', '--node-a-dim': 'rgba(34,211,238,0.12)',
+ '--node-b': '#34d399', '--node-b-dim': 'rgba(52,211,153,0.12)',
+ '--node-c': '#fbbf24', '--node-c-dim': 'rgba(251,191,36,0.12)',
+ '--green': '#34d399', '--green-dim': 'rgba(52,211,153,0.12)',
+ '--red': '#f87171', '--red-dim': 'rgba(248,113,113,0.12)',
+ '--orange': '#fbbf24', '--orange-dim': 'rgba(251,191,36,0.12)',
+ },
+ },
+ {
+ name: 'Terracotta Sage',
+ fonts: 'DM+Sans:wght@400;500;600;700&family=Fira+Code:wght@400;500',
+ fontBody: "'DM Sans', system-ui, sans-serif",
+ fontMono: "'Fira Code', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#faf7f5', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.07)', '--border-bright': 'rgba(0,0,0,0.12)',
+ '--text': '#292524', '--text-dim': '#78716c',
+ '--accent': '#c2410c', '--accent-dim': 'rgba(194,65,12,0.08)',
+ '--node-a': '#c2410c', '--node-a-dim': 'rgba(194,65,12,0.08)',
+ '--node-b': '#65a30d', '--node-b-dim': 'rgba(101,163,13,0.08)',
+ '--node-c': '#a16207', '--node-c-dim': 'rgba(161,98,7,0.08)',
+ '--green': '#65a30d', '--green-dim': 'rgba(101,163,13,0.08)',
+ '--red': '#dc2626', '--red-dim': 'rgba(220,38,38,0.08)',
+ '--orange': '#ea580c', '--orange-dim': 'rgba(234,88,12,0.08)',
+ },
+ dark: {
+ '--bg': '#1c1917', '--surface': '#292524', '--surface-elevated': '#44403c',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#fafaf9', '--text-dim': '#a8a29e',
+ '--accent': '#fb923c', '--accent-dim': 'rgba(251,146,60,0.12)',
+ '--node-a': '#fb923c', '--node-a-dim': 'rgba(251,146,60,0.12)',
+ '--node-b': '#a3e635', '--node-b-dim': 'rgba(163,230,53,0.12)',
+ '--node-c': '#fbbf24', '--node-c-dim': 'rgba(251,191,36,0.12)',
+ '--green': '#a3e635', '--green-dim': 'rgba(163,230,53,0.12)',
+ '--red': '#f87171', '--red-dim': 'rgba(248,113,113,0.12)',
+ '--orange': '#fbbf24', '--orange-dim': 'rgba(251,191,36,0.12)',
+ },
+ },
+ {
+ name: 'Editorial Rose',
+ fonts: 'Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500',
+ fontBody: "'Instrument Serif', Georgia, serif",
+ fontMono: "'JetBrains Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#fafaf9', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.08)', '--border-bright': 'rgba(0,0,0,0.14)',
+ '--text': '#1c1917', '--text-dim': '#78716c',
+ '--accent': '#be123c', '--accent-dim': 'rgba(190,18,60,0.08)',
+ '--node-a': '#be123c', '--node-a-dim': 'rgba(190,18,60,0.08)',
+ '--node-b': '#0f766e', '--node-b-dim': 'rgba(15,118,110,0.08)',
+ '--node-c': '#92400e', '--node-c-dim': 'rgba(146,64,14,0.08)',
+ '--green': '#059669', '--green-dim': 'rgba(5,150,105,0.08)',
+ '--red': '#be123c', '--red-dim': 'rgba(190,18,60,0.08)',
+ '--orange': '#d97706', '--orange-dim': 'rgba(217,119,6,0.08)',
+ },
+ dark: {
+ '--bg': '#0f0d0d', '--surface': '#1a1717', '--surface-elevated': '#262121',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#f5f0ed', '--text-dim': '#a39e9b',
+ '--accent': '#fb7185', '--accent-dim': 'rgba(251,113,133,0.12)',
+ '--node-a': '#fb7185', '--node-a-dim': 'rgba(251,113,133,0.12)',
+ '--node-b': '#5eead4', '--node-b-dim': 'rgba(94,234,212,0.12)',
+ '--node-c': '#fbbf24', '--node-c-dim': 'rgba(251,191,36,0.12)',
+ '--green': '#34d399', '--green-dim': 'rgba(52,211,153,0.12)',
+ '--red': '#fb7185', '--red-dim': 'rgba(251,113,133,0.12)',
+ '--orange': '#fbbf24', '--orange-dim': 'rgba(251,191,36,0.12)',
+ },
+ },
+ {
+ name: 'Deep Navy Gold',
+ fonts: 'IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500',
+ fontBody: "'IBM Plex Sans', system-ui, sans-serif",
+ fontMono: "'IBM Plex Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#f5f5f4', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.08)', '--border-bright': 'rgba(0,0,0,0.15)',
+ '--text': '#1e3a5f', '--text-dim': '#64748b',
+ '--accent': '#1e3a5f', '--accent-dim': 'rgba(30,58,95,0.08)',
+ '--node-a': '#1e3a5f', '--node-a-dim': 'rgba(30,58,95,0.08)',
+ '--node-b': '#d4a73a', '--node-b-dim': 'rgba(212,167,58,0.08)',
+ '--node-c': '#7c3aed', '--node-c-dim': 'rgba(124,58,237,0.08)',
+ '--green': '#059669', '--green-dim': 'rgba(5,150,105,0.08)',
+ '--red': '#dc2626', '--red-dim': 'rgba(220,38,38,0.08)',
+ '--orange': '#d4a73a', '--orange-dim': 'rgba(212,167,58,0.08)',
+ },
+ dark: {
+ '--bg': '#0a1628', '--surface': '#112240', '--surface-elevated': '#1a3050',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#ccd6f6', '--text-dim': '#8892b0',
+ '--accent': '#d4a73a', '--accent-dim': 'rgba(212,167,58,0.12)',
+ '--node-a': '#64b5f6', '--node-a-dim': 'rgba(100,181,246,0.12)',
+ '--node-b': '#d4a73a', '--node-b-dim': 'rgba(212,167,58,0.12)',
+ '--node-c': '#a78bfa', '--node-c-dim': 'rgba(167,139,250,0.12)',
+ '--green': '#34d399', '--green-dim': 'rgba(52,211,153,0.12)',
+ '--red': '#f87171', '--red-dim': 'rgba(248,113,113,0.12)',
+ '--orange': '#d4a73a', '--orange-dim': 'rgba(212,167,58,0.12)',
+ },
+ },
+ {
+ name: 'Amber Emerald',
+ fonts: 'Bricolage+Grotesque:wght@400;500;600;700&family=Fragment+Mono',
+ fontBody: "'Bricolage Grotesque', system-ui, sans-serif",
+ fontMono: "'Fragment Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#fafaf9', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.07)', '--border-bright': 'rgba(0,0,0,0.12)',
+ '--text': '#1c1917', '--text-dim': '#78716c',
+ '--accent': '#d97706', '--accent-dim': 'rgba(217,119,6,0.08)',
+ '--node-a': '#d97706', '--node-a-dim': 'rgba(217,119,6,0.08)',
+ '--node-b': '#059669', '--node-b-dim': 'rgba(5,150,105,0.08)',
+ '--node-c': '#0284c7', '--node-c-dim': 'rgba(2,132,199,0.08)',
+ '--green': '#059669', '--green-dim': 'rgba(5,150,105,0.08)',
+ '--red': '#dc2626', '--red-dim': 'rgba(220,38,38,0.08)',
+ '--orange': '#d97706', '--orange-dim': 'rgba(217,119,6,0.08)',
+ },
+ dark: {
+ '--bg': '#0c0a09', '--surface': '#1c1917', '--surface-elevated': '#292524',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#fafaf9', '--text-dim': '#a8a29e',
+ '--accent': '#fbbf24', '--accent-dim': 'rgba(251,191,36,0.12)',
+ '--node-a': '#fbbf24', '--node-a-dim': 'rgba(251,191,36,0.12)',
+ '--node-b': '#34d399', '--node-b-dim': 'rgba(52,211,153,0.12)',
+ '--node-c': '#38bdf8', '--node-c-dim': 'rgba(56,189,248,0.12)',
+ '--green': '#34d399', '--green-dim': 'rgba(52,211,153,0.12)',
+ '--red': '#f87171', '--red-dim': 'rgba(248,113,113,0.12)',
+ '--orange': '#fbbf24', '--orange-dim': 'rgba(251,191,36,0.12)',
+ },
+ },
+ {
+ name: 'Dracula',
+ fonts: 'Plus+Jakarta+Sans:wght@400;500;600;700&family=Azeret+Mono:wght@400;500',
+ fontBody: "'Plus Jakarta Sans', system-ui, sans-serif",
+ fontMono: "'Azeret Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#f8f8f2', '--surface': '#ffffff', '--surface-elevated': '#ffffff',
+ '--border': 'rgba(0,0,0,0.08)', '--border-bright': 'rgba(0,0,0,0.14)',
+ '--text': '#282a36', '--text-dim': '#6272a4',
+ '--accent': '#bd93f9', '--accent-dim': 'rgba(189,147,249,0.1)',
+ '--node-a': '#bd93f9', '--node-a-dim': 'rgba(189,147,249,0.1)',
+ '--node-b': '#50fa7b', '--node-b-dim': 'rgba(80,250,123,0.1)',
+ '--node-c': '#ffb86c', '--node-c-dim': 'rgba(255,184,108,0.1)',
+ '--green': '#50fa7b', '--green-dim': 'rgba(80,250,123,0.1)',
+ '--red': '#ff5555', '--red-dim': 'rgba(255,85,85,0.1)',
+ '--orange': '#ffb86c', '--orange-dim': 'rgba(255,184,108,0.1)',
+ },
+ dark: {
+ '--bg': '#282a36', '--surface': '#343746', '--surface-elevated': '#44475a',
+ '--border': 'rgba(255,255,255,0.08)', '--border-bright': 'rgba(255,255,255,0.14)',
+ '--text': '#f8f8f2', '--text-dim': '#6272a4',
+ '--accent': '#bd93f9', '--accent-dim': 'rgba(189,147,249,0.15)',
+ '--node-a': '#bd93f9', '--node-a-dim': 'rgba(189,147,249,0.15)',
+ '--node-b': '#50fa7b', '--node-b-dim': 'rgba(80,250,123,0.15)',
+ '--node-c': '#ffb86c', '--node-c-dim': 'rgba(255,184,108,0.15)',
+ '--green': '#50fa7b', '--green-dim': 'rgba(80,250,123,0.15)',
+ '--red': '#ff5555', '--red-dim': 'rgba(255,85,85,0.15)',
+ '--orange': '#ffb86c', '--orange-dim': 'rgba(255,184,108,0.15)',
+ },
+ },
+ {
+ name: 'Nord',
+ fonts: 'Sora:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500',
+ fontBody: "'Sora', system-ui, sans-serif",
+ fontMono: "'IBM Plex Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#eceff4', '--surface': '#e5e9f0', '--surface-elevated': '#d8dee9',
+ '--border': 'rgba(0,0,0,0.06)', '--border-bright': 'rgba(0,0,0,0.1)',
+ '--text': '#2e3440', '--text-dim': '#4c566a',
+ '--accent': '#5e81ac', '--accent-dim': 'rgba(94,129,172,0.1)',
+ '--node-a': '#5e81ac', '--node-a-dim': 'rgba(94,129,172,0.1)',
+ '--node-b': '#a3be8c', '--node-b-dim': 'rgba(163,190,140,0.1)',
+ '--node-c': '#ebcb8b', '--node-c-dim': 'rgba(235,203,139,0.1)',
+ '--green': '#a3be8c', '--green-dim': 'rgba(163,190,140,0.1)',
+ '--red': '#bf616a', '--red-dim': 'rgba(191,97,106,0.1)',
+ '--orange': '#d08770', '--orange-dim': 'rgba(208,135,112,0.1)',
+ },
+ dark: {
+ '--bg': '#2e3440', '--surface': '#3b4252', '--surface-elevated': '#434c5e',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#eceff4', '--text-dim': '#d8dee9',
+ '--accent': '#88c0d0', '--accent-dim': 'rgba(136,192,208,0.12)',
+ '--node-a': '#88c0d0', '--node-a-dim': 'rgba(136,192,208,0.12)',
+ '--node-b': '#a3be8c', '--node-b-dim': 'rgba(163,190,140,0.12)',
+ '--node-c': '#ebcb8b', '--node-c-dim': 'rgba(235,203,139,0.12)',
+ '--green': '#a3be8c', '--green-dim': 'rgba(163,190,140,0.12)',
+ '--red': '#bf616a', '--red-dim': 'rgba(191,97,106,0.12)',
+ '--orange': '#d08770', '--orange-dim': 'rgba(208,135,112,0.12)',
+ },
+ },
+ {
+ name: 'Catppuccin Mocha',
+ fonts: 'Red+Hat+Display:wght@400;500;600;700&family=Red+Hat+Mono:wght@400;500',
+ fontBody: "'Red Hat Display', system-ui, sans-serif",
+ fontMono: "'Red Hat Mono', 'SF Mono', Consolas, monospace",
+ light: {
+ '--bg': '#eff1f5', '--surface': '#e6e9ef', '--surface-elevated': '#dce0e8',
+ '--border': 'rgba(0,0,0,0.06)', '--border-bright': 'rgba(0,0,0,0.1)',
+ '--text': '#4c4f69', '--text-dim': '#6c6f85',
+ '--accent': '#1e66f5', '--accent-dim': 'rgba(30,102,245,0.1)',
+ '--node-a': '#1e66f5', '--node-a-dim': 'rgba(30,102,245,0.1)',
+ '--node-b': '#40a02b', '--node-b-dim': 'rgba(64,160,43,0.1)',
+ '--node-c': '#df8e1d', '--node-c-dim': 'rgba(223,142,29,0.1)',
+ '--green': '#40a02b', '--green-dim': 'rgba(64,160,43,0.1)',
+ '--red': '#d20f39', '--red-dim': 'rgba(210,15,57,0.1)',
+ '--orange': '#fe640b', '--orange-dim': 'rgba(254,100,11,0.1)',
+ },
+ dark: {
+ '--bg': '#1e1e2e', '--surface': '#313244', '--surface-elevated': '#45475a',
+ '--border': 'rgba(255,255,255,0.06)', '--border-bright': 'rgba(255,255,255,0.1)',
+ '--text': '#cdd6f4', '--text-dim': '#a6adc8',
+ '--accent': '#89b4fa', '--accent-dim': 'rgba(137,180,250,0.12)',
+ '--node-a': '#89b4fa', '--node-a-dim': 'rgba(137,180,250,0.12)',
+ '--node-b': '#a6e3a1', '--node-b-dim': 'rgba(166,227,161,0.12)',
+ '--node-c': '#f9e2af', '--node-c-dim': 'rgba(249,226,175,0.12)',
+ '--green': '#a6e3a1', '--green-dim': 'rgba(166,227,161,0.12)',
+ '--red': '#f38ba8', '--red-dim': 'rgba(243,139,168,0.12)',
+ '--orange': '#fab387', '--orange-dim': 'rgba(250,179,135,0.12)',
+ },
+ },
+];
+```
+
+#### Theme Engine (JavaScript)
+
+Place this after the preset definitions. Handles font loading, CSS variable application, mode toggling, and Mermaid re-rendering.
-// Toggle function
-function toggleTheme() {
- const current = document.documentElement.getAttribute('data-theme');
- document.documentElement.setAttribute('data-theme', current === 'light' ? 'dark' : 'light');
-}
+```javascript
+const ThemeSwitcher = (function() {
+ let currentPresetIndex = 0;
+ let currentMode = 'dark';
+ let fontLink = null;
+
+ function init() {
+ // Pick a random preset on first load
+ currentPresetIndex = Math.floor(Math.random() * THEME_PRESETS.length);
+ currentMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+ fontLink = document.getElementById('theme-fonts');
+ apply();
+ }
+
+ function apply() {
+ const preset = THEME_PRESETS[currentPresetIndex];
+ const vars = preset[currentMode];
+ const root = document.documentElement;
+
+ // Apply CSS custom properties
+ root.setAttribute('data-theme', currentMode);
+ Object.entries(vars).forEach(([k, v]) => root.style.setProperty(k, v));
+ root.style.setProperty('--font-body', preset.fontBody);
+ root.style.setProperty('--font-mono', preset.fontMono);
+
+ // Hot-reload Google Fonts
+ if (fontLink) {
+ const href = 'https://fonts.googleapis.com/css2?family='
+ + preset.fonts + '&display=swap';
+ if (fontLink.getAttribute('href') !== href) {
+ fontLink.setAttribute('href', href);
+ }
+ }
+
+ // Update dropdown display if it exists
+ const label = document.querySelector('.theme-switcher__label');
+ if (label) label.textContent = preset.name;
+ const modeBtn = document.querySelector('.theme-switcher__mode');
+ if (modeBtn) modeBtn.textContent = currentMode === 'dark' ? '\u2600' : '\u263E';
+
+ // Re-render Mermaid diagrams with updated theme colors
+ rerenderMermaid(vars);
+ }
+
+ function setPreset(index) {
+ currentPresetIndex = index;
+ apply();
+ }
+
+ function toggleMode() {
+ currentMode = currentMode === 'dark' ? 'light' : 'dark';
+ apply();
+ }
+
+ function rerenderMermaid(vars) {
+ if (typeof mermaid === 'undefined') return;
+ var shells = document.querySelectorAll('.diagram-shell');
+ if (!shells.length) return;
+
+ // Build themeVariables from current palette
+ var isDark = currentMode === 'dark';
+ mermaid.initialize({
+ startOnLoad: false,
+ theme: 'base',
+ themeVariables: {
+ primaryColor: vars['--node-a-dim'] || vars['--accent-dim'],
+ primaryBorderColor: vars['--node-a'] || vars['--accent'],
+ primaryTextColor: vars['--text'],
+ secondaryColor: vars['--node-b-dim'] || vars['--surface'],
+ secondaryBorderColor: vars['--node-b'] || vars['--border-bright'],
+ secondaryTextColor: vars['--text'],
+ tertiaryColor: vars['--node-c-dim'] || vars['--surface-elevated'],
+ tertiaryBorderColor: vars['--node-c'] || vars['--border-bright'],
+ tertiaryTextColor: vars['--text'],
+ lineColor: vars['--text-dim'],
+ fontSize: '16px',
+ fontFamily: 'var(--font-body)',
+ noteBkgColor: vars['--surface-elevated'] || vars['--surface'],
+ noteTextColor: vars['--text'],
+ noteBorderColor: vars['--border-bright'],
+ },
+ });
+
+ // Re-render each diagram from its source
+ shells.forEach(function(shell) {
+ var source = shell.querySelector('.diagram-source');
+ var canvas = shell.querySelector('.mermaid-canvas');
+ if (!source || !canvas) return;
+ var code = source.textContent.trim();
+ if (!code) return;
+
+ var id = 'diagram-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);
+ mermaid.render(id, code).then(function(result) {
+ canvas.innerHTML = result.svg;
+ // Re-run the diagram's fit logic if initDiagram exposed it
+ if (typeof shell._fitDiagram === 'function') shell._fitDiagram();
+ }).catch(function(err) {
+ console.warn('Theme switch: Mermaid re-render failed', err);
+ });
+ });
+ }
+
+ return { init: init, setPreset: setPreset, toggleMode: toggleMode };
+})();
```
+#### Dropdown HTML
+
+Place this in the page `
`, typically fixed in the top-right corner. The font ` ` needs `id="theme-fonts"` so the engine can swap it.
+
```html
-
-
-
-
-
-
-
-
+
+
+
+
+
+
☾
+
+
+ Teal Slate
+ ▾
+
+
+
+
```
+Build the menu dynamically and wire the trigger:
+
+```javascript
+document.addEventListener('DOMContentLoaded', function() {
+ ThemeSwitcher.init();
+
+ // Populate dropdown menu
+ var menu = document.querySelector('.theme-switcher__menu');
+ var trigger = document.querySelector('.theme-switcher__trigger');
+ if (!menu || !trigger) return;
+
+ THEME_PRESETS.forEach(function(preset, i) {
+ var li = document.createElement('li');
+ li.setAttribute('role', 'option');
+ li.textContent = preset.name;
+ li.onclick = function() {
+ ThemeSwitcher.setPreset(i);
+ menu.classList.remove('open');
+ trigger.setAttribute('aria-expanded', 'false');
+ };
+ menu.appendChild(li);
+ });
+
+ // Toggle dropdown
+ trigger.onclick = function() {
+ var open = menu.classList.toggle('open');
+ trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
+ };
+
+ // Close on outside click
+ document.addEventListener('click', function(e) {
+ if (!e.target.closest('.theme-switcher__dropdown')) {
+ menu.classList.remove('open');
+ trigger.setAttribute('aria-expanded', 'false');
+ }
+ });
+
+ // Keyboard navigation
+ trigger.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ menu.classList.remove('open');
+ trigger.setAttribute('aria-expanded', 'false');
+ }
+ });
+});
+```
+
+#### Dropdown CSS
+
```css
-.theme-toggle {
+.theme-switcher {
position: fixed;
- top: 20px;
- right: 20px;
+ top: 16px;
+ right: 16px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ z-index: 200;
+ font-family: var(--font-mono);
+}
+
+.theme-switcher__mode {
+ width: 36px;
+ height: 36px;
+ border: 1px solid var(--border);
+ background: var(--surface);
+ color: var(--text-dim);
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background 0.15s, color 0.15s, border-color 0.15s;
+}
+
+.theme-switcher__mode:hover {
+ background: var(--surface-elevated);
+ color: var(--text);
+}
+
+.theme-switcher__dropdown {
+ position: relative;
+}
+
+.theme-switcher__trigger {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 8px 12px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ color: var(--text);
+ font-family: var(--font-mono);
+ font-size: 12px;
+ cursor: pointer;
+ white-space: nowrap;
+ transition: background 0.15s, border-color 0.15s;
+}
+
+.theme-switcher__trigger:hover {
+ background: var(--surface-elevated);
+ border-color: var(--border-bright);
+}
+
+.theme-switcher__caret {
+ font-size: 10px;
+ color: var(--text-dim);
+ transition: transform 0.15s;
+}
+
+.theme-switcher__menu.open + .theme-switcher__trigger .theme-switcher__caret,
+.theme-switcher__trigger[aria-expanded="true"] .theme-switcher__caret {
+ transform: rotate(180deg);
+}
+
+.theme-switcher__menu {
+ display: none;
+ position: absolute;
+ top: calc(100% + 4px);
+ right: 0;
+ min-width: 180px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
- padding: 8px;
+ padding: 4px;
+ margin: 0;
+ list-style: none;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
+ overflow: hidden;
+ transition: background 0.15s, border-color 0.15s;
+}
+
+.theme-switcher__menu.open {
+ display: block;
+}
+
+.theme-switcher__menu li {
+ padding: 8px 12px;
+ font-size: 12px;
+ color: var(--text);
cursor: pointer;
- z-index: 100;
+ border-radius: 5px;
+ transition: background 0.1s;
+}
+
+.theme-switcher__menu li:hover {
+ background: var(--accent-dim);
+}
+
+/* Responsive: collapse label on narrow viewports */
+@media (max-width: 600px) {
+ .theme-switcher__label { display: none; }
+ .theme-switcher__trigger { padding: 8px; }
+}
+
+@media print {
+ .theme-switcher { display: none; }
}
-[data-theme="light"] .theme-toggle__moon { display: none; }
-[data-theme="dark"] .theme-toggle__sun { display: none; }
```
+### Share Button
+
+Add a share button to the theme switcher bar. It provides three options: copy the page HTML to clipboard (with theme baked in, chrome stripped), download as .html file, or save as PDF via the browser's print dialog.
+
+The `SharePage.getCleanHTML()` function clones the document, removes the `.theme-switcher` element (which contains all interactive chrome: theme dropdown, background dropdown, share button, mode toggle), bakes the current CSS variable values into `:root`, and returns clean standalone HTML. Recipients see the exact theme the sharer chose, with no interactive controls.
+
+```html
+
+
+```
+
+See `templates/theme-switcher-demo.html` for the full CSS (identical to `.theme-switcher__menu` styling), JS module (`SharePage` IIFE), and toast notification pattern.
+
+### Background Switcher
+
+A dropdown for switching page background patterns at runtime. Lives in the `.theme-switcher` bar alongside the theme and share dropdowns.
+
+**Available patterns:** Dot Grid, Fine Dots, Diagonal Lines, Cross Hatch, Radial Glow, Dual Glow, Blueprint Grid, Noise Texture, None.
+
+Each pattern applies a `background-image` and `background-size` to ``. Patterns use CSS custom properties (`var(--border)`, `var(--accent-dim)`, etc.) so they automatically adapt when the theme changes.
+
+```html
+
+
+
+
+
+
+
+
+
+
+```
+
+The menu is populated dynamically from a `BG_PATTERNS` array. See `templates/theme-switcher-demo.html` for the full pattern definitions and CSS.
+
### Prose Anti-Patterns
Avoid these in reading-first content:
@@ -1811,3 +2347,125 @@ Small image floated beside a section. Use when the illustration supports but doe
```html
```
+
+## Print Styles
+
+Include these rules in every page so diagrams print/PDF cleanly. The active theme is preserved — do NOT force light colors, because users may want to share a dark-themed PDF. The `@page { margin: 0 }` rule suppresses browser-injected headers and footers (date, URL, page title) that ruin presentation output.
+
+```css
+/* Suppress browser headers/footers (date, URL, title) */
+@page {
+ margin: 0;
+ size: auto;
+}
+
+@media print {
+ /* Preserve the active theme colors — do NOT override CSS variables.
+ The user chose a theme; the PDF should match it exactly. */
+ body {
+ -webkit-print-color-adjust: exact !important;
+ print-color-adjust: exact !important;
+ padding: 40px !important;
+ }
+
+ /* Hide interactive elements */
+ .theme-switcher,
+ .zoom-controls,
+ .diagram-shell__hint,
+ .share-toast,
+ .toc,
+ .deck-dots,
+ .deck-progress,
+ .deck-counter,
+ .deck-hints {
+ display: none !important;
+ }
+
+ /* Remove animations */
+ *, *::before, *::after {
+ animation: none !important;
+ transition: none !important;
+ }
+
+ /* Keep components together across page breaks */
+ .ve-card, .ve-card--elevated, .ve-card--hero,
+ .kpi-card, .table-wrap, .mermaid-wrap,
+ .code-block, .code-file, .kpi-row {
+ break-inside: avoid;
+ }
+
+ /* Expand all collapsible sections */
+ details.collapsible[open] > * {
+ display: block !important;
+ }
+
+ /* Mermaid diagrams: ensure visible without zoom */
+ .mermaid-wrap {
+ overflow: visible !important;
+ min-height: auto !important;
+ }
+ .mermaid-viewport {
+ overflow: visible !important;
+ position: static !important;
+ }
+ .mermaid-canvas {
+ position: static !important;
+ }
+}
+```
+
+**Note on browser print dialogs:** `@page { margin: 0 }` removes the header/footer area in most browsers (Chrome, Edge, Firefox). If a user still sees headers/footers, they need to uncheck "Headers and footers" in the print dialog — CSS can remove the margin but some browsers require that checkbox too.
+
+## Accessibility
+
+Minimum requirements for every generated page. These complement `prefers-reduced-motion` (already covered in Animations) and `aria-label` on interactive elements.
+
+### Color Contrast
+
+All text must meet WCAG 2.1 AA contrast ratios: 4.5:1 for body text, 3:1 for large text (18px+ or 14px+ bold). The curated palettes in the Theme Switcher presets are designed to pass these thresholds, but verify when creating custom palettes.
+
+**Common failures to watch:**
+- `--text-dim` on `--surface` in light mode (often too faint)
+- Status badge text on colored backgrounds (`.status--match`, `.status--gap`)
+- Edge labels in Mermaid diagrams (override via `.mermaid .edgeLabel` CSS)
+- Accent-colored text on accent-dim backgrounds
+
+### Keyboard Navigation
+
+```css
+/* Visible focus indicator for keyboard users */
+:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+/* Don't show focus ring on mouse clicks */
+:focus:not(:focus-visible) {
+ outline: none;
+}
+```
+
+Interactive elements (buttons, links, dropdown triggers) must be reachable via Tab and activatable via Enter/Space. The theme switcher dropdown should close on Escape.
+
+### ARIA Landmarks
+
+Use semantic HTML to create an accessible document structure:
+
+```html
+
+ ...
+
+
+ Page Title
+
+
+
+
+```
+
+### Screen Reader Considerations
+
+- Every Mermaid diagram should have a text summary in a `` or `` adjacent to the `.diagram-shell`. Screen readers can't parse SVG graph structures.
+- Status indicators (`.status--match`, `.status--gap`) should include text, not just color. The existing patterns already do this -- don't remove the text label.
+- Images must have descriptive `alt` text. Base64-embedded images from surf-cli should describe the concept, not "AI-generated illustration."
+- Data tables need proper ``/` ` structure and `scope="col"` on header cells for screen reader table navigation.
diff --git a/plugins/visual-explainer/scripts/share.sh b/plugins/visual-explainer/scripts/share.sh
index 37cf407..c747ea9 100755
--- a/plugins/visual-explainer/scripts/share.sh
+++ b/plugins/visual-explainer/scripts/share.sh
@@ -24,9 +24,13 @@ if [ ! -f "$HTML_FILE" ]; then
exit 1
fi
-# Find vercel-deploy skill
+# Find vercel-deploy skill (check Pi, Claude Code, and mounted skill paths)
VERCEL_SCRIPT=""
-for dir in ~/.pi/agent/skills/vercel-deploy/scripts /mnt/skills/user/vercel-deploy/scripts; do
+for dir in \
+ ~/.pi/agent/skills/vercel-deploy/scripts \
+ ~/.claude/plugins/vercel-deploy/scripts \
+ ~/.claude/skills/vercel-deploy/scripts \
+ /mnt/skills/user/vercel-deploy/scripts; do
if [ -f "$dir/deploy.sh" ]; then
VERCEL_SCRIPT="$dir/deploy.sh"
break
diff --git a/plugins/visual-explainer/templates/theme-switcher-demo.html b/plugins/visual-explainer/templates/theme-switcher-demo.html
new file mode 100644
index 0000000..e0f5a58
--- /dev/null
+++ b/plugins/visual-explainer/templates/theme-switcher-demo.html
@@ -0,0 +1,2015 @@
+
+
+
+
+
+ Theme Switcher Demo — Visual Explainer
+
+
+
+
+
+
+
+
+
+
☾
+
+
+ Loading...
+ ▾
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Theme Switcher Demo
+
visual-explainer component showcase — swap palettes and fonts live
+
+
+
KPI Dashboard
+
+
+
11
+
Theme Presets
+
+3 new
+
+
+
+
2
+
Color Modes
+
light + dark
+
+
+
+
+
+
Card Components
+
+
+
Primary Accent
+
Elevated card with left accent border. Uses --node-a color.
+
+
+
Secondary Accent
+
Each preset defines three node colors for visual variety across cards and diagram elements.
+
+
+
Tertiary Accent
+
Tags: primary secondary tertiary
+
+
+
+
+
+
Hero Card
+
Hero cards use a subtle accent-tinted background to draw the eye. Use for executive summaries, key findings, or primary metrics. The border picks up the accent color at 50% mix.
+
+
+
+
Status Indicators
+
+ Match
+ Gap
+ Warning
+ Info
+
+
+
+
Diff Panels
+
+
+
+
+
Basic light/dark toggle with prefers-color-scheme media query. Single palette. No font switching. Mermaid stays stale on theme change.
+
+
+
Full theme switcher dropdown with 8 curated presets. Live CSS variable swaps, Google Fonts hot-reload, and Mermaid re-rendering on every palette change.
+
+
+
+
+
Data Table
+
+
+
+
Code Block
+
// Theme switching is one function call
+ThemeSwitcher.setPreset(2); // Editorial Rose
+ThemeSwitcher.toggleMode(); // light <-> dark
+
+// Mermaid re-renders automatically
+// Google Fonts hot-reload via link swap
+// All CSS variables update in place
+
+
+
Collapsible Section
+
+ Implementation Details (click to expand)
+
+
The theme engine uses a closure pattern (ThemeSwitcher IIFE) with three public methods: init(), setPreset(index), and toggleMode().
+
Mermaid re-rendering works by reading the original source from <script type="text/plain" class="diagram-source"> elements, calling mermaid.render() with updated themeVariables, and replacing the SVG in the canvas.
+
+
+
+
+
+
How to test
+
Click the dropdown in the top-right corner to switch between all 8 presets. Click the moon/sun button to toggle light/dark mode. Watch fonts, colors, badges, cards, table, diff panels, and the Mermaid diagram all update in sync.
+
+
+
+
Mermaid Diagram (re-renders on theme change)
+
+
+ Theme switch re-renders this diagram with updated colors.
+
+
+
+ +
+ −
+ ↺
+ 100%
+
+
+
+
+
+
+
+
+
+
+
+
+
From 30213a529d9882caa56438202394cc12a6d221d2 Mon Sep 17 00:00:00 2001
From: Jimmy CrakCrn <128385879+jccidc@users.noreply.github.com>
Date: Fri, 10 Apr 2026 20:32:54 -0500
Subject: [PATCH 2/9] =?UTF-8?q?fix:=20remove=20page=20breaks,=20PDF=20save?=
=?UTF-8?q?,=20print=20preview=20=E2=80=94=20CSS=20print=20layout=20unreli?=
=?UTF-8?q?able?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Page break markers, print preview overlay, and Save as PDF removed.
CSS print page breaks are a black box — break-before/break-inside
hints don't give enough control for presentation-quality output.
Use slide mode (/generate-slides) for shareable decks instead.
Share menu now: Copy HTML, Download .html (both strip chrome, bake theme).
Print CSS retained: break-inside:avoid on components, theme preservation.
---
CHANGELOG.md | 11 +-
.../references/css-patterns.md | 1 -
.../templates/theme-switcher-demo.html | 290 +-----------------
3 files changed, 8 insertions(+), 294 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebe58b4..1e0af1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,21 +13,20 @@
- Keyboard accessible: Escape closes dropdown, Tab navigation through options
### Share Button
-- In-page share dropdown with three options: Copy HTML, Download .html, Save as PDF
+- In-page share dropdown: Copy HTML, Download .html
- Copy/download strips all interactive chrome (theme switcher, share button, background selector) and bakes current theme into the HTML — recipients see exactly what you see
-- Save as PDF opens browser print dialog with print-optimized CSS (light theme forced, chrome hidden, page breaks between sections)
- Toast notification confirms each action
### Background Switcher
-- Runtime background pattern selector with 9 options: Dot Grid, Fine Dots, Diagonal Lines, Cross Hatch, Radial Glow, Dual Glow, Blueprint Grid, Noise Texture, None
-- Patterns use CSS custom properties so they adapt automatically when the theme changes
-- Background choice persists through theme switches
+- Runtime background pattern selector with 15 options: Dot Grid, Hex Grid, Diagonal Lines, Cross Hatch, Radial Glow, Dual Glow, Blueprint Grid, Noise Texture, Honeycomb, Isometric, Topography, Parchment, Sketchbook, Graph Paper, Linen
+- Patterns use CSS custom properties and mode-aware opacity (stronger in light mode)
+- Background re-applies automatically on theme switch
### Windows Support
- Added Windows `start` command to browser-open instructions in SKILL.md
### Print Styles
-- New `@media print` rules in `css-patterns.md`: force light colors, hide interactive chrome, page breaks between sections, expand collapsibles, show link URLs
+- New `@media print` rules in `css-patterns.md`: preserve active theme, hide interactive chrome, prevent component splits with `break-inside: avoid`
### Accessibility
- Color contrast guidance (WCAG 2.1 AA ratios)
diff --git a/plugins/visual-explainer/references/css-patterns.md b/plugins/visual-explainer/references/css-patterns.md
index d8e65e8..21352d1 100644
--- a/plugins/visual-explainer/references/css-patterns.md
+++ b/plugins/visual-explainer/references/css-patterns.md
@@ -2202,7 +2202,6 @@ The `SharePage.getCleanHTML()` function clones the document, removes the `.theme
```
diff --git a/plugins/visual-explainer/templates/theme-switcher-demo.html b/plugins/visual-explainer/templates/theme-switcher-demo.html
index e0f5a58..6b96201 100644
--- a/plugins/visual-explainer/templates/theme-switcher-demo.html
+++ b/plugins/visual-explainer/templates/theme-switcher-demo.html
@@ -759,182 +759,24 @@
margin-bottom: 32px;
}
- /* === Page Break Markers === */
- .page-break-marker {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 8px 0;
- padding: 4px 0;
- cursor: default;
- }
-
- .page-break-marker::before {
- content: '';
- position: absolute;
- left: 0;
- right: 0;
- top: 50%;
- border-top: 2px dashed var(--accent);
- opacity: 0.5;
- }
-
- .page-break-marker__label {
- position: relative;
- font-family: var(--font-mono);
- font-size: 10px;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 1.5px;
- color: var(--accent);
- background: var(--bg);
- padding: 2px 12px;
- border: 1px solid var(--accent);
- border-radius: 4px;
- opacity: 0.7;
- display: flex;
- align-items: center;
- gap: 6px;
- }
-
- .page-break-marker__label svg {
- width: 12px;
- height: 12px;
- }
-
- .page-break-marker__remove {
- position: absolute;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- width: 22px;
- height: 22px;
- border: 1px solid var(--border-bright);
- background: var(--surface);
- color: var(--text-dim);
- border-radius: 50%;
- cursor: pointer;
- font-size: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0;
- transition: opacity 0.15s, color 0.15s, border-color 0.15s;
- }
-
- .page-break-marker:hover .page-break-marker__remove {
- opacity: 1;
- }
-
- .page-break-marker__remove:hover {
- color: var(--red);
- border-color: var(--red);
- }
-
- /* Insertion points — shown when page break mode is active */
- .page-break-insert {
- display: none;
- height: 24px;
- margin: 4px 0;
- position: relative;
- cursor: pointer;
- border-radius: 6px;
- transition: background 0.15s;
- }
-
- body.pb-mode .page-break-insert {
- display: block;
- }
-
- .page-break-insert::before {
- content: '';
- position: absolute;
- left: 20%;
- right: 20%;
- top: 50%;
- border-top: 1px dashed var(--text-dim);
- opacity: 0;
- transition: opacity 0.15s;
- }
-
- .page-break-insert::after {
- content: '+ page break';
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- font-family: var(--font-mono);
- font-size: 10px;
- color: var(--text-dim);
- opacity: 0;
- transition: opacity 0.15s;
- white-space: nowrap;
- background: var(--bg);
- padding: 0 8px;
- }
-
- .page-break-insert:hover {
- background: var(--accent-dim);
- }
-
- .page-break-insert:hover::before,
- .page-break-insert:hover::after {
- opacity: 1;
- }
-
- /* Toolbar button active state */
- .pb-toggle.active {
- background: var(--accent-dim) !important;
- color: var(--accent) !important;
- border-color: var(--accent) !important;
- }
-
/* === Print === */
- /* Print: keep active theme, strip chrome, kill browser headers/footers */
- @page {
- margin: 0;
- size: auto;
- }
+ @page { margin: 0; size: auto; }
@media print {
- /* Force ALL background colors to print — must be on every element */
*, *::before, *::after {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
-
- body {
- padding: 40px !important;
- }
+ body { padding: 40px !important; }
.theme-switcher, .zoom-controls, .diagram-shell__hint, .share-toast { display: none !important; }
*, *::before, *::after { animation: none !important; transition: none !important; }
-
- /* Manual page break markers */
- .page-break-marker {
- break-before: page;
- border: none !important;
- height: 0 !important;
- padding: 0 !important;
- margin: 0 !important;
- visibility: hidden;
- }
- .page-break-marker .page-break-marker__label,
- .page-break-marker .page-break-marker__remove {
- display: none !important;
- }
-
- /* Keep components together — don't split across pages */
.ve-card, .ve-card--elevated, .ve-card--hero,
.kpi-card, .kpi-row, .table-wrap,
.code-block, .mermaid-wrap, .diagram-shell,
.diff-panels, details.collapsible, .card-grid, .callout {
break-inside: avoid;
}
-
- /* Hide page break mode UI */
- .page-break-insert { display: none !important; }
}
@@ -951,15 +793,6 @@
-
-
-
-
-
-
-
-
-
@@ -985,10 +818,6 @@
Download .html
-
-
- Save as PDF
-
@@ -1869,128 +1698,15 @@ Mermaid Diagram (re-renders on theme change)
closeMenus();
}
- function savePDF() {
- closeMenus();
- toast('Destination: Save as PDF | More settings: check "Background graphics"');
-
- // Bake all computed colors as inline styles — CSS variables don't survive print
- const allEls = document.querySelectorAll('body, .ve-card, .ve-card--hero, .ve-card--elevated, .kpi-card, .table-wrap, .code-block, .mermaid-wrap, .diff-panel__body, .diff-panel__header, .diff-panel__header--before, .diff-panel__header--after, details.collapsible summary, details.collapsible .collapsible__body, .data-table th, .data-table td, .data-table tbody tr, .status, .tag, .callout, .zoom-controls, h1, h2, p, li, span, code, .subtitle, .kpi-card__value, .kpi-card__label, .kpi-card__trend, .ve-card__label');
- const originals = [];
-
- allEls.forEach(el => {
- const cs = getComputedStyle(el);
- originals.push({
- el,
- bg: el.style.backgroundColor,
- color: el.style.color,
- borderColor: el.style.borderColor,
- borderLeftColor: el.style.borderLeftColor,
- borderBottomColor: el.style.borderBottomColor,
- boxShadow: el.style.boxShadow,
- });
- el.style.backgroundColor = cs.backgroundColor;
- el.style.color = cs.color;
- el.style.borderColor = cs.borderColor;
- el.style.borderLeftColor = cs.borderLeftColor;
- el.style.borderBottomColor = cs.borderBottomColor;
- // Skip box-shadow — it's decorative and causes print issues
- });
-
- setTimeout(() => {
- window.print();
- // Restore after print dialog closes
- setTimeout(() => {
- originals.forEach(o => {
- o.el.style.backgroundColor = o.bg;
- o.el.style.color = o.color;
- o.el.style.borderColor = o.borderColor;
- o.el.style.borderLeftColor = o.borderLeftColor;
- o.el.style.borderBottomColor = o.borderBottomColor;
- o.el.style.boxShadow = o.boxShadow;
- });
- }, 1000);
- }, 400);
- }
-
function closeMenus() {
document.querySelectorAll('.share-dropdown__menu, .theme-switcher__menu, .bg-dropdown__menu').forEach(m => m.classList.remove('open'));
}
- return { copyHTML, download, savePDF };
+ return { copyHTML, download };
})();
window.SharePage = SharePage;
- // === PageBreaks module ===
- const PageBreaks = (function() {
- let active = false;
- let insertPoints = [];
-
- function toggle() {
- active = !active;
- document.body.classList.toggle('pb-mode', active);
- document.querySelector('.pb-toggle').classList.toggle('active', active);
-
- if (active) {
- buildInsertPoints();
- } else {
- removeInsertPoints();
- }
- }
-
- function buildInsertPoints() {
- removeInsertPoints();
- // Find all direct children of .wrap that are block elements
- const wrap = document.querySelector('.wrap');
- if (!wrap) return;
- const children = Array.from(wrap.children).filter(el =>
- !el.classList.contains('page-break-insert') &&
- !el.classList.contains('page-break-marker') &&
- el.tagName !== 'SCRIPT'
- );
-
- children.forEach((child, i) => {
- if (i === 0) return; // Don't put one before the title
- const point = document.createElement('div');
- point.className = 'page-break-insert';
- point.onclick = function() {
- insertBreak(point);
- };
- child.parentNode.insertBefore(point, child);
- insertPoints.push(point);
- });
- }
-
- function removeInsertPoints() {
- insertPoints.forEach(p => p.remove());
- insertPoints = [];
- }
-
- function insertBreak(insertPoint) {
- const marker = document.createElement('div');
- marker.className = 'page-break-marker';
- marker.innerHTML = `
-
-
- Page Break
-
- ×
- `;
- marker.querySelector('.page-break-marker__remove').onclick = function(e) {
- e.stopPropagation();
- marker.remove();
- };
-
- insertPoint.parentNode.insertBefore(marker, insertPoint);
- insertPoint.remove();
- insertPoints = insertPoints.filter(p => p !== insertPoint);
- }
-
- return { toggle };
- })();
-
- window.PageBreaks = PageBreaks;
-
// === BackgroundSwitcher module ===
// Populate background menu
const bgMenuEl = document.querySelector('.bg-dropdown__menu');
From 14c0714e3e6251746e58aac2ce9451997de25914 Mon Sep 17 00:00:00 2001
From: Jimmy CrakCrn <128385879+jccidc@users.noreply.github.com>
Date: Fri, 10 Apr 2026 21:25:57 -0500
Subject: [PATCH 3/9] fix: commit Interactive Controls section, bump version to
0.7.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
SKILL.md had the entire "Step 4: Interactive Controls" section
(theme switcher, background patterns, share button requirement)
as an unstaged local change — agents loading from git got no
theme engine instructions. Now committed.
Also syncs version to 0.7.0 across plugin.json, marketplace.json,
package.json, and SKILL.md frontmatter. CHANGELOG print styles
bullet removed (feature was reverted in 30213a5), replaced with
Removed section noting the revert.
---
.claude-plugin/marketplace.json | 2 +-
CHANGELOG.md | 6 ++---
package.json | 2 +-
.../.claude-plugin/plugin.json | 2 +-
plugins/visual-explainer/SKILL.md | 26 +++++++++++++++++--
5 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index f7e33d6..5744e90 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -13,7 +13,7 @@
"name": "visual-explainer",
"source": "./plugins/visual-explainer",
"description": "Generate beautiful HTML pages for diagrams, diff reviews, plan reviews, slides, and data tables",
- "version": "0.6.3",
+ "version": "0.7.0",
"author": {
"name": "nicobailon"
},
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e0af1e..a9b1a5b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,15 +25,15 @@
### Windows Support
- Added Windows `start` command to browser-open instructions in SKILL.md
-### Print Styles
-- New `@media print` rules in `css-patterns.md`: preserve active theme, hide interactive chrome, prevent component splits with `break-inside: avoid`
-
### Accessibility
- Color contrast guidance (WCAG 2.1 AA ratios)
- Focus-visible keyboard indicators
- ARIA landmarks guidance (nav, main, article)
- Screen reader considerations for Mermaid diagrams, status indicators, images, tables
+### Removed
+- Print/PDF export (page breaks, PDF save, print preview) — CSS print layout was unreliable across browsers. May revisit with a different approach.
+
### Bug Fixes
- Synced `marketplace.json` version from 0.5.1 to 0.6.3 (was stale)
- Added Claude Code skill paths to `share.sh` (previously only checked Pi paths)
diff --git a/package.json b/package.json
index 8649bf5..59857bc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "visual-explainer",
- "version": "0.6.3",
+ "version": "0.7.0",
"description": "Agent skill that generates beautiful HTML pages for diagrams, diff reviews, plan reviews, and data tables",
"keywords": [
"claude-code-plugin",
diff --git a/plugins/visual-explainer/.claude-plugin/plugin.json b/plugins/visual-explainer/.claude-plugin/plugin.json
index e0feb43..6a6c1e0 100644
--- a/plugins/visual-explainer/.claude-plugin/plugin.json
+++ b/plugins/visual-explainer/.claude-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "visual-explainer",
"description": "Generate beautiful HTML pages for diagrams, diff reviews, plan reviews, and data tables",
- "version": "0.6.3",
+ "version": "0.7.0",
"author": {
"name": "nicobailon"
},
diff --git a/plugins/visual-explainer/SKILL.md b/plugins/visual-explainer/SKILL.md
index 6c13055..e68f450 100644
--- a/plugins/visual-explainer/SKILL.md
+++ b/plugins/visual-explainer/SKILL.md
@@ -5,7 +5,7 @@ license: MIT
compatibility: Requires a browser to view generated HTML files. Optional surf-cli for AI image generation.
metadata:
author: nicobailon
- version: "0.6.3"
+ version: "0.7.0"
---
# Visual Explainer
@@ -189,7 +189,29 @@ Put your primary aesthetic in `:root` and the alternate in the media query:
Keep animations purposeful: entrance reveals, hover feedback, and user-initiated interactions. Nothing should glow or pulse on its own.
-### 4. Deliver
+### 4. Interactive Controls (Required)
+
+**Every generated page must include the theme switcher toolbar.** Read the Theme Switcher, Share Button, and Background Switcher sections in `./references/css-patterns.md` for the full implementation. Read `./templates/theme-switcher-demo.html` as the canonical reference — it contains the complete working implementation with all presets, patterns, and share functionality.
+
+The toolbar goes in `` as the first element (fixed top-right) and includes:
+1. **Light/dark mode toggle** — sun/moon button
+2. **Theme preset dropdown** — all 11 presets with font hot-reload and Mermaid re-render
+3. **Background pattern dropdown** — all 15+ patterns, mode-aware
+4. **Share dropdown** — Copy HTML (strips chrome, bakes theme) and Download .html
+
+**Implementation checklist:**
+- ` ` in `` (the engine swaps this href on preset change)
+- All 11 `THEME_PRESETS` with full light/dark palettes
+- All background patterns with `patLine()`, `patDot()`, `patLineBold()` helpers
+- `ThemeSwitcher` IIFE with `init()`, `setPreset()`, `toggleMode()`, `rerenderMermaid()`
+- `SharePage` IIFE with `copyHTML()`, `download()` — `getCleanHTML()` strips the toolbar and bakes CSS variables
+- Background menu populated dynamically from `BG_PATTERNS` array
+- `currentBgIndex` tracking + re-apply on theme change
+- Default preset: random (vary each generation). Default background: Dot Grid. Default mode: detect from `prefers-color-scheme`.
+
+**Do not abbreviate or subset the presets/patterns.** Copy them from the demo template. Missing presets or truncated pattern functions are bugs.
+
+### 5. Deliver
**Output location:** Write to `~/.agent/diagrams/`. Use a descriptive filename based on content: `modem-architecture.html`, `pipeline-flow.html`, `schema-overview.html`. The directory persists across sessions.
From 03ae2c1a6e8b5487db9c4945a07b2b53906e741a Mon Sep 17 00:00:00 2001
From: Jimmy CrakCrn <128385879+jccidc@users.noreply.github.com>
Date: Fri, 10 Apr 2026 21:29:20 -0500
Subject: [PATCH 4/9] =?UTF-8?q?fix:=20correct=20preset=20count=20in=20CHAN?=
=?UTF-8?q?GELOG=20=E2=80=94=2011,=20not=208?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9b1a5b..36d6697 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
## [0.7.0] - 2026-04-10
### Theme Switcher
-- Runtime palette and font switching via dropdown — 8 curated presets: Teal Slate, Terracotta Sage, Editorial Rose, Deep Navy Gold, Amber Emerald, Dracula, Nord, Catppuccin Mocha
+- Runtime palette and font switching via dropdown — 11 curated presets: Teal Slate, Terracotta Sage, Editorial Rose, Deep Navy Gold, Amber Emerald, Dracula, Nord, Catppuccin Mocha, Solarized Light, Rose Pine Dawn, GitHub Light
- Each preset defines full light and dark palettes with all CSS custom properties (bg, surface, border, text, accent, node colors, status colors)
- Google Fonts hot-reload via ` ` element swap — fonts change instantly without page refresh
- Mermaid diagrams re-render automatically on theme change with updated `themeVariables`
From 053aa7111cd64204b4b4aa934f48a279ff7b5168 Mon Sep 17 00:00:00 2001
From: Jimmy CrakCrn <128385879+jccidc@users.noreply.github.com>
Date: Fri, 10 Apr 2026 21:37:35 -0500
Subject: [PATCH 5/9] docs: add screenshots for theme engine PR
---
docs/screenshots/background-patterns.png | Bin 0 -> 200462 bytes
docs/screenshots/files-changed.png | Bin 0 -> 139603 bytes
docs/screenshots/known-issues.png | Bin 0 -> 111160 bytes
docs/screenshots/overview.png | Bin 0 -> 569557 bytes
docs/screenshots/test-matrix.png | Bin 0 -> 208542 bytes
docs/screenshots/theme-presets.png | Bin 0 -> 179907 bytes
6 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 docs/screenshots/background-patterns.png
create mode 100644 docs/screenshots/files-changed.png
create mode 100644 docs/screenshots/known-issues.png
create mode 100644 docs/screenshots/overview.png
create mode 100644 docs/screenshots/test-matrix.png
create mode 100644 docs/screenshots/theme-presets.png
diff --git a/docs/screenshots/background-patterns.png b/docs/screenshots/background-patterns.png
new file mode 100644
index 0000000000000000000000000000000000000000..45bd671d8813d12da12649e961971880b1e08980
GIT binary patch
literal 200462
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 `Dd{AL0mv@b$_GM!2s&IT57Vu>}(=i&P
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