This commit is contained in:
2026-04-12 01:06:31 +07:00
commit 10d660cbcb
1066 changed files with 228596 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
# Generation Modes
## Step 1: Determine Output Location
1. Check if there's an active plan context (from `## Plan Context` in hook injection)
2. If active plan exists: save to `{plan_dir}/visuals/{topic-slug}.md`
3. If no active plan: save to `plans/visuals/{topic-slug}.md`
4. Create `visuals/` directory if it doesn't exist
## Step 2: Generate Content
**Mermaid Diagram Syntax:**
When generating mermaid code blocks, use `/ck:mermaidjs-v11` skill for v11 syntax rules.
**Essential rules (always apply):**
- Quote node text with special characters: `A["text with /slashes"]`
- Escape brackets in labels: `A["array[0]"]`
Use the appropriate template based on flag:
### --explain (Visual Explanation)
```markdown
# Visual Explanation: {topic}
## Overview
Brief description of what we're explaining.
## Quick View (ASCII)
[ASCII diagram of component relationships]
## Detailed Flow
[Mermaid sequence/flowchart diagram]
## Key Concepts
1. **Concept A** - Explanation
2. **Concept B** - Explanation
## Code Example (if applicable)
[Relevant code snippet with comments]
```
### --slides (Presentation Format)
```markdown
# {Topic} - Visual Presentation
---
## Slide 1: Introduction
- One concept per slide
- Bullet points only
---
## Slide 2: The Problem
[Mermaid flowchart]
---
## Slide 3: The Solution
- Key point 1
- Key point 2
---
## Slide 4: Summary
Key takeaways...
```
### --diagram (Focused Diagram)
```markdown
# Diagram: {topic}
## ASCII Version
[ASCII architecture diagram]
## Mermaid Version
[Mermaid flowchart/graph]
```
### --ascii (Terminal-Friendly Only)
```
[ASCII-only box diagram with legend]
```
## Step 3: Save and Preview
1. Write generated content to determined path
2. Start preview server with the generated file:
```bash
node .opencode/skills/markdown-novel-viewer/scripts/server.cjs \
--file "<generated-file-path>" --host 0.0.0.0 --open --foreground
```
## Step 4: Report to User
Report:
- Generated file path
- Preview URL (local + network)
- Remind: file saved in plan's `visuals/` folder for future reference
---
## HTML Mode Generation
When `--html` flag is present (or implied by `--diff`, `--plan-review`, `--recap`), generate self-contained HTML instead of Markdown.
### HTML Step 1: Determine Output Location
- Same plan-aware logic as markdown mode but with `.html` extension
- Active plan: `{plan_dir}/visuals/{topic-slug}.html`
- No plan: `plans/visuals/{topic-slug}.html`
- Create `visuals/` directory if needed
### HTML Step 2: Read References
Always read `html-design-guidelines.md` first (anti-slop rules, style presets).
Then read mode-specific references:
| Mode | References | Templates to study |
|------|------------|-------------------|
| --html --explain | html-css-patterns.md, html-libraries.md | architecture.html |
| --html --diagram | html-css-patterns.md, html-libraries.md | mermaid-flowchart.html or architecture.html |
| --html --slides | html-slide-patterns.md, html-css-patterns.md, html-libraries.md | slide-deck.html |
| --html --diff | html-css-patterns.md, html-libraries.md | data-table.html, architecture.html |
| --html --plan-review | html-css-patterns.md, html-libraries.md | architecture.html, data-table.html |
| --html --recap | html-css-patterns.md, html-libraries.md | architecture.html, data-table.html |
For multi-section pages (explain, diff, plan-review, recap): also read `html-responsive-nav.md`.
### HTML Step 3: Generate Content
Follow the 4-phase workflow:
**Think:** Determine content-type routing:
- Mermaid for topology (flowcharts, sequence, ER, state, mind maps, class, C4)
- CSS Grid for text-heavy architecture (cards with descriptions, code references)
- HTML `<table>` for data (requirement audits, comparisons, matrices)
- Chart.js for real charts (KPI dashboards, sparklines)
- Hybrid for complex systems (15+ elements): simple Mermaid overview + detailed CSS Grid cards
**Structure:** Pick template pattern, plan sections, assign depth tiers (hero/elevated/default/recessed).
**Style:** Select font pairing + palette from curated presets. Vary from previous outputs. Apply anti-slop checks:
- No Inter/Roboto/system-ui alone as body font
- No indigo/violet (#8b5cf6, #7c3aed) as accent
- No animated glowing box-shadows
- No gradient text on headings
- No emoji icons in section headers
- No three-dot window chrome on code blocks
**Deliver:** Write single self-contained `.html` file — all CSS and JavaScript inline. External resources: CDN only (Google Fonts, Mermaid.js v11, Chart.js, anime.js).
**MANDATORY — Theme Toggle:** Every HTML page MUST include the light/dark theme toggle button from `html-css-patterns.md` → "Theme Toggle Button" section. This is non-negotiable. The toggle button (`<button class="theme-toggle">`) must be the first child of `<body>`, with its CSS and JS inlined. Pages without the toggle are considered incomplete.
For `--slides`: recommend invoking `/ck:ui-ux-pro-max` for richer style selection.
Must use `/ck:mermaidjs-v11` for any Mermaid diagrams.
### HTML Step 4: Open in Browser
- macOS: `open "{output-path}"`
- Linux: `xdg-open "{output-path}"`
- Windows: `start "{output-path}"`
- No server needed — file is self-contained
- Report file path and confirm browser opened
### Data Gathering for HTML-Only Modes
#### --diff [ref]
1. Detect scope: branch name → working tree diff; commit hash → `git show`; HEAD → uncommitted; PR number → `gh pr diff`; range → two commits; no arg → diff against main
2. Run: `git diff --stat`, `git diff --name-status`, line counts
3. Read all changed files + surrounding context
4. Scan new public API surface (grep exports, functions, classes, interfaces)
5. Check CHANGELOG.md, README.md, docs updates
6. Reconstruct decision rationale from commits/conversation/progress docs
#### --plan-review [plan-file]
1. Input: explicit plan file path OR detect from active plan context
2. Read plan in full (problem, changes, rejected alternatives, scope)
3. Read every file the plan references + their dependencies
4. Map blast radius (imports, tests, config, public API)
5. Cross-reference: plan assumptions vs actual code state
#### --recap [timeframe]
1. Parse time window: shorthand (2w, 30d, 3m) → git `--since` format; default 2w
2. Project identity: README, CHANGELOG, package.json, file structure
3. Recent activity: `git log --oneline --since=...`, `git shortlog`
4. Current state: `git status`, stale branches, TODOs, progress docs
5. Decision context: commit messages, plans, ADRs
6. Architecture scan: key files, module structure, frequently changed areas
### Quality Checklist
Before delivering HTML output, verify:
- [ ] **Squint test:** Visual hierarchy visible at arm's length?
- [ ] **Swap test:** Would this look AI-generated? Check against forbidden patterns
- [ ] **Theme toggle (MANDATORY):** Toggle button present as first child of `<body>`? Both light and dark modes render correctly? See `html-css-patterns.md` → "Theme Toggle Button".
- [ ] **Overflow:** No horizontal scroll on content (tables excepted, wrapped in scroll container)
- [ ] **Mermaid:** Zoom controls present? ELK layout for 10+ nodes?
- [ ] **Responsiveness:** Readable on mobile width?

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
# HTML Design Guidelines
Guidelines for generating distinctive, high-quality HTML pages. Read this before generating any HTML output.
---
## Anti-Slop: Forbidden Patterns
These patterns are explicitly forbidden — they signal "AI-generated template" and undermine quality. Check every page against this list before delivering.
### Typography — Forbidden Fonts as Primary `--font-body`
- **Inter** — the single most overused AI default
- **Roboto, Arial, Helvetica** — generic system fallbacks promoted to primary
- **system-ui, sans-serif alone** — no character, no intent
**Required:** Pick from the font pairings in `html-libraries.md`. Every generation should use a different pairing from the last.
### Color Palette — Forbidden Accents
- `#8b5cf6`, `#7c3aed`, `#a78bfa` — Tailwind's indigo/violet defaults
- `#d946ef` — fuchsia
- `#06b6d4` + `#d946ef` + `#f472b6` — the cyan + magenta + pink neon gradient combination
- Any palette describable as "Tailwind defaults with purple/pink/cyan accents"
**Forbidden color effects:**
- Gradient text on headings (`background: linear-gradient(...); background-clip: text;`) — screams AI-generated
- Animated glowing box-shadows (`@keyframes glow { box-shadow: 0 0 20px... }`) — always produces AI slop
- Multiple overlapping radial glows in accent colors creating a "neon haze"
- Pulsing/breathing effects on static content
- Continuous animations that run after page load (except progress indicators)
**Required accents (use these):**
- Terracotta + sage (`#c2410c`, `#65a30d`) — warm, earthy
- Teal + slate (`#0891b2`, `#0369a1`) — technical, precise
- Rose + cranberry (`#be123c`, `#881337`) — editorial, refined
- Amber + emerald (`#d97706`, `#059669`) — data-focused
- Deep blue + gold (`#1e3a5f`, `#d4a73a`) — premium, sophisticated
### Section Headers — Forbidden
- Emoji icons in section headers (🏗️ ⚙️ 📁 💻 📅 🔗 ⚡ 🔧 📦 🚀, etc.)
- Section headers that all use the same icon-in-rounded-box pattern
**Required:** Use styled monospace labels with colored dot indicators (`.section-label` + `.ve-card__label` pattern), numbered badges, or asymmetric section dividers. If an icon is genuinely needed, use inline SVG matching the palette.
### Layout — Forbidden
- Perfectly centered everything with uniform padding
- All cards styled identically with the same border-radius, shadow, and spacing
- Every section getting equal visual treatment — no hero/primary vs. secondary distinction
- Symmetric layouts where left and right halves mirror each other
### Template Clichés — Forbidden
- Three-dot window chrome (red/yellow/green dots) on code blocks
- KPI cards where every metric has identical gradient text treatment
- "Neon Dashboard" aesthetic
- Gradient meshes with pink/purple/cyan blobs in the background
### The Slop Test
Before delivering, check: **Would a developer immediately think "AI generated this"?**
Signs of slop:
1. Inter or Roboto font with purple/violet gradient accents
2. Every heading has `background-clip: text` gradient
3. Emoji icons leading every section
4. Glowing cards with animated shadows
5. Cyan-magenta-pink color scheme on dark background
6. Perfectly uniform card grid with no visual hierarchy
7. Three-dot code block chrome
If two or more are present: regenerate with Blueprint, Editorial, Paper/ink, or a specific IDE theme.
---
## Palette Cohesion Principles
**Every generated page must feel like one intentional color story.** This is the single most important readability rule.
1. **Background warmth must match accent warmth.** Terracotta accents need warm cream backgrounds (`#faf7f5`), not cool gray (`#f8f9fa`). Teal accents need cool-tinted backgrounds (`#f0fdfa`). Mixing warm accents on cool backgrounds (or vice versa) creates visual dissonance that makes pages feel generic.
2. **Text-dim must belong to the same family.** On warm backgrounds, use warm grays (`#8a7e72`, `#a69889`). On cool backgrounds, use cool grays (`#5f8a85`, `#8b949e`). Never use GitHub-style `#6b7280` on warm cream.
3. **Borders should be tinted, not neutral.** Use `rgba(0, 0, 0, 0.07)` or palette-tinted borders instead of flat `#e5e7eb`. Borders should be barely visible — felt, not seen.
4. **Surface layers create depth without fighting.** Define `--surface`, `--surface2`, and `--surface-elevated` as gradations of the same hue, not different colors.
5. **Extend every palette with semantic colors.** Every preset below should also define `--green`, `--red`, `--amber`, `--sage`, `--teal`, `--plum` (and their `*-dim` variants) that harmonize with the base palette. Richer semantic sets prevent monotony without clashing.
---
## 6 Curated Style Presets
Pick one and commit. The constrained presets (Blueprint, Editorial, Paper/Ink, Terminal Mono) are safer — they have specific requirements that prevent defaulting to generic patterns.
**IMPORTANT:** After choosing a preset, extend it with semantic colors (`--green`, `--red`, `--amber`, `--sage`, `--teal`, `--plum` + `*-dim` variants) that harmonize with the base palette. The default palette in `html-css-patterns.md` shows the full semantic structure — replicate that structure for whichever preset you choose.
### Blueprint
Technical drawing feel. Subtle grid background, deep slate/blue palette, monospace labels, precise borders.
```css
:root {
--font-body: 'DM Sans', system-ui, sans-serif;
--font-mono: 'Fira Code', 'SF Mono', monospace;
--bg: #0d1421;
--surface: #111d2e;
--surface-elevated: #162438;
--border: rgba(100, 160, 220, 0.12);
--border-bright: rgba(100, 160, 220, 0.22);
--text: #c8d8e8;
--text-dim: #607080;
--accent: #4a90d9;
--accent-dim: rgba(74, 144, 217, 0.1);
}
@media (prefers-color-scheme: light) {
:root {
--bg: #f0f4f8;
--surface: #ffffff;
--surface-elevated: #e8eef4;
--border: rgba(30, 60, 100, 0.1);
--text: #1a2a3a;
--text-dim: #5a7090;
--accent: #1a5fa8;
--accent-dim: rgba(26, 95, 168, 0.08);
}
}
```
Background: faint dot grid (`background-image: radial-gradient(circle, var(--border) 1px, transparent 1px); background-size: 24px 24px`). Monospace labels throughout.
### Editorial
Serif headlines (Instrument Serif or Crimson Pro), generous whitespace, muted earth tones or deep navy + gold.
```css
:root {
--font-body: 'Instrument Serif', Georgia, serif;
--font-mono: 'JetBrains Mono', 'SF Mono', monospace;
--bg: #0f1729;
--surface: #162040;
--surface-elevated: #1d2b52;
--border: rgba(200, 180, 140, 0.08);
--text: #e8e4d8;
--text-dim: #9a9484;
--accent: #d4a73a;
--accent-dim: rgba(212, 167, 58, 0.1);
}
@media (prefers-color-scheme: light) {
:root {
--bg: #faf8f2;
--surface: #ffffff;
--surface-elevated: #f5f0e6;
--border: rgba(30, 30, 50, 0.08);
--text: #1a1814;
--text-dim: #7a7468;
--accent: #b8860b;
--accent-dim: rgba(184, 134, 11, 0.08);
}
}
```
### Paper/Ink
Warm cream `#faf7f5` background, terracotta/sage accents, informal feel.
```css
:root {
--font-body: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-mono: 'Azeret Mono', 'SF Mono', monospace;
--bg: #faf6f0;
--surface: #ffffff;
--surface-elevated: #fffdf5;
--border: rgba(60, 40, 20, 0.08);
--text: #2c2a25;
--text-dim: #7c756a;
--accent: #c2410c;
--accent-dim: rgba(194, 65, 12, 0.08);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1c1916;
--surface: #262220;
--surface-elevated: #3a3430;
--border: rgba(200, 180, 160, 0.08);
--text: #f0e8dc;
--text-dim: #a09888;
--accent: #e85d2a;
--accent-dim: rgba(232, 93, 42, 0.1);
}
}
```
### Terminal Mono
Green/amber on near-black, monospace everything. Developer-native.
```css
:root {
--font-body: 'Geist Mono', 'SF Mono', Consolas, monospace;
--font-mono: 'Geist Mono', 'SF Mono', Consolas, monospace;
--bg: #0a0e14;
--surface: #12161e;
--surface-elevated: #222836;
--border: rgba(80, 250, 123, 0.06);
--text: #c8d6e5;
--text-dim: #5a6a7a;
--accent: #50fa7b;
--accent-dim: rgba(80, 250, 123, 0.08);
}
@media (prefers-color-scheme: light) {
:root {
--bg: #f4f6f8;
--surface: #ffffff;
--border: rgba(0, 80, 40, 0.08);
--text: #1a2332;
--text-dim: #5a6a7a;
--accent: #0d7a3e;
--accent-dim: rgba(13, 122, 62, 0.08);
}
}
```
Background: faint dot grid. Everything monospace. CRT glow optional (CSS only, no animation).
### Swiss Clean
White, geometric sans, single bold accent, visible grid. Minimal and precise.
```css
:root {
--font-body: 'DM Sans', system-ui, sans-serif;
--font-mono: 'Fira Code', 'SF Mono', monospace;
--bg: #ffffff;
--surface: #f8f8f8;
--surface-elevated: #ffffff;
--border: rgba(0, 0, 0, 0.08);
--text: #111111;
--text-dim: #666666;
--accent: #0055ff;
--accent-dim: rgba(0, 85, 255, 0.06);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #111111;
--surface: #1a1a1a;
--surface-elevated: #2a2a2a;
--border: rgba(255, 255, 255, 0.08);
--text: #f0f0f0;
--text-dim: #888888;
--accent: #3b82f6;
--accent-dim: rgba(59, 130, 246, 0.08);
}
}
```
### Warm Signal
Cream paper, bold sans, terracotta accents. Confident, modern. Same as Paper/Ink but bolder.
Uses Plus Jakarta Sans + Azeret Mono, terracotta `#c2410c` accent. See Paper/Ink preset above — Warm Signal is the same palette with higher contrast headings and stronger section dividers.
---
## Typography Rules
### Font Pairings (12 options — rotate, never repeat consecutively)
| Body / Headings | Mono / Labels | Feel | Use for |
|---|---|---|---|
| DM Sans | Fira Code | Friendly, developer | Blueprint, technical docs |
| Instrument Serif | JetBrains Mono | Editorial, refined | Plan reviews, decision logs |
| IBM Plex Sans | IBM Plex Mono | Reliable, readable | Architecture diagrams |
| Bricolage Grotesque | Fragment Mono | Bold, characterful | Data tables, dashboards |
| Plus Jakarta Sans | Azeret Mono | Rounded, approachable | Status reports, audits |
| Outfit | Space Mono | Clean geometric, modern | Flowcharts, pipelines |
| Sora | IBM Plex Mono | Technical, precise | ER diagrams, schemas |
| Crimson Pro | Noto Sans Mono | Scholarly, serious | RFC reviews, specs |
| Fraunces | Source Code Pro | Warm, distinctive | Project recaps |
| Geist | Geist Mono | Vercel-inspired, sharp | Modern API docs |
| Red Hat Display | Red Hat Mono | Cohesive family | System overviews |
| Libre Franklin | Inconsolata | Classic, reliable | Data-dense tables |
The first 5 pairings are recommended for most use cases.
### Load via Google Fonts
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
```
Always use `display=swap` for fast rendering. Include system font fallback in `font-family` stack.
### Typography by Content Voice
For prose-heavy pages, match fonts to content voice:
| Voice | Fonts | Best For |
|-------|-------|----------|
| Literary / Thoughtful | Literata, Lora, Newsreader, Merriweather | Essays, personal posts, long-form |
| Technical / Precise | IBM Plex Sans + Mono, Geist + Geist Mono | Documentation, READMEs, API refs |
| Bold / Contemporary | Bricolage Grotesque, Space Grotesk, DM Sans | Product pages, announcements |
| Minimal / Focused | Source Serif 4 + Source Sans 3, Karla + Inconsolata | Tutorials, focused reading |
---
## Quality Checklist
Before delivering any HTML page:
- **Squint test**: Blur your eyes. Can you still perceive hierarchy? Are sections visually distinct?
- **Swap test**: Would replacing fonts and colors with a generic dark theme make this indistinguishable? If yes, push the aesthetic further.
- **Theme toggle (MANDATORY)**: Toggle button MUST be present (first child of `<body>`). Switch between light and dark using the button. Both themes should look intentional, not broken. See `html-css-patterns.md` → "Theme Toggle Button".
- **Information completeness**: Does the page actually convey what was asked? Pretty but incomplete is a failure.
- **No overflow**: Resize the browser. No content should clip or escape its container. Every grid/flex child needs `min-width: 0`. Side-by-side panels need `overflow-wrap: break-word`.
- **Mermaid zoom controls**: Every `.mermaid-wrap` must have zoom controls (+//reset/expand), Ctrl/Cmd+scroll zoom, click-and-drag panning, and click-to-expand. See `html-css-patterns.md`.
- **File opens cleanly**: No console errors, no broken font loads, no layout shifts.
---
## Depth Tier System
Vary card depth to signal importance. Hero sections dominate; reference sections stay compact.
```css
/* Default — flat, no shadow */
.ve-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px 20px;
}
/* Elevated — KPIs, key sections */
.ve-card--elevated {
background: var(--surface-elevated);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
}
/* Recessed — code blocks, secondary content */
.ve-card--recessed {
background: color-mix(in srgb, var(--bg) 70%, var(--surface) 30%);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
}
/* Hero — executive summaries, focal elements */
.ve-card--hero {
background: color-mix(in srgb, var(--surface) 92%, var(--accent) 8%);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04);
border-color: color-mix(in srgb, var(--border) 50%, var(--accent) 50%);
}
```
Rule: Don't make everything elevated — when everything pops, nothing does.
---
## Content-Type Routing
When deciding how to render content:
| Content type | Approach | Why |
|---|---|---|
| Architecture (text-heavy) | CSS Grid cards + flow arrows | Rich card content needs CSS control |
| Architecture (topology-focused) | **Mermaid** | Visible connections need automatic edge routing |
| Flowchart / pipeline | **Mermaid** | Automatic node positioning |
| Sequence diagram | **Mermaid** | Lifelines need automatic layout |
| Data flow | **Mermaid** with edge labels | Connections need auto-routing |
| ER / schema diagram | **Mermaid** | Relationship lines between entities |
| State machine | **Mermaid** | State transitions with labeled edges |
| Mind map | **Mermaid** | Hierarchical branching |
| Class diagram | **Mermaid** | Inheritance lines with auto-routing |
| C4 architecture | **Mermaid** `graph TD` + `subgraph` | Native C4 hardcodes its own styles |
| Data table | HTML `<table>` | Semantic markup, accessibility, copy-paste |
| Timeline | CSS (central line + cards) | Simple linear layout |
| Dashboard | CSS Grid + Chart.js | Card grid with embedded charts |
| Simple A→B→C flows in slides | CSS Pipeline cards | Mermaid renders too small for simple linear flows |
---
## AI Image Generation
If `/ck:ai-multimodal` skill is available and image generation is appropriate, it can be used for hero banners, conceptual illustrations, and decorative accents that establish the page's visual tone.
**When to use:** Hero banners, conceptual illustrations for abstract systems, educational diagrams benefiting from artistic rendering, decorative accents reinforcing the aesthetic.
**When to skip:** Anything Mermaid or CSS handles well. Generic decoration that doesn't convey meaning. Data-heavy pages where images would distract. Always degrade gracefully — the page should stand on its own with CSS and typography alone.
For `--slides` presentation-grade output, consider invoking `/ck:ui-ux-pro-max` for richer style selection and distinctive font/palette pairing.

View File

@@ -0,0 +1,592 @@
# External Libraries (CDN)
Optional CDN libraries for cases where pure CSS/HTML isn't enough. Only include what the diagram actually needs — most diagrams need zero external JS.
## Mermaid.js — Diagramming Engine
Use for flowcharts, sequence diagrams, ER diagrams, state machines, mind maps, class diagrams, and any diagram where automatic node positioning and edge routing saves effort. Mermaid handles layout — you handle theming.
Do NOT use for dashboards — CSS Grid card layouts with Chart.js look better for those. Data tables use `<table>` elements.
**CDN:**
```html
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true, /* ... */ });
</script>
```
**With ELK layout** (required for `layout: 'elk'` — it's a separate package, not bundled in core):
```html
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
import elkLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk/dist/mermaid-layout-elk.esm.min.mjs';
mermaid.registerLayoutLoaders(elkLayouts);
mermaid.initialize({ startOnLoad: true, layout: 'elk', /* ... */ });
</script>
```
Without the ELK import and registration, `layout: 'elk'` silently falls back to dagre. Only import ELK when you actually need it — it adds significant bundle weight. Most simple diagrams render fine with dagre.
### Deep Theming
Always use `theme: 'base'` — it's the only theme where all `themeVariables` are fully customizable. The built-in themes (`default`, `dark`, `forest`, `neutral`) ignore most variable overrides.
```html
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
mermaid.initialize({
startOnLoad: true,
theme: 'base',
look: 'classic',
themeVariables: {
// Background and surfaces — teal/slate palette (not violet/indigo!)
primaryColor: isDark ? '#134e4a' : '#ccfbf1',
primaryBorderColor: isDark ? '#14b8a6' : '#0d9488',
primaryTextColor: isDark ? '#f0fdfa' : '#134e4a',
secondaryColor: isDark ? '#1e293b' : '#f0fdf4',
secondaryBorderColor: isDark ? '#059669' : '#16a34a',
secondaryTextColor: isDark ? '#f1f5f9' : '#1e293b',
tertiaryColor: isDark ? '#27201a' : '#fef3c7',
tertiaryBorderColor: isDark ? '#d97706' : '#f59e0b',
tertiaryTextColor: isDark ? '#fef3c7' : '#27201a',
// Lines and edges
lineColor: isDark ? '#64748b' : '#94a3b8',
// Text
fontSize: '16px',
fontFamily: 'var(--font-body)',
// Notes and labels
noteBkgColor: isDark ? '#1e293b' : '#fefce8',
noteTextColor: isDark ? '#f1f5f9' : '#1e293b',
noteBorderColor: isDark ? '#fbbf24' : '#d97706',
}
});
</script>
```
**FORBIDDEN in Mermaid themeVariables:** `#8b5cf6`, `#7c3aed`, `#a78bfa` (indigo/violet), `#d946ef` (fuchsia). Use teal, slate, amber, emerald, or colors from your page's palette.
### CSS Overrides on Mermaid SVG
Mermaid renders SVG. Override its classes for pixel-perfect control that `themeVariables` can't reach:
```css
/* Container — see html-css-patterns.md "Mermaid Zoom Controls" for the full zoom pattern */
.mermaid-wrap {
position: relative;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
overflow: auto;
}
/* CRITICAL: Force node/edge text to follow the page's color scheme.
Without this, themeVariables.primaryTextColor works for DEFAULT nodes,
but any classDef that sets color: will hardcode a single value that
breaks in the opposite color scheme. Fix: never set color: in classDef,
and always include these CSS overrides. */
.mermaid .nodeLabel { color: var(--text) !important; }
.mermaid .edgeLabel { color: var(--text-dim) !important; background-color: var(--bg) !important; }
.mermaid .edgeLabel rect { fill: var(--bg) !important; }
/* Node shapes */
.mermaid .node rect,
.mermaid .node circle,
.mermaid .node polygon {
stroke-width: 1.5px;
}
/* Edge paths */
.mermaid .edge-pattern-solid {
stroke-width: 1.5px;
}
/* Edge labels — smaller than node labels for visual hierarchy */
.mermaid .edgeLabel {
font-family: var(--font-mono) !important;
font-size: 13px !important;
}
/* Node labels — 16px default; drop to 14px for complex diagrams (20+ nodes) */
.mermaid .nodeLabel {
font-family: var(--font-body) !important;
font-size: 16px !important;
}
/* Sequence diagram actors */
.mermaid .actor {
stroke-width: 1.5px;
}
/* Sequence diagram messages */
.mermaid .messageText {
font-family: var(--font-mono) !important;
font-size: 12px !important;
}
/* ER diagram entities */
.mermaid .er.entityBox {
stroke-width: 1.5px;
}
/* Mind map nodes */
.mermaid .mindmap-node rect {
stroke-width: 1.5px;
}
```
### classDef and style Gotchas
`classDef` values and per-node `style` directives are static text inside `<pre>` — they can't use CSS variables or JS ternaries. Two rules:
1. **Never set `color:` in classDef or per-node `style` directives.** It hardcodes a text color that breaks in the opposite color scheme. Let the CSS overrides above handle text color via `var(--text)`.
2. **Use semi-transparent fills (8-digit hex) for node backgrounds.** They layer over whatever Mermaid's base theme background is, producing a tint that works in both light and dark modes. Use `20``44` alpha for subtle, `55``77` for prominent:
```
classDef highlight fill:#b5761433,stroke:#b57614,stroke-width:2px
classDef muted fill:#7c6f6411,stroke:#7c6f6444,stroke-width:1px
```
### Node Label Special Characters
Mermaid uses certain characters for shape syntax. Node labels containing these characters cause syntax errors unless quoted.
**Shape characters to watch:**
- `[/text/]` — parallelogram
- `[(text)]` — cylindrical
- `[[text]]` — subroutine
- `((text))` — circle
- `{{text}}` — hexagon
**If your node label starts with `/`, `\`, `(`, or `{`, wrap it in quotes:**
```
%% WRONG — syntax error (/ starts parallelogram shape)
CMD[/gallery command] --> SRV[server]
%% RIGHT — quotes escape the special character
CMD["/gallery command"] --> SRV[server]
```
**Edge labels with special characters also need quotes:**
```
%% WRONG
UI -->|"Use as Reference"| RET
%% RIGHT — use single quotes or no quotes for simple text
UI -->|Use as Reference| RET
```
Avoid opaque light fills like `fill:#fefce8` — they render as bright boxes in dark mode.
### stateDiagram-v2 Label Limitations
State diagram transition labels have a strict parser. Avoid:
- `<br/>` — only works in flowcharts; causes a parse error in state diagrams
- Parentheses in labels — `cancel()` can confuse the parser
- Multiple colons — the first `:` is the label delimiter; extra colons may break parsing
If you need multi-line labels or special characters, use a `flowchart` instead of `stateDiagram-v2`. Flowcharts support quoted labels (`|"label with: special chars"|`) and `<br/>` for line breaks.
### Writing Valid Mermaid
Most Mermaid failures come from a few recurring issues.
**For multi-line flowchart node labels, use `<br/>` (not `\n`):**
```
%% WRONG — renders literal "\n" in node text
A["Copilot Backend\n/api + /api/voicebot"] --> B["Redis"]
%% RIGHT — renders on two lines
A["Copilot Backend<br/>/api + /api/voicebot"] --> B["Redis"]
```
**Quote labels with special characters.** Parentheses, colons, commas, brackets, and ampersands break the parser when unquoted:
```
A["handleRequest(ctx)"] --> B["DB: query users"]
A[handleRequest] --> B[query users]
```
**Keep IDs simple.** Node IDs should be alphanumeric with no spaces or punctuation:
```
userSvc["User Service"] --> authSvc["Auth Service"]
```
**Max 10-12 nodes per Mermaid diagram.** Beyond that, readability collapses even with zoom controls. For complex architectures (15+ elements), use the **hybrid pattern**: a simple 5-8 node Mermaid overview showing module relationships, followed by CSS Grid cards with detailed function lists.
```
subgraph Auth
login --> validate --> token
end
subgraph API
gateway --> router --> handler
end
Auth --> API
```
**Arrow styles for semantic meaning:**
| Arrow | Meaning | Use for |
|-------|---------|---------|
| `-->` | Solid | Primary flow |
| `-.->` | Dotted | Optional, async, or fallback paths |
| `==>` | Thick | Critical or highlighted path |
| `--x` | Cross | Rejected or blocked |
| `-->\|label\|` | Labeled | Decision branches, data descriptions |
**Sequence diagram messages must be plain text.** Unlike flowchart labels, sequence diagram messages cannot be quoted or escaped. Curly braces `{}`, square brackets `[]`, angle brackets `<>`, and `&` will silently break the parser:
```
%% WRONG — parser chokes on braces, brackets, ampersand
A->>B: web_search({ queries: [...] })
B->>B: User removes query 2, keeps 1 & 3
%% RIGHT — plain English, no special characters
A->>B: Call web_search with queries
B->>B: User removes query 2, keeps 1 and 3
```
### Layout Direction: TD vs LR
`flowchart LR` (left-to-right) spreads horizontally. With many nodes, Mermaid scales everything down to fit the width, making text unreadable. `flowchart TD` (top-down) is almost always better.
| Direction | Use when | Avoid when |
|-----------|----------|------------|
| `TD` (top-down) | Complex diagrams, 5+ nodes, hierarchies | Simple A→B→C linear flows |
| `LR` (left-to-right) | Simple linear flows, 3-4 nodes | Complex graphs, many branches |
**Rule of thumb:** If the diagram has more than one row of nodes or any branching, use `TD`.
### Diagram Type Examples
**Flowchart with decisions:**
```html
<pre class="mermaid">
graph TD
A[Request] --> B{Authenticated?}
B -->|Yes| C[Load Dashboard]
B -->|No| D[Login Page]
D --> E[Submit Credentials]
E --> B
C --> F{Role?}
F -->|Admin| G[Admin Panel]
F -->|User| H[User Dashboard]
</pre>
```
**Sequence diagram:**
```html
<pre class="mermaid">
sequenceDiagram
participant C as Client
participant G as Gateway
participant S as Service
participant D as Database
C->>G: POST /api/data
G->>G: Validate JWT
G->>S: Forward request
S->>D: Query
D-->>S: Results
S-->>G: Response
G-->>C: 200 OK
</pre>
```
**ER diagram:**
```html
<pre class="mermaid">
erDiagram
USERS ||--o{ ORDERS : places
ORDERS ||--|{ LINE_ITEMS : contains
LINE_ITEMS }o--|| PRODUCTS : references
USERS { string email PK }
ORDERS { int id PK }
LINE_ITEMS { int quantity }
PRODUCTS { string name }
</pre>
```
**State diagram:**
```html
<pre class="mermaid">
stateDiagram-v2
[*] --> Draft
Draft --> Review : submit
Review --> Approved : approve
Review --> Draft : request_changes
Approved --> Published : publish
Published --> Archived : archive
Archived --> [*]
</pre>
```
**Mind map:**
```html
<pre class="mermaid">
mindmap
root((Project))
Frontend
React
Next.js
Tailwind
Backend
Node.js
PostgreSQL
Redis
Infrastructure
AWS
Docker
Terraform
</pre>
```
**Class diagram:**
```html
<pre class="mermaid">
classDiagram
class User {
+string email
+string name
+login()
+logout()
}
class Order {
+int id
+decimal total
+submit()
}
class Product {
+string name
+decimal price
}
User "1" --> "*" Order : places
Order "*" --> "*" Product : contains
</pre>
```
**C4 architecture (flowchart-as-C4):**
```html
<pre class="mermaid">
graph TD
user("User<br/><small>Browser client</small>")
subgraph boundary["Web Platform"]
app["Web App<br/><small>Node.js</small>"]
db[("Database<br/><small>PostgreSQL</small>")]
end
email["Email Service"]:::ext
payment["Payment Gateway"]:::ext
user -->|"HTTPS"| app
app -->|"SQL"| db
app -->|"SMTP"| email
app -->|"API"| payment
classDef ext fill:none,stroke-dasharray:5 5
</pre>
```
Do NOT use native `C4Context` / `C4Container` syntax — it hardcodes sharp corners, its own font, and inline colors that ignore `themeVariables`. Use `graph TD` + `subgraph` for C4 boundaries instead.
### Which Mermaid Diagram Type?
| You want to show... | Use | Syntax keyword |
|---|---|---|
| Process flow, decisions, pipelines | Flowchart | `graph TD` / `graph LR` |
| Request/response, API calls, temporal interactions | Sequence diagram | `sequenceDiagram` |
| Database tables and relationships | ER diagram | `erDiagram` |
| OOP classes, domain models with methods | Class diagram | `classDiagram` |
| System architecture at multiple zoom levels | C4 diagram | `graph TD` + `subgraph` |
| State transitions, lifecycles | State diagram | `stateDiagram-v2` |
| Hierarchical breakdowns, brainstorms | Mind map | `mindmap` |
### Dark Mode Handling
Mermaid initializes once — it can't reactively switch themes. Read the preference at load time inside your `<script type="module">`:
```javascript
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
// Use isDark to pick light or dark values in themeVariables
```
The CSS overrides on the container (`.mermaid-wrap`) and page will still respond to `prefers-color-scheme` normally — only the Mermaid SVG internals are static.
---
## Chart.js — Data Visualizations
Use for bar charts, line charts, pie/doughnut charts, radar charts, and other data-driven visualizations in dashboard-type diagrams. Overkill for static numbers — use pure SVG/CSS for simple progress bars and sparklines.
```html
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<canvas id="myChart" width="600" height="300"></canvas>
<script>
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const textColor = isDark ? '#8b949e' : '#6b7280';
const gridColor = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';
const fontFamily = getComputedStyle(document.documentElement)
.getPropertyValue('--font-body').trim() || 'system-ui, sans-serif';
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Feedback Items',
data: [45, 62, 78, 91, 120],
backgroundColor: isDark ? 'rgba(129, 140, 248, 0.6)' : 'rgba(79, 70, 229, 0.6)',
borderColor: isDark ? '#818cf8' : '#4f46e5',
borderWidth: 1,
borderRadius: 4,
}]
},
options: {
responsive: true,
plugins: {
legend: { labels: { color: textColor, font: { family: fontFamily } } },
},
scales: {
x: { ticks: { color: textColor, font: { family: fontFamily } }, grid: { color: gridColor } },
y: { ticks: { color: textColor, font: { family: fontFamily } }, grid: { color: gridColor } },
}
}
});
</script>
```
Wrap the canvas in a styled container:
```css
.chart-container {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 20px;
position: relative;
}
.chart-container canvas {
max-height: 300px;
}
```
---
## anime.js — Orchestrated Animations
Use when a diagram has 10+ elements and you want a choreographed entrance sequence (staggered reveals, path drawing, count-up numbers). For simpler diagrams, CSS `animation-delay` staggering is sufficient.
```html
<script src="https://cdn.jsdelivr.net/npm/animejs@3.2.2/lib/anime.min.js"></script>
<script>
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReduced) {
anime({
targets: '.ve-card',
opacity: [0, 1],
translateY: [20, 0],
delay: anime.stagger(80, { start: 200 }),
easing: 'easeOutCubic',
duration: 500,
});
anime({
targets: '.connector path',
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutCubic',
duration: 800,
delay: anime.stagger(150, { start: 600 }),
});
document.querySelectorAll('[data-count]').forEach(el => {
anime({
targets: { val: 0 },
val: parseInt(el.dataset.count),
round: 1,
duration: 1200,
delay: 400,
easing: 'easeOutExpo',
update: (anim) => { el.textContent = anim.animations[0].currentValue; }
});
});
}
</script>
```
When using anime.js, set initial opacity to 0 in CSS so elements don't flash before the animation:
```css
.ve-card { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
.ve-card { opacity: 1 !important; }
}
```
---
## Google Fonts — Typography
Always load with `display=swap` for fast rendering. Pick a distinctive pairing — body + mono at minimum, optionally a display font for the title.
**FORBIDDEN as `--font-body` (AI slop signals):**
- Inter — the single most overused AI default font
- Roboto — generic Android/Google default
- Arial, Helvetica — system defaults with no character
- system-ui alone without a named font — signals zero design intent
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
```
Define as CSS variables for easy reference:
```css
:root {
--font-body: 'Outfit', system-ui, sans-serif;
--font-mono: 'Space Mono', 'SF Mono', Consolas, monospace;
}
```
**Font pairings** (rotate — never use the same pairing twice in a row):
| Body / Headings | Mono / Labels | Feel | Use for |
|---|---|---|---|
| DM Sans | Fira Code | Friendly, developer | Blueprint, technical docs |
| Instrument Serif | JetBrains Mono | Editorial, refined | Plan reviews, decision logs |
| IBM Plex Sans | IBM Plex Mono | Reliable, readable | Architecture diagrams |
| Bricolage Grotesque | Fragment Mono | Bold, characterful | Data tables, dashboards |
| Plus Jakarta Sans | Azeret Mono | Rounded, approachable | Status reports, audits |
| Outfit | Space Mono | Clean geometric, modern | Flowcharts, pipelines |
| Sora | IBM Plex Mono | Technical, precise | ER diagrams, schemas |
| Crimson Pro | Noto Sans Mono | Scholarly, serious | RFC reviews, specs |
| Fraunces | Source Code Pro | Warm, distinctive | Project recaps |
| Geist | Geist Mono | Vercel-inspired, sharp | Modern API docs |
| Red Hat Display | Red Hat Mono | Cohesive family | System overviews |
| Libre Franklin | Inconsolata | Classic, reliable | Data-dense tables |
| Playfair Display | Roboto Mono | Elegant contrast | Executive summaries |
The first 5 pairings are recommended for most use cases. Vary across consecutive diagrams.
### Typography by Content Voice
For prose-heavy pages (documentation, articles, essays), match typography to the content's voice:
| Voice | Fonts | Best For |
|-------|-------|----------|
| **Literary / Thoughtful** | Literata, Lora, Newsreader, Merriweather | Essays, personal posts, long-form articles |
| **Technical / Precise** | IBM Plex Sans + Mono, Geist + Geist Mono, Source family | Documentation, READMEs, API references |
| **Bold / Contemporary** | Bricolage Grotesque, Space Grotesk, DM Sans | Product pages, feature announcements |
| **Minimal / Focused** | Source Serif 4 + Source Sans 3, Karla + Inconsolata | Tutorials, how-tos, focused reading |
**Literata** deserves special mention — it has optical sizing designed specifically for screen reading. Google's answer to Georgia, but modernized.

View File

@@ -0,0 +1,212 @@
# Responsive Section Navigation
Navigation pattern for multi-section pages (reviews, recaps, dashboards). Provides a sticky sidebar TOC on desktop and a sticky horizontal scrollable bar on mobile.
## Layout Structure
The page uses a two-column CSS Grid: sidebar (TOC) + main content. On mobile it collapses to single-column with the TOC becoming a horizontal bar.
```html
<body>
<div class="wrap">
<nav class="toc" id="toc">
<div class="toc-title">Contents</div>
<a href="#s1">1. First Section</a>
<a href="#s2">2. Second Section</a>
<!-- one link per section -->
</nav>
<div class="main">
<h1>Page Title</h1>
<p class="subtitle">Subtitle text</p>
<div id="s1" class="sec-head ...">1 — First Section</div>
<!-- section content -->
<div id="s2" class="sec-head ...">2 — Second Section</div>
<!-- section content -->
</div><!-- /main -->
</div><!-- /wrap -->
</body>
```
Key structural rules:
- `<nav class="toc">` is the **first child** of `.wrap`
- All page content goes inside `<div class="main">`
- Every section heading gets an `id="s1"`, `id="s2"`, etc.
- TOC links use `href="#s1"` matching those IDs
- Keep TOC link text short (truncate long section names)
## CSS
### Wrap (grid layout)
```css
.wrap {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 170px 1fr;
gap: 0 40px;
}
.main { min-width: 0; }
```
### TOC — Desktop (sticky sidebar)
```css
.toc {
position: sticky;
top: 24px;
align-self: start;
padding: 14px 0;
grid-row: 1 / -1;
max-height: calc(100dvh - 48px);
overflow-y: auto;
}
.toc::-webkit-scrollbar { width: 3px; }
.toc::-webkit-scrollbar-thumb { background: var(--surface-elevated); border-radius: 2px; }
.toc-title {
font-family: var(--font-mono);
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--text-dim);
padding: 0 0 10px;
margin-bottom: 8px;
border-bottom: 1px solid var(--border);
}
.toc a {
display: block;
font-size: 11px;
color: var(--text-dim);
text-decoration: none;
padding: 4px 8px;
border-radius: 5px;
border-left: 2px solid transparent;
transition: all 0.15s;
line-height: 1.4;
margin-bottom: 1px;
}
.toc a:hover { color: var(--text); background: var(--surface2); }
.toc a.active { color: var(--text); border-left-color: var(--accent); }
```
Replace `var(--accent)` with your page's primary accent color variable (e.g., `var(--orange)`, `var(--blue)`).
### TOC — Mobile (sticky horizontal bar)
```css
@media (max-width: 1000px) {
.wrap { grid-template-columns: 1fr; padding-top: 0; }
body { padding-top: 0; }
.toc {
position: sticky;
top: 0;
z-index: 200;
max-height: none;
display: flex;
gap: 4px;
align-items: center;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
background: var(--bg);
border-bottom: 1px solid var(--border);
padding: 10px 0;
margin: 0 -40px;
padding-left: 40px;
padding-right: 40px;
grid-row: auto;
}
.toc::-webkit-scrollbar { display: none; }
.toc-title { display: none; }
.toc a {
white-space: nowrap;
flex-shrink: 0;
border-left: none;
border-bottom: 2px solid transparent;
border-radius: 4px 4px 0 0;
padding: 6px 10px;
font-size: 10px;
}
.toc a.active {
border-left: none;
border-bottom-color: var(--accent);
background: var(--surface);
}
.main { padding-top: 20px; }
/* Offset scroll target so headings clear the sticky bar */
.sec-head { scroll-margin-top: 52px; }
}
```
Adjust `margin: 0 -40px` and `padding-left/right: 40px` to match your `body` padding so the bar bleeds edge-to-edge.
## JavaScript — Scroll Spy
Place before `</body>`, after any Mermaid init:
```html
<script>
(function() {
const toc = document.getElementById('toc');
const links = toc.querySelectorAll('a');
const sections = [];
links.forEach(link => {
const id = link.getAttribute('href').slice(1);
const el = document.getElementById(id);
if (el) sections.push({ id, el, link });
});
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
links.forEach(l => l.classList.remove('active'));
const match = sections.find(s => s.el === entry.target);
if (match) {
match.link.classList.add('active');
// On mobile, auto-scroll the active tab into view
if (window.innerWidth <= 1000) {
match.link.scrollIntoView({
behavior: 'smooth', block: 'nearest', inline: 'center'
});
}
}
}
});
}, { rootMargin: '-10% 0px -80% 0px' });
sections.forEach(s => observer.observe(s.el));
links.forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
const id = link.getAttribute('href').slice(1);
const el = document.getElementById(id);
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
history.replaceState(null, '', '#' + id);
}
});
});
})();
</script>
```
## Adaptation Notes
- The `.toc-title` text, link labels, accent color, and section IDs change per page. Everything else is copy-paste.
- For pages with fewer than 4 sections, skip the TOC entirely — it adds clutter without value.
- The `grid-template-columns: 170px 1fr` width works for most TOCs. If section names are longer, go up to `200px`.
- The `rootMargin: '-10% 0px -80% 0px'` means a section is "active" when its heading enters the top 10-20% of the viewport. This works well with sticky headers.
- On mobile, the horizontal bar uses `overflow-x: auto` with hidden scrollbar. The active tab auto-scrolls into the center of the bar as the user scrolls the page.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
# View Mode
## Execution
**IMPORTANT:** Run server as Claude Code background task using `run_in_background: true` with the Bash tool.
The skill is located at `.opencode/skills/markdown-novel-viewer/`.
### Stop Server
If `--stop` flag is provided:
```bash
node .opencode/skills/markdown-novel-viewer/scripts/server.cjs --stop
```
### Start Server
Run the `markdown-novel-viewer` server as CC background task with `--foreground` flag:
```bash
INPUT_PATH="<resolved-path>"
if [[ -d "$INPUT_PATH" ]]; then
node .opencode/skills/markdown-novel-viewer/scripts/server.cjs \
--dir "$INPUT_PATH" --host 0.0.0.0 --open --foreground
else
node .opencode/skills/markdown-novel-viewer/scripts/server.cjs \
--file "$INPUT_PATH" --host 0.0.0.0 --open --foreground
fi
```
**Critical:** When calling the Bash tool:
- Set `run_in_background: true`
- Set `timeout: 300000` (5 minutes)
- Parse JSON output and report URL to user
After starting, report:
- Local URL for browser access
- Network URL for remote device access
- Inform user that server is now running as CC background task (visible in `/tasks`)
**CRITICAL:** MUST display the FULL URL including path and query string. NEVER truncate to just `host:port`.