forge-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/worktree-create.sh +64 -0
- package/.claude/hooks/worktree-remove.sh +57 -0
- package/.claude/settings.local.json +29 -0
- package/.forge/knowledge/conventions.yaml +1 -0
- package/.forge/knowledge/decisions.yaml +1 -0
- package/.forge/knowledge/gotchas.yaml +1 -0
- package/.forge/knowledge/patterns.yaml +1 -0
- package/.forge/manifest.yaml +6 -0
- package/CLAUDE.md +144 -0
- package/bin/setup-forge.sh +132 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +553 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/codebase.d.ts +57 -0
- package/dist/context/codebase.d.ts.map +1 -0
- package/dist/context/codebase.js +301 -0
- package/dist/context/codebase.js.map +1 -0
- package/dist/context/injector.d.ts +147 -0
- package/dist/context/injector.d.ts.map +1 -0
- package/dist/context/injector.js +533 -0
- package/dist/context/injector.js.map +1 -0
- package/dist/context/memory.d.ts +32 -0
- package/dist/context/memory.d.ts.map +1 -0
- package/dist/context/memory.js +140 -0
- package/dist/context/memory.js.map +1 -0
- package/dist/context/session-index.d.ts +54 -0
- package/dist/context/session-index.d.ts.map +1 -0
- package/dist/context/session-index.js +265 -0
- package/dist/context/session-index.js.map +1 -0
- package/dist/context/session.d.ts +42 -0
- package/dist/context/session.d.ts.map +1 -0
- package/dist/context/session.js +121 -0
- package/dist/context/session.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/ingestion/chunker.d.ts +19 -0
- package/dist/ingestion/chunker.d.ts.map +1 -0
- package/dist/ingestion/chunker.js +189 -0
- package/dist/ingestion/chunker.js.map +1 -0
- package/dist/ingestion/embedder.d.ts +45 -0
- package/dist/ingestion/embedder.d.ts.map +1 -0
- package/dist/ingestion/embedder.js +152 -0
- package/dist/ingestion/embedder.js.map +1 -0
- package/dist/ingestion/git-analyzer.d.ts +77 -0
- package/dist/ingestion/git-analyzer.d.ts.map +1 -0
- package/dist/ingestion/git-analyzer.js +437 -0
- package/dist/ingestion/git-analyzer.js.map +1 -0
- package/dist/ingestion/indexer.d.ts +79 -0
- package/dist/ingestion/indexer.d.ts.map +1 -0
- package/dist/ingestion/indexer.js +766 -0
- package/dist/ingestion/indexer.js.map +1 -0
- package/dist/ingestion/markdown-chunker.d.ts +19 -0
- package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
- package/dist/ingestion/markdown-chunker.js +243 -0
- package/dist/ingestion/markdown-chunker.js.map +1 -0
- package/dist/ingestion/markdown-knowledge.d.ts +21 -0
- package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
- package/dist/ingestion/markdown-knowledge.js +129 -0
- package/dist/ingestion/markdown-knowledge.js.map +1 -0
- package/dist/ingestion/parser.d.ts +20 -0
- package/dist/ingestion/parser.d.ts.map +1 -0
- package/dist/ingestion/parser.js +429 -0
- package/dist/ingestion/parser.js.map +1 -0
- package/dist/ingestion/watcher.d.ts +28 -0
- package/dist/ingestion/watcher.d.ts.map +1 -0
- package/dist/ingestion/watcher.js +147 -0
- package/dist/ingestion/watcher.js.map +1 -0
- package/dist/knowledge/hydrator.d.ts +37 -0
- package/dist/knowledge/hydrator.d.ts.map +1 -0
- package/dist/knowledge/hydrator.js +220 -0
- package/dist/knowledge/hydrator.js.map +1 -0
- package/dist/knowledge/registry.d.ts +129 -0
- package/dist/knowledge/registry.d.ts.map +1 -0
- package/dist/knowledge/registry.js +361 -0
- package/dist/knowledge/registry.js.map +1 -0
- package/dist/knowledge/search.d.ts +114 -0
- package/dist/knowledge/search.d.ts.map +1 -0
- package/dist/knowledge/search.js +428 -0
- package/dist/knowledge/search.js.map +1 -0
- package/dist/knowledge/store.d.ts +76 -0
- package/dist/knowledge/store.d.ts.map +1 -0
- package/dist/knowledge/store.js +230 -0
- package/dist/knowledge/store.js.map +1 -0
- package/dist/learning/confidence.d.ts +30 -0
- package/dist/learning/confidence.d.ts.map +1 -0
- package/dist/learning/confidence.js +165 -0
- package/dist/learning/confidence.js.map +1 -0
- package/dist/learning/patterns.d.ts +52 -0
- package/dist/learning/patterns.d.ts.map +1 -0
- package/dist/learning/patterns.js +290 -0
- package/dist/learning/patterns.js.map +1 -0
- package/dist/learning/trajectory.d.ts +55 -0
- package/dist/learning/trajectory.d.ts.map +1 -0
- package/dist/learning/trajectory.js +200 -0
- package/dist/learning/trajectory.js.map +1 -0
- package/dist/memory/memory-compat.d.ts +100 -0
- package/dist/memory/memory-compat.d.ts.map +1 -0
- package/dist/memory/memory-compat.js +146 -0
- package/dist/memory/memory-compat.js.map +1 -0
- package/dist/memory/observation-store.d.ts +57 -0
- package/dist/memory/observation-store.d.ts.map +1 -0
- package/dist/memory/observation-store.js +154 -0
- package/dist/memory/observation-store.js.map +1 -0
- package/dist/memory/session-tracker.d.ts +81 -0
- package/dist/memory/session-tracker.d.ts.map +1 -0
- package/dist/memory/session-tracker.js +262 -0
- package/dist/memory/session-tracker.js.map +1 -0
- package/dist/pipeline/engine.d.ts +179 -0
- package/dist/pipeline/engine.d.ts.map +1 -0
- package/dist/pipeline/engine.js +691 -0
- package/dist/pipeline/engine.js.map +1 -0
- package/dist/pipeline/events.d.ts +54 -0
- package/dist/pipeline/events.d.ts.map +1 -0
- package/dist/pipeline/events.js +157 -0
- package/dist/pipeline/events.js.map +1 -0
- package/dist/pipeline/parallel.d.ts +83 -0
- package/dist/pipeline/parallel.d.ts.map +1 -0
- package/dist/pipeline/parallel.js +277 -0
- package/dist/pipeline/parallel.js.map +1 -0
- package/dist/pipeline/state-machine.d.ts +65 -0
- package/dist/pipeline/state-machine.d.ts.map +1 -0
- package/dist/pipeline/state-machine.js +176 -0
- package/dist/pipeline/state-machine.js.map +1 -0
- package/dist/query/graph-queries.d.ts +84 -0
- package/dist/query/graph-queries.d.ts.map +1 -0
- package/dist/query/graph-queries.js +216 -0
- package/dist/query/graph-queries.js.map +1 -0
- package/dist/query/hybrid-search.d.ts +34 -0
- package/dist/query/hybrid-search.d.ts.map +1 -0
- package/dist/query/hybrid-search.js +263 -0
- package/dist/query/hybrid-search.js.map +1 -0
- package/dist/query/intent-detector.d.ts +35 -0
- package/dist/query/intent-detector.d.ts.map +1 -0
- package/dist/query/intent-detector.js +115 -0
- package/dist/query/intent-detector.js.map +1 -0
- package/dist/query/ranking.d.ts +57 -0
- package/dist/query/ranking.d.ts.map +1 -0
- package/dist/query/ranking.js +109 -0
- package/dist/query/ranking.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +291 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/falkordb-store.d.ts +73 -0
- package/dist/storage/falkordb-store.d.ts.map +1 -0
- package/dist/storage/falkordb-store.js +346 -0
- package/dist/storage/falkordb-store.js.map +1 -0
- package/dist/storage/file-cache.d.ts +32 -0
- package/dist/storage/file-cache.d.ts.map +1 -0
- package/dist/storage/file-cache.js +115 -0
- package/dist/storage/file-cache.js.map +1 -0
- package/dist/storage/interfaces.d.ts +151 -0
- package/dist/storage/interfaces.d.ts.map +1 -0
- package/dist/storage/interfaces.js +7 -0
- package/dist/storage/interfaces.js.map +1 -0
- package/dist/storage/qdrant-store.d.ts +110 -0
- package/dist/storage/qdrant-store.d.ts.map +1 -0
- package/dist/storage/qdrant-store.js +467 -0
- package/dist/storage/qdrant-store.js.map +1 -0
- package/dist/storage/schema.d.ts +4 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +136 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts +35 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +132 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/tools/collaboration-tools.d.ts +111 -0
- package/dist/tools/collaboration-tools.d.ts.map +1 -0
- package/dist/tools/collaboration-tools.js +174 -0
- package/dist/tools/collaboration-tools.js.map +1 -0
- package/dist/tools/context-tools.d.ts +293 -0
- package/dist/tools/context-tools.d.ts.map +1 -0
- package/dist/tools/context-tools.js +437 -0
- package/dist/tools/context-tools.js.map +1 -0
- package/dist/tools/graph-tools.d.ts +129 -0
- package/dist/tools/graph-tools.d.ts.map +1 -0
- package/dist/tools/graph-tools.js +237 -0
- package/dist/tools/graph-tools.js.map +1 -0
- package/dist/tools/ingestion-tools.d.ts +96 -0
- package/dist/tools/ingestion-tools.d.ts.map +1 -0
- package/dist/tools/ingestion-tools.js +90 -0
- package/dist/tools/ingestion-tools.js.map +1 -0
- package/dist/tools/learning-tools.d.ts +168 -0
- package/dist/tools/learning-tools.d.ts.map +1 -0
- package/dist/tools/learning-tools.js +158 -0
- package/dist/tools/learning-tools.js.map +1 -0
- package/dist/tools/memory-tools.d.ts +183 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +197 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/phase-tools.d.ts +954 -0
- package/dist/tools/phase-tools.d.ts.map +1 -0
- package/dist/tools/phase-tools.js +1215 -0
- package/dist/tools/phase-tools.js.map +1 -0
- package/dist/tools/pipeline-tools.d.ts +140 -0
- package/dist/tools/pipeline-tools.d.ts.map +1 -0
- package/dist/tools/pipeline-tools.js +162 -0
- package/dist/tools/pipeline-tools.js.map +1 -0
- package/dist/tools/registration-tools.d.ts +220 -0
- package/dist/tools/registration-tools.d.ts.map +1 -0
- package/dist/tools/registration-tools.js +391 -0
- package/dist/tools/registration-tools.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +75 -0
- package/dist/util/circuit-breaker.d.ts.map +1 -0
- package/dist/util/circuit-breaker.js +159 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/config.d.ts +23 -0
- package/dist/util/config.d.ts.map +1 -0
- package/dist/util/config.js +164 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/logger.d.ts +13 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +45 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/token-counter.d.ts +24 -0
- package/dist/util/token-counter.d.ts.map +1 -0
- package/dist/util/token-counter.js +48 -0
- package/dist/util/token-counter.js.map +1 -0
- package/dist/util/types.d.ts +525 -0
- package/dist/util/types.d.ts.map +1 -0
- package/dist/util/types.js +5 -0
- package/dist/util/types.js.map +1 -0
- package/docker-compose.yml +20 -0
- package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
- package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
- package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
- package/package.json +39 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/.mcp.json +15 -0
- package/plugin/README.md +134 -0
- package/plugin/agents/architect.md +367 -0
- package/plugin/agents/backend-specialist.md +263 -0
- package/plugin/agents/brainstormer.md +122 -0
- package/plugin/agents/data-specialist.md +266 -0
- package/plugin/agents/designer.md +408 -0
- package/plugin/agents/frontend-specialist.md +241 -0
- package/plugin/agents/inspector.md +406 -0
- package/plugin/agents/knowledge-keeper.md +443 -0
- package/plugin/agents/platform-engineer.md +326 -0
- package/plugin/agents/product-manager.md +268 -0
- package/plugin/agents/product-owner.md +438 -0
- package/plugin/agents/pulse-checker.md +73 -0
- package/plugin/agents/qa-strategist.md +500 -0
- package/plugin/agents/self-improver.md +310 -0
- package/plugin/agents/strategist.md +360 -0
- package/plugin/agents/supervisor.md +380 -0
- package/plugin/commands/brainstorm.md +25 -0
- package/plugin/commands/forge.md +88 -0
- package/plugin/docs/atlassian-integration.md +110 -0
- package/plugin/docs/workflow.md +126 -0
- package/plugin/skills/agent-development/.skillfish.json +10 -0
- package/plugin/skills/agent-development/SKILL.md +415 -0
- package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
- package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
- package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
- package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
- package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
- package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
- package/plugin/skills/agent-handoff/SKILL.md +335 -0
- package/plugin/skills/anti-stub/SKILL.md +317 -0
- package/plugin/skills/brainstorm/SKILL.md +31 -0
- package/plugin/skills/debugging/SKILL.md +276 -0
- package/plugin/skills/fix/SKILL.md +62 -0
- package/plugin/skills/frontend-design/.skillfish.json +10 -0
- package/plugin/skills/frontend-design/SKILL.md +42 -0
- package/plugin/skills/gotchas/SKILL.md +61 -0
- package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
- package/plugin/skills/history/SKILL.md +58 -0
- package/plugin/skills/impact/SKILL.md +59 -0
- package/plugin/skills/implementation-execution/SKILL.md +291 -0
- package/plugin/skills/index-repo/SKILL.md +55 -0
- package/plugin/skills/interviewing/SKILL.md +225 -0
- package/plugin/skills/knowledge-curation/SKILL.md +393 -0
- package/plugin/skills/learn/SKILL.md +69 -0
- package/plugin/skills/mcp-integration/.skillfish.json +10 -0
- package/plugin/skills/mcp-integration/SKILL.md +554 -0
- package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
- package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
- package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
- package/plugin/skills/mcp-integration/references/authentication.md +549 -0
- package/plugin/skills/mcp-integration/references/server-types.md +536 -0
- package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
- package/plugin/skills/nestjs/.skillfish.json +10 -0
- package/plugin/skills/nestjs/SKILL.md +669 -0
- package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
- package/plugin/skills/nestjs/reference.md +1447 -0
- package/plugin/skills/nestjs/workflow-optimization.md +229 -0
- package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
- package/plugin/skills/project-discovery/SKILL.md +304 -0
- package/plugin/skills/search/SKILL.md +56 -0
- package/plugin/skills/security-audit/SKILL.md +362 -0
- package/plugin/skills/skill-development/.skillfish.json +10 -0
- package/plugin/skills/skill-development/SKILL.md +637 -0
- package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
- package/plugin/skills/tdd/SKILL.md +273 -0
- package/plugin/skills/terminal-presentation/SKILL.md +395 -0
- package/plugin/skills/test-strategy/SKILL.md +365 -0
- package/plugin/skills/verification-protocol/SKILL.md +256 -0
- package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
- package/plugin/skills/visual-explainer/LICENSE +21 -0
- package/plugin/skills/visual-explainer/README.md +137 -0
- package/plugin/skills/visual-explainer/SKILL.md +352 -0
- package/plugin/skills/visual-explainer/banner.png +0 -0
- package/plugin/skills/visual-explainer/package.json +11 -0
- package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
- package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
- package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
- package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
- package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
- package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
- package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
- package/plugin/skills/visual-explainer/references/libraries.md +470 -0
- package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
- package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
- package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
- package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
- package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
- package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
- package/src/cli.ts +655 -0
- package/src/context/.gitkeep +0 -0
- package/src/context/codebase.ts +393 -0
- package/src/context/injector.ts +797 -0
- package/src/context/memory.ts +187 -0
- package/src/context/session-index.ts +327 -0
- package/src/context/session.ts +152 -0
- package/src/index.ts +47 -0
- package/src/ingestion/.gitkeep +0 -0
- package/src/ingestion/chunker.ts +277 -0
- package/src/ingestion/embedder.ts +167 -0
- package/src/ingestion/git-analyzer.ts +545 -0
- package/src/ingestion/indexer.ts +984 -0
- package/src/ingestion/markdown-chunker.ts +337 -0
- package/src/ingestion/markdown-knowledge.ts +175 -0
- package/src/ingestion/parser.ts +475 -0
- package/src/ingestion/watcher.ts +182 -0
- package/src/knowledge/.gitkeep +0 -0
- package/src/knowledge/hydrator.ts +246 -0
- package/src/knowledge/registry.ts +463 -0
- package/src/knowledge/search.ts +565 -0
- package/src/knowledge/store.ts +262 -0
- package/src/learning/.gitkeep +0 -0
- package/src/learning/confidence.ts +193 -0
- package/src/learning/patterns.ts +360 -0
- package/src/learning/trajectory.ts +268 -0
- package/src/memory/.gitkeep +0 -0
- package/src/memory/memory-compat.ts +233 -0
- package/src/memory/observation-store.ts +224 -0
- package/src/memory/session-tracker.ts +332 -0
- package/src/pipeline/.gitkeep +0 -0
- package/src/pipeline/engine.ts +1139 -0
- package/src/pipeline/events.ts +253 -0
- package/src/pipeline/parallel.ts +394 -0
- package/src/pipeline/state-machine.ts +199 -0
- package/src/query/.gitkeep +0 -0
- package/src/query/graph-queries.ts +262 -0
- package/src/query/hybrid-search.ts +337 -0
- package/src/query/intent-detector.ts +131 -0
- package/src/query/ranking.ts +161 -0
- package/src/server.ts +352 -0
- package/src/storage/.gitkeep +0 -0
- package/src/storage/falkordb-store.ts +388 -0
- package/src/storage/file-cache.ts +141 -0
- package/src/storage/interfaces.ts +201 -0
- package/src/storage/qdrant-store.ts +557 -0
- package/src/storage/schema.ts +139 -0
- package/src/storage/sqlite.ts +168 -0
- package/src/tools/.gitkeep +0 -0
- package/src/tools/collaboration-tools.ts +208 -0
- package/src/tools/context-tools.ts +493 -0
- package/src/tools/graph-tools.ts +295 -0
- package/src/tools/ingestion-tools.ts +122 -0
- package/src/tools/learning-tools.ts +181 -0
- package/src/tools/memory-tools.ts +234 -0
- package/src/tools/phase-tools.ts +1452 -0
- package/src/tools/pipeline-tools.ts +188 -0
- package/src/tools/registration-tools.ts +450 -0
- package/src/util/.gitkeep +0 -0
- package/src/util/circuit-breaker.ts +193 -0
- package/src/util/config.ts +177 -0
- package/src/util/logger.ts +53 -0
- package/src/util/token-counter.ts +52 -0
- package/src/util/types.ts +710 -0
- package/tests/context/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/knowledge/.gitkeep +0 -0
- package/tests/learning/.gitkeep +0 -0
- package/tests/pipeline/.gitkeep +0 -0
- package/tests/tools/.gitkeep +0 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +10 -0
- package/vscode-extension/.vscodeignore +7 -0
- package/vscode-extension/README.md +43 -0
- package/vscode-extension/out/edge-collector.js +274 -0
- package/vscode-extension/out/edge-collector.js.map +1 -0
- package/vscode-extension/out/extension.js +264 -0
- package/vscode-extension/out/extension.js.map +1 -0
- package/vscode-extension/out/forge-client.js +318 -0
- package/vscode-extension/out/forge-client.js.map +1 -0
- package/vscode-extension/package-lock.json +59 -0
- package/vscode-extension/package.json +71 -0
- package/vscode-extension/src/edge-collector.ts +320 -0
- package/vscode-extension/src/extension.ts +269 -0
- package/vscode-extension/src/forge-client.ts +364 -0
- package/vscode-extension/tsconfig.json +19 -0
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
# CSS Patterns for Diagrams
|
|
2
|
+
|
|
3
|
+
Reusable patterns for layout, connectors, theming, and visual effects in self-contained HTML diagrams.
|
|
4
|
+
|
|
5
|
+
## Theme Setup
|
|
6
|
+
|
|
7
|
+
Always define both light and dark palettes via custom properties. Start with whichever fits the chosen aesthetic, ensure both work.
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
:root {
|
|
11
|
+
--font-body: 'Outfit', system-ui, sans-serif;
|
|
12
|
+
--font-mono: 'Space Mono', 'SF Mono', Consolas, monospace;
|
|
13
|
+
|
|
14
|
+
--bg: #f8f9fa;
|
|
15
|
+
--surface: #ffffff;
|
|
16
|
+
--surface-elevated: #ffffff;
|
|
17
|
+
--border: rgba(0, 0, 0, 0.08);
|
|
18
|
+
--border-bright: rgba(0, 0, 0, 0.15);
|
|
19
|
+
--text: #1a1a2e;
|
|
20
|
+
--text-dim: #6b7280;
|
|
21
|
+
--accent: #0891b2;
|
|
22
|
+
--accent-dim: rgba(8, 145, 178, 0.1);
|
|
23
|
+
/* Semantic accents for diagram elements */
|
|
24
|
+
--node-a: #0891b2;
|
|
25
|
+
--node-a-dim: rgba(8, 145, 178, 0.1);
|
|
26
|
+
--node-b: #059669;
|
|
27
|
+
--node-b-dim: rgba(5, 150, 105, 0.1);
|
|
28
|
+
--node-c: #d97706;
|
|
29
|
+
--node-c-dim: rgba(217, 119, 6, 0.1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (prefers-color-scheme: dark) {
|
|
33
|
+
:root {
|
|
34
|
+
--bg: #0d1117;
|
|
35
|
+
--surface: #161b22;
|
|
36
|
+
--surface-elevated: #1c2333;
|
|
37
|
+
--border: rgba(255, 255, 255, 0.06);
|
|
38
|
+
--border-bright: rgba(255, 255, 255, 0.12);
|
|
39
|
+
--text: #e6edf3;
|
|
40
|
+
--text-dim: #8b949e;
|
|
41
|
+
--accent: #22d3ee;
|
|
42
|
+
--accent-dim: rgba(34, 211, 238, 0.12);
|
|
43
|
+
--node-a: #22d3ee;
|
|
44
|
+
--node-a-dim: rgba(34, 211, 238, 0.12);
|
|
45
|
+
--node-b: #34d399;
|
|
46
|
+
--node-b-dim: rgba(52, 211, 153, 0.12);
|
|
47
|
+
--node-c: #fbbf24;
|
|
48
|
+
--node-c-dim: rgba(251, 191, 36, 0.12);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Background Atmosphere
|
|
54
|
+
|
|
55
|
+
Flat backgrounds feel dead. Use subtle gradients or patterns.
|
|
56
|
+
|
|
57
|
+
```css
|
|
58
|
+
/* Radial glow behind focal area */
|
|
59
|
+
body {
|
|
60
|
+
background: var(--bg);
|
|
61
|
+
background-image: radial-gradient(ellipse at 50% 0%, var(--accent-dim) 0%, transparent 60%);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Faint dot grid */
|
|
65
|
+
body {
|
|
66
|
+
background-color: var(--bg);
|
|
67
|
+
background-image: radial-gradient(circle, var(--border) 1px, transparent 1px);
|
|
68
|
+
background-size: 24px 24px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Diagonal subtle lines */
|
|
72
|
+
body {
|
|
73
|
+
background-color: var(--bg);
|
|
74
|
+
background-image: repeating-linear-gradient(
|
|
75
|
+
-45deg, transparent, transparent 40px,
|
|
76
|
+
var(--border) 40px, var(--border) 41px
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Gradient mesh (pick 2-3 positioned radials) */
|
|
81
|
+
body {
|
|
82
|
+
background: var(--bg);
|
|
83
|
+
background-image:
|
|
84
|
+
radial-gradient(at 20% 20%, var(--node-a-dim) 0%, transparent 50%),
|
|
85
|
+
radial-gradient(at 80% 60%, var(--node-b-dim) 0%, transparent 50%);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Section / Card Components
|
|
90
|
+
|
|
91
|
+
The fundamental building block. A colored card representing a system component, pipeline step, or data entity.
|
|
92
|
+
|
|
93
|
+
**IMPORTANT: Never use `.node` as a CSS class name.** Mermaid.js internally uses `.node` on its SVG `<g>` elements with `transform: translate(x, y)` for positioning. Any page-level `.node` styles (hover transforms, box-shadows, transitions) will leak into Mermaid diagrams and break their layout. Use `.ve-card` instead (namespaced to avoid collisions with CSS frameworks like Bootstrap/Tailwind that also use `.card`).
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
.ve-card {
|
|
97
|
+
background: var(--surface);
|
|
98
|
+
border: 1px solid var(--border);
|
|
99
|
+
border-radius: 10px;
|
|
100
|
+
padding: 16px 20px;
|
|
101
|
+
position: relative;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Colored accent border (left or top) */
|
|
105
|
+
.ve-card--accent-a {
|
|
106
|
+
border-left: 3px solid var(--node-a);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* --- Depth tiers: vary card depth to signal importance --- */
|
|
110
|
+
|
|
111
|
+
/* Elevated: KPIs, key sections, anything that should pop */
|
|
112
|
+
.ve-card--elevated {
|
|
113
|
+
background: var(--surface-elevated);
|
|
114
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Recessed: code blocks, secondary content, detail panels */
|
|
118
|
+
.ve-card--recessed {
|
|
119
|
+
background: color-mix(in srgb, var(--bg) 70%, var(--surface) 30%);
|
|
120
|
+
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
121
|
+
border-color: var(--border);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Hero: executive summaries, focal elements — demands attention */
|
|
125
|
+
.ve-card--hero {
|
|
126
|
+
background: color-mix(in srgb, var(--surface) 92%, var(--accent) 8%);
|
|
127
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04);
|
|
128
|
+
border-color: color-mix(in srgb, var(--border) 50%, var(--accent) 50%);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Glass: special-occasion overlay effect (use sparingly) */
|
|
132
|
+
.ve-card--glass {
|
|
133
|
+
background: color-mix(in srgb, var(--surface) 60%, transparent 40%);
|
|
134
|
+
backdrop-filter: blur(12px);
|
|
135
|
+
-webkit-backdrop-filter: blur(12px);
|
|
136
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Section label (monospace, uppercase, small) */
|
|
140
|
+
.ve-card__label {
|
|
141
|
+
font-family: var(--font-mono);
|
|
142
|
+
font-size: 10px;
|
|
143
|
+
font-weight: 600;
|
|
144
|
+
text-transform: uppercase;
|
|
145
|
+
letter-spacing: 1.5px;
|
|
146
|
+
color: var(--node-a);
|
|
147
|
+
margin-bottom: 10px;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Colored dot indicator */
|
|
154
|
+
.ve-card__label::before {
|
|
155
|
+
content: '';
|
|
156
|
+
width: 8px;
|
|
157
|
+
height: 8px;
|
|
158
|
+
border-radius: 50%;
|
|
159
|
+
background: currentColor;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Overflow Protection
|
|
164
|
+
|
|
165
|
+
Grid and flex children default to `min-width: auto`, which prevents them from shrinking below their content width. Long text, inline code badges, and non-wrapping elements will blow out containers.
|
|
166
|
+
|
|
167
|
+
### Global rules
|
|
168
|
+
|
|
169
|
+
```css
|
|
170
|
+
/* Every grid/flex child must be able to shrink */
|
|
171
|
+
.grid > *, .flex > *,
|
|
172
|
+
[style*="display: grid"] > *,
|
|
173
|
+
[style*="display: flex"] > * {
|
|
174
|
+
min-width: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Long text wraps instead of overflowing */
|
|
178
|
+
body {
|
|
179
|
+
overflow-wrap: break-word;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Side-by-side comparison panels
|
|
184
|
+
|
|
185
|
+
```css
|
|
186
|
+
.comparison {
|
|
187
|
+
display: grid;
|
|
188
|
+
grid-template-columns: 1fr 1fr;
|
|
189
|
+
gap: 16px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.comparison > * {
|
|
193
|
+
min-width: 0;
|
|
194
|
+
overflow-wrap: break-word;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@media (max-width: 768px) {
|
|
198
|
+
.comparison { grid-template-columns: 1fr; }
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Never use `display: flex` on `<li>` for marker characters
|
|
203
|
+
|
|
204
|
+
Using `display: flex` on a list item to position a `::before` marker creates an anonymous flex item for the remaining text content. That anonymous flex item gets `min-width: auto` and you **cannot** set `min-width: 0` on anonymous boxes. Lines with many inline `<code>` badges will overflow their container with no CSS fix possible.
|
|
205
|
+
|
|
206
|
+
Use absolute positioning for markers instead:
|
|
207
|
+
|
|
208
|
+
```css
|
|
209
|
+
/* WRONG — causes overflow with inline code badges */
|
|
210
|
+
li {
|
|
211
|
+
display: flex;
|
|
212
|
+
align-items: baseline;
|
|
213
|
+
gap: 6px;
|
|
214
|
+
}
|
|
215
|
+
li::before {
|
|
216
|
+
content: '›';
|
|
217
|
+
flex-shrink: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* RIGHT — text wraps normally */
|
|
221
|
+
li {
|
|
222
|
+
padding-left: 14px;
|
|
223
|
+
position: relative;
|
|
224
|
+
}
|
|
225
|
+
li::before {
|
|
226
|
+
content: '›';
|
|
227
|
+
position: absolute;
|
|
228
|
+
left: 0;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Mermaid Zoom Controls
|
|
233
|
+
|
|
234
|
+
Mermaid diagrams are often too small to read comfortably, especially complex flowcharts and sequence diagrams. Add zoom controls to every `.mermaid-wrap` container.
|
|
235
|
+
|
|
236
|
+
**Centering fix.** Mermaid SVGs render at a fixed size and default to the top-left of their container, leaving dead space in larger containers. Always add `display: flex; align-items: center; justify-content: center;` to `.mermaid-wrap` so the SVG centers regardless of container size. Use `transform-origin: center center` so zoom radiates from the middle.
|
|
237
|
+
|
|
238
|
+
**Small diagrams in slides.** If a diagram has fewer than ~7 nodes with no branching, it will render tiny in a full-viewport slide container. For simple linear flows (A → B → C → D), use CSS pipeline cards instead of Mermaid — see `slide-patterns.md` "CSS Pipeline Slide." Reserve Mermaid for complex graphs where automatic edge routing is actually needed.
|
|
239
|
+
|
|
240
|
+
### CSS
|
|
241
|
+
|
|
242
|
+
```css
|
|
243
|
+
.mermaid-wrap {
|
|
244
|
+
position: relative;
|
|
245
|
+
background: var(--surface);
|
|
246
|
+
border: 1px solid var(--border);
|
|
247
|
+
border-radius: 12px;
|
|
248
|
+
padding: 32px 24px;
|
|
249
|
+
overflow: auto;
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
scrollbar-width: thin;
|
|
254
|
+
scrollbar-color: var(--border) transparent;
|
|
255
|
+
}
|
|
256
|
+
.mermaid-wrap::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
257
|
+
.mermaid-wrap::-webkit-scrollbar-track { background: transparent; }
|
|
258
|
+
.mermaid-wrap::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
259
|
+
.mermaid-wrap::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
|
|
260
|
+
|
|
261
|
+
.mermaid-wrap .mermaid {
|
|
262
|
+
transition: transform 0.2s ease;
|
|
263
|
+
transform-origin: center center;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.zoom-controls {
|
|
267
|
+
position: absolute;
|
|
268
|
+
top: 8px;
|
|
269
|
+
right: 8px;
|
|
270
|
+
display: flex;
|
|
271
|
+
gap: 2px;
|
|
272
|
+
z-index: 10;
|
|
273
|
+
background: var(--surface);
|
|
274
|
+
border: 1px solid var(--border);
|
|
275
|
+
border-radius: 6px;
|
|
276
|
+
padding: 2px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.zoom-controls button {
|
|
280
|
+
width: 28px;
|
|
281
|
+
height: 28px;
|
|
282
|
+
border: none;
|
|
283
|
+
background: transparent;
|
|
284
|
+
color: var(--text-dim);
|
|
285
|
+
font-family: var(--font-mono);
|
|
286
|
+
font-size: 14px;
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
border-radius: 4px;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
justify-content: center;
|
|
292
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.zoom-controls button:hover {
|
|
296
|
+
background: var(--border);
|
|
297
|
+
color: var(--text);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.mermaid-wrap.is-zoomed { cursor: grab; }
|
|
301
|
+
.mermaid-wrap.is-panning { cursor: grabbing; user-select: none; }
|
|
302
|
+
|
|
303
|
+
@media (prefers-reduced-motion: reduce) {
|
|
304
|
+
.mermaid-wrap .mermaid { transition: none; }
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### HTML
|
|
309
|
+
|
|
310
|
+
```html
|
|
311
|
+
<div class="mermaid-wrap">
|
|
312
|
+
<div class="zoom-controls">
|
|
313
|
+
<button onclick="zoomDiagram(this, 1.2)" title="Zoom in">+</button>
|
|
314
|
+
<button onclick="zoomDiagram(this, 0.8)" title="Zoom out">−</button>
|
|
315
|
+
<button onclick="resetZoom(this)" title="Reset zoom">↺</button>
|
|
316
|
+
</div>
|
|
317
|
+
<pre class="mermaid">
|
|
318
|
+
graph TD
|
|
319
|
+
A --> B
|
|
320
|
+
</pre>
|
|
321
|
+
</div>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### JavaScript
|
|
325
|
+
|
|
326
|
+
Add once at the end of the page. Handles button clicks and scroll-to-zoom on all `.mermaid-wrap` containers:
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
function updateZoomState(wrap) {
|
|
330
|
+
var target = wrap.querySelector('.mermaid');
|
|
331
|
+
var zoom = parseFloat(target.dataset.zoom || '1');
|
|
332
|
+
wrap.classList.toggle('is-zoomed', zoom > 1);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function zoomDiagram(btn, factor) {
|
|
336
|
+
var wrap = btn.closest('.mermaid-wrap');
|
|
337
|
+
var target = wrap.querySelector('.mermaid');
|
|
338
|
+
var current = parseFloat(target.dataset.zoom || '1');
|
|
339
|
+
var next = Math.min(Math.max(current * factor, 0.3), 5);
|
|
340
|
+
target.dataset.zoom = next;
|
|
341
|
+
target.style.transform = 'scale(' + next + ')';
|
|
342
|
+
updateZoomState(wrap);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function resetZoom(btn) {
|
|
346
|
+
var wrap = btn.closest('.mermaid-wrap');
|
|
347
|
+
var target = wrap.querySelector('.mermaid');
|
|
348
|
+
target.dataset.zoom = '1';
|
|
349
|
+
target.style.transform = 'scale(1)';
|
|
350
|
+
updateZoomState(wrap);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
document.querySelectorAll('.mermaid-wrap').forEach(function(wrap) {
|
|
354
|
+
// Ctrl/Cmd + scroll to zoom
|
|
355
|
+
wrap.addEventListener('wheel', function(e) {
|
|
356
|
+
if (!e.ctrlKey && !e.metaKey) return;
|
|
357
|
+
e.preventDefault();
|
|
358
|
+
var target = wrap.querySelector('.mermaid');
|
|
359
|
+
var current = parseFloat(target.dataset.zoom || '1');
|
|
360
|
+
var factor = e.deltaY < 0 ? 1.1 : 0.9;
|
|
361
|
+
var next = Math.min(Math.max(current * factor, 0.3), 5);
|
|
362
|
+
target.dataset.zoom = next;
|
|
363
|
+
target.style.transform = 'scale(' + next + ')';
|
|
364
|
+
updateZoomState(wrap);
|
|
365
|
+
}, { passive: false });
|
|
366
|
+
|
|
367
|
+
// Click-and-drag to pan when zoomed
|
|
368
|
+
var startX, startY, scrollL, scrollT;
|
|
369
|
+
wrap.addEventListener('mousedown', function(e) {
|
|
370
|
+
if (e.target.closest('.zoom-controls')) return;
|
|
371
|
+
var target = wrap.querySelector('.mermaid');
|
|
372
|
+
if (parseFloat(target.dataset.zoom || '1') <= 1) return;
|
|
373
|
+
wrap.classList.add('is-panning');
|
|
374
|
+
startX = e.clientX;
|
|
375
|
+
startY = e.clientY;
|
|
376
|
+
scrollL = wrap.scrollLeft;
|
|
377
|
+
scrollT = wrap.scrollTop;
|
|
378
|
+
});
|
|
379
|
+
window.addEventListener('mousemove', function(e) {
|
|
380
|
+
if (!wrap.classList.contains('is-panning')) return;
|
|
381
|
+
wrap.scrollLeft = scrollL - (e.clientX - startX);
|
|
382
|
+
wrap.scrollTop = scrollT - (e.clientY - startY);
|
|
383
|
+
});
|
|
384
|
+
window.addEventListener('mouseup', function() {
|
|
385
|
+
wrap.classList.remove('is-panning');
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Scroll-to-zoom requires Ctrl/Cmd+scroll to avoid hijacking normal page scroll. Click-and-drag panning activates only when zoomed in (zoom > 1). Cursor changes to `grab`/`grabbing` to signal the behavior. The zoom range is capped at 0.3x–5x.
|
|
391
|
+
|
|
392
|
+
## Grid Layouts
|
|
393
|
+
|
|
394
|
+
### Architecture Diagram (2-column with sidebar)
|
|
395
|
+
```css
|
|
396
|
+
.arch-grid {
|
|
397
|
+
display: grid;
|
|
398
|
+
grid-template-columns: 260px 1fr;
|
|
399
|
+
grid-template-rows: auto;
|
|
400
|
+
gap: 20px;
|
|
401
|
+
max-width: 1100px;
|
|
402
|
+
margin: 0 auto;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.arch-grid__sidebar { grid-column: 1; }
|
|
406
|
+
.arch-grid__main { grid-column: 2; }
|
|
407
|
+
.arch-grid__full { grid-column: 1 / -1; }
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Pipeline (horizontal steps)
|
|
411
|
+
```css
|
|
412
|
+
.pipeline {
|
|
413
|
+
display: flex;
|
|
414
|
+
align-items: stretch;
|
|
415
|
+
gap: 0;
|
|
416
|
+
overflow-x: auto;
|
|
417
|
+
padding-bottom: 8px;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.pipeline__step {
|
|
421
|
+
min-width: 130px;
|
|
422
|
+
flex-shrink: 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.pipeline__arrow {
|
|
426
|
+
display: flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
padding: 0 4px;
|
|
429
|
+
color: var(--border-bright);
|
|
430
|
+
font-size: 18px;
|
|
431
|
+
flex-shrink: 0;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Parallel branch within a pipeline */
|
|
435
|
+
.pipeline__parallel {
|
|
436
|
+
display: flex;
|
|
437
|
+
flex-direction: column;
|
|
438
|
+
gap: 6px;
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Card Grid (dashboard / metrics)
|
|
443
|
+
```css
|
|
444
|
+
.card-grid {
|
|
445
|
+
display: grid;
|
|
446
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
447
|
+
gap: 16px;
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Data Tables
|
|
452
|
+
|
|
453
|
+
Use real `<table>` elements for tabular data. Wrap in a scrollable container for wide tables.
|
|
454
|
+
|
|
455
|
+
```css
|
|
456
|
+
/* Scrollable wrapper for wide tables */
|
|
457
|
+
.table-wrap {
|
|
458
|
+
background: var(--surface);
|
|
459
|
+
border: 1px solid var(--border);
|
|
460
|
+
border-radius: 12px;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.table-scroll {
|
|
465
|
+
overflow-x: auto;
|
|
466
|
+
-webkit-overflow-scrolling: touch;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* Base table */
|
|
470
|
+
.data-table {
|
|
471
|
+
width: 100%;
|
|
472
|
+
border-collapse: collapse;
|
|
473
|
+
font-size: 13px;
|
|
474
|
+
line-height: 1.5;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Header */
|
|
478
|
+
.data-table thead {
|
|
479
|
+
position: sticky;
|
|
480
|
+
top: 0;
|
|
481
|
+
z-index: 2;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.data-table th {
|
|
485
|
+
background: var(--surface-elevated, var(--surface2, var(--surface)));
|
|
486
|
+
font-family: var(--font-mono);
|
|
487
|
+
font-size: 11px;
|
|
488
|
+
font-weight: 600;
|
|
489
|
+
text-transform: uppercase;
|
|
490
|
+
letter-spacing: 1px;
|
|
491
|
+
color: var(--text-dim);
|
|
492
|
+
text-align: left;
|
|
493
|
+
padding: 12px 16px;
|
|
494
|
+
border-bottom: 2px solid var(--border-bright);
|
|
495
|
+
white-space: nowrap;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* Cells */
|
|
499
|
+
.data-table td {
|
|
500
|
+
padding: 12px 16px;
|
|
501
|
+
border-bottom: 1px solid var(--border);
|
|
502
|
+
vertical-align: top;
|
|
503
|
+
color: var(--text);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* Let text-heavy columns wrap naturally */
|
|
507
|
+
.data-table .wide {
|
|
508
|
+
min-width: 200px;
|
|
509
|
+
max-width: 500px;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* Right-align numeric columns */
|
|
513
|
+
.data-table td.num,
|
|
514
|
+
.data-table th.num {
|
|
515
|
+
text-align: right;
|
|
516
|
+
font-variant-numeric: tabular-nums;
|
|
517
|
+
font-family: var(--font-mono);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/* Alternating rows */
|
|
521
|
+
.data-table tbody tr:nth-child(even) {
|
|
522
|
+
background: var(--accent-dim);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* Row hover */
|
|
526
|
+
.data-table tbody tr {
|
|
527
|
+
transition: background 0.15s ease;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.data-table tbody tr:hover {
|
|
531
|
+
background: var(--border);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/* Last row: no bottom border (container handles it) */
|
|
535
|
+
.data-table tbody tr:last-child td {
|
|
536
|
+
border-bottom: none;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* Code inside cells */
|
|
540
|
+
.data-table code {
|
|
541
|
+
font-family: var(--font-mono);
|
|
542
|
+
font-size: 11px;
|
|
543
|
+
background: var(--accent-dim);
|
|
544
|
+
color: var(--accent);
|
|
545
|
+
padding: 1px 5px;
|
|
546
|
+
border-radius: 3px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/* Secondary detail text */
|
|
550
|
+
.data-table small {
|
|
551
|
+
display: block;
|
|
552
|
+
color: var(--text-dim);
|
|
553
|
+
font-size: 11px;
|
|
554
|
+
margin-top: 2px;
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### Status Indicators
|
|
559
|
+
|
|
560
|
+
Styled spans for match/gap/warning states. Never use emoji.
|
|
561
|
+
|
|
562
|
+
```css
|
|
563
|
+
.status {
|
|
564
|
+
display: inline-flex;
|
|
565
|
+
align-items: center;
|
|
566
|
+
gap: 6px;
|
|
567
|
+
font-family: var(--font-mono);
|
|
568
|
+
font-size: 11px;
|
|
569
|
+
font-weight: 500;
|
|
570
|
+
padding: 3px 10px;
|
|
571
|
+
border-radius: 6px;
|
|
572
|
+
white-space: nowrap;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.status--match {
|
|
576
|
+
background: var(--green-dim, rgba(5, 150, 105, 0.1));
|
|
577
|
+
color: var(--green, #059669);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.status--gap {
|
|
581
|
+
background: var(--red-dim, rgba(239, 68, 68, 0.1));
|
|
582
|
+
color: var(--red, #ef4444);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.status--warn {
|
|
586
|
+
background: var(--orange-dim, rgba(217, 119, 6, 0.1));
|
|
587
|
+
color: var(--orange, #d97706);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.status--info {
|
|
591
|
+
background: var(--accent-dim);
|
|
592
|
+
color: var(--accent);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* Dot variant (compact, no text) */
|
|
596
|
+
.status-dot {
|
|
597
|
+
width: 8px;
|
|
598
|
+
height: 8px;
|
|
599
|
+
border-radius: 50%;
|
|
600
|
+
display: inline-block;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.status-dot--match { background: var(--green, #059669); }
|
|
604
|
+
.status-dot--gap { background: var(--red, #ef4444); }
|
|
605
|
+
.status-dot--warn { background: var(--orange, #d97706); }
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
Usage in table cells:
|
|
609
|
+
```html
|
|
610
|
+
<td><span class="status status--match">Match</span></td>
|
|
611
|
+
<td><span class="status status--gap">Gap</span></td>
|
|
612
|
+
<td><span class="status status--warn">Partial</span></td>
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
#### Table Summary Row
|
|
616
|
+
|
|
617
|
+
For totals, counts, or aggregate status at the bottom:
|
|
618
|
+
|
|
619
|
+
```css
|
|
620
|
+
.data-table tfoot td {
|
|
621
|
+
background: var(--surface-elevated, var(--surface2, var(--surface)));
|
|
622
|
+
font-weight: 600;
|
|
623
|
+
font-size: 12px;
|
|
624
|
+
border-top: 2px solid var(--border-bright);
|
|
625
|
+
border-bottom: none;
|
|
626
|
+
padding: 12px 16px;
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
#### Sticky First Column (for very wide tables)
|
|
631
|
+
|
|
632
|
+
```css
|
|
633
|
+
.data-table th:first-child,
|
|
634
|
+
.data-table td:first-child {
|
|
635
|
+
position: sticky;
|
|
636
|
+
left: 0;
|
|
637
|
+
z-index: 1;
|
|
638
|
+
background: var(--surface);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.data-table tbody tr:nth-child(even) td:first-child {
|
|
642
|
+
background: color-mix(in srgb, var(--surface) 95%, var(--accent) 5%);
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
## Connectors
|
|
647
|
+
|
|
648
|
+
### CSS Arrow (vertical, between stacked sections)
|
|
649
|
+
```css
|
|
650
|
+
.flow-arrow {
|
|
651
|
+
display: flex;
|
|
652
|
+
justify-content: center;
|
|
653
|
+
align-items: center;
|
|
654
|
+
gap: 8px;
|
|
655
|
+
color: var(--text-dim);
|
|
656
|
+
font-family: var(--font-mono);
|
|
657
|
+
font-size: 12px;
|
|
658
|
+
padding: 6px 0;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* Down arrow via SVG icon */
|
|
662
|
+
.flow-arrow svg {
|
|
663
|
+
width: 20px;
|
|
664
|
+
height: 20px;
|
|
665
|
+
fill: none;
|
|
666
|
+
stroke: var(--border-bright);
|
|
667
|
+
stroke-width: 2;
|
|
668
|
+
stroke-linecap: round;
|
|
669
|
+
stroke-linejoin: round;
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Down arrow SVG (reuse inline):
|
|
674
|
+
```html
|
|
675
|
+
<svg viewBox="0 0 20 20"><path d="M10 4 L10 16 M6 12 L10 16 L14 12"/></svg>
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### CSS Arrow (horizontal, between inline steps)
|
|
679
|
+
Use `::after` or a literal arrow character:
|
|
680
|
+
```css
|
|
681
|
+
.h-arrow::after {
|
|
682
|
+
content: '→';
|
|
683
|
+
color: var(--border-bright);
|
|
684
|
+
font-size: 18px;
|
|
685
|
+
padding: 0 4px;
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### SVG Curved Connector (between arbitrary nodes)
|
|
690
|
+
For connections that aren't simple vertical/horizontal, use an absolutely positioned SVG overlay:
|
|
691
|
+
```html
|
|
692
|
+
<svg class="connectors" style="position:absolute;inset:0;width:100%;height:100%;pointer-events:none;">
|
|
693
|
+
<path d="M 150,100 C 150,200 350,100 350,200" fill="none" stroke="var(--accent)" stroke-width="1.5" stroke-dasharray="4 3"/>
|
|
694
|
+
<!-- Arrowhead -->
|
|
695
|
+
<polygon points="348,195 352,205 356,195" fill="var(--accent)"/>
|
|
696
|
+
</svg>
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
Position the parent container as `position: relative` to scope the SVG overlay.
|
|
700
|
+
|
|
701
|
+
## Animations
|
|
702
|
+
|
|
703
|
+
### Staggered Fade-In on Load
|
|
704
|
+
|
|
705
|
+
Define the keyframe once, then stagger via a `--i` CSS variable set per element. This approach works regardless of DOM nesting or interleaved non-animated elements (unlike `nth-child` which breaks when siblings aren't all the same type).
|
|
706
|
+
|
|
707
|
+
```css
|
|
708
|
+
@keyframes fadeUp {
|
|
709
|
+
from { opacity: 0; transform: translateY(12px); }
|
|
710
|
+
to { opacity: 1; transform: translateY(0); }
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.ve-card {
|
|
714
|
+
animation: fadeUp 0.4s ease-out both;
|
|
715
|
+
animation-delay: calc(var(--i, 0) * 0.05s);
|
|
716
|
+
}
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Set `--i` per element in the HTML to control stagger order:
|
|
720
|
+
|
|
721
|
+
```html
|
|
722
|
+
<div class="ve-card" style="--i: 0">First</div>
|
|
723
|
+
<div class="connector">...</div>
|
|
724
|
+
<div class="ve-card" style="--i: 1">Second</div>
|
|
725
|
+
<div class="connector">...</div>
|
|
726
|
+
<div class="ve-card" style="--i: 2">Third</div>
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Hover Lift
|
|
730
|
+
```css
|
|
731
|
+
.ve-card {
|
|
732
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.ve-card:hover {
|
|
736
|
+
transform: translateY(-2px);
|
|
737
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Scale-Fade (for KPI cards, badges, status indicators)
|
|
742
|
+
|
|
743
|
+
```css
|
|
744
|
+
@keyframes fadeScale {
|
|
745
|
+
from { opacity: 0; transform: scale(0.92); }
|
|
746
|
+
to { opacity: 1; transform: scale(1); }
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.kpi-card {
|
|
750
|
+
animation: fadeScale 0.35s ease-out both;
|
|
751
|
+
animation-delay: calc(var(--i, 0) * 0.06s);
|
|
752
|
+
}
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
### SVG Draw-In (for connectors, progress rings, path elements)
|
|
756
|
+
|
|
757
|
+
```css
|
|
758
|
+
@keyframes drawIn {
|
|
759
|
+
from { stroke-dashoffset: var(--path-length); }
|
|
760
|
+
to { stroke-dashoffset: 0; }
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/* Set --path-length to the path's getTotalLength() value */
|
|
764
|
+
.connector path {
|
|
765
|
+
stroke-dasharray: var(--path-length);
|
|
766
|
+
animation: drawIn 0.8s ease-in-out both;
|
|
767
|
+
animation-delay: calc(var(--i, 0) * 0.1s);
|
|
768
|
+
}
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### CSS Counter (for hero numbers without JS)
|
|
772
|
+
|
|
773
|
+
Uses `@property` to animate a custom property as an integer, then display it via `counter()`. No JS required. Falls back to showing the final value immediately in browsers without `@property` support.
|
|
774
|
+
|
|
775
|
+
```css
|
|
776
|
+
@property --count {
|
|
777
|
+
syntax: '<integer>';
|
|
778
|
+
initial-value: 0;
|
|
779
|
+
inherits: false;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
@keyframes countUp {
|
|
783
|
+
to { --count: var(--target); }
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.kpi-card__value--animated {
|
|
787
|
+
--target: 247;
|
|
788
|
+
counter-reset: val var(--count);
|
|
789
|
+
animation: countUp 1.2s ease-out forwards;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.kpi-card__value--animated::after {
|
|
793
|
+
content: counter(val);
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Choreography
|
|
798
|
+
|
|
799
|
+
Don't use the same animation for everything. Mix types by element role, with easing stagger (fast-then-slow, not linear):
|
|
800
|
+
|
|
801
|
+
- **Cards**: `fadeUp` — the default entrance, reliable and subtle
|
|
802
|
+
- **KPI / badges**: `fadeScale` — scale draws the eye to important numbers
|
|
803
|
+
- **SVG connectors**: `drawIn` — reveals flow direction, pairs with card stagger
|
|
804
|
+
- **Hero numbers**: `countUp` — counting motion signals "this number matters"
|
|
805
|
+
- **Stagger timing**: `calc(var(--i) * 0.06s)` with lower `--i` values on important elements so they appear first
|
|
806
|
+
|
|
807
|
+
### Respect Reduced Motion
|
|
808
|
+
```css
|
|
809
|
+
@media (prefers-reduced-motion: reduce) {
|
|
810
|
+
*, *::before, *::after {
|
|
811
|
+
animation-duration: 0.01ms !important;
|
|
812
|
+
animation-iteration-count: 1 !important;
|
|
813
|
+
transition-duration: 0.01ms !important;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
## Sparklines and Simple Charts (Pure SVG)
|
|
819
|
+
|
|
820
|
+
For simple inline visualizations without a library:
|
|
821
|
+
|
|
822
|
+
```html
|
|
823
|
+
<!-- Sparkline -->
|
|
824
|
+
<svg viewBox="0 0 100 30" style="width:100px;height:30px;">
|
|
825
|
+
<polyline points="0,25 15,20 30,22 45,10 60,15 75,5 90,12 100,8"
|
|
826
|
+
fill="none" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round"/>
|
|
827
|
+
</svg>
|
|
828
|
+
|
|
829
|
+
<!-- Progress bar -->
|
|
830
|
+
<div style="height:6px;background:var(--border);border-radius:3px;overflow:hidden;">
|
|
831
|
+
<div style="height:100%;width:72%;background:var(--accent);border-radius:3px;"></div>
|
|
832
|
+
</div>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
## Responsive Breakpoint
|
|
836
|
+
|
|
837
|
+
Include a single breakpoint for narrow viewports:
|
|
838
|
+
|
|
839
|
+
```css
|
|
840
|
+
@media (max-width: 768px) {
|
|
841
|
+
.arch-grid { grid-template-columns: 1fr; }
|
|
842
|
+
.pipeline { flex-wrap: wrap; gap: 8px; }
|
|
843
|
+
.pipeline__arrow { display: none; }
|
|
844
|
+
body { padding: 16px; }
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
## Badges and Tags
|
|
849
|
+
|
|
850
|
+
Small inline labels for categorizing elements:
|
|
851
|
+
|
|
852
|
+
```css
|
|
853
|
+
.tag {
|
|
854
|
+
font-family: var(--font-mono);
|
|
855
|
+
font-size: 10px;
|
|
856
|
+
font-weight: 500;
|
|
857
|
+
padding: 2px 7px;
|
|
858
|
+
border-radius: 4px;
|
|
859
|
+
background: var(--node-a-dim);
|
|
860
|
+
color: var(--node-a);
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
## Lists Inside Nodes
|
|
865
|
+
|
|
866
|
+
For tool listings, feature lists, table columns:
|
|
867
|
+
|
|
868
|
+
```css
|
|
869
|
+
.node-list {
|
|
870
|
+
list-style: none;
|
|
871
|
+
padding: 0;
|
|
872
|
+
margin: 0;
|
|
873
|
+
font-size: 12px;
|
|
874
|
+
line-height: 1.8;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.node-list li {
|
|
878
|
+
padding-left: 14px;
|
|
879
|
+
position: relative;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.node-list li::before {
|
|
883
|
+
content: '›';
|
|
884
|
+
color: var(--text-dim);
|
|
885
|
+
font-weight: 600;
|
|
886
|
+
position: absolute;
|
|
887
|
+
left: 0;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.node-list code {
|
|
891
|
+
font-family: var(--font-mono);
|
|
892
|
+
font-size: 11px;
|
|
893
|
+
background: var(--accent-dim);
|
|
894
|
+
color: var(--accent);
|
|
895
|
+
padding: 1px 5px;
|
|
896
|
+
border-radius: 3px;
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
## KPI / Metric Cards
|
|
901
|
+
|
|
902
|
+
Large hero number with trend indicator and label. For dashboards, review summaries, and impact sections.
|
|
903
|
+
|
|
904
|
+
```css
|
|
905
|
+
.kpi-row {
|
|
906
|
+
display: grid;
|
|
907
|
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
908
|
+
gap: 16px;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.kpi-card {
|
|
912
|
+
background: var(--surface-elevated);
|
|
913
|
+
border: 1px solid var(--border);
|
|
914
|
+
border-radius: 10px;
|
|
915
|
+
padding: 20px;
|
|
916
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.kpi-card__value {
|
|
920
|
+
font-size: 36px;
|
|
921
|
+
font-weight: 700;
|
|
922
|
+
letter-spacing: -1px;
|
|
923
|
+
line-height: 1.1;
|
|
924
|
+
font-variant-numeric: tabular-nums;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.kpi-card__label {
|
|
928
|
+
font-family: var(--font-mono);
|
|
929
|
+
font-size: 10px;
|
|
930
|
+
font-weight: 600;
|
|
931
|
+
text-transform: uppercase;
|
|
932
|
+
letter-spacing: 1.5px;
|
|
933
|
+
color: var(--text-dim);
|
|
934
|
+
margin-top: 6px;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.kpi-card__trend {
|
|
938
|
+
font-family: var(--font-mono);
|
|
939
|
+
font-size: 12px;
|
|
940
|
+
margin-top: 4px;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.kpi-card__trend--up { color: var(--node-b, #059669); }
|
|
944
|
+
.kpi-card__trend--down { color: var(--red, #ef4444); }
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
```html
|
|
948
|
+
<div class="kpi-row">
|
|
949
|
+
<div class="kpi-card">
|
|
950
|
+
<div class="kpi-card__value">247</div>
|
|
951
|
+
<div class="kpi-card__label">Lines Added</div>
|
|
952
|
+
<div class="kpi-card__trend kpi-card__trend--up">+34%</div>
|
|
953
|
+
</div>
|
|
954
|
+
<!-- ... more cards -->
|
|
955
|
+
</div>
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
## Before / After Panels
|
|
959
|
+
|
|
960
|
+
Two-column comparison with diff-colored headers. For review pages, migration docs, and feature comparisons.
|
|
961
|
+
|
|
962
|
+
```css
|
|
963
|
+
.diff-panels {
|
|
964
|
+
display: grid;
|
|
965
|
+
grid-template-columns: 1fr 1fr;
|
|
966
|
+
gap: 0;
|
|
967
|
+
border: 1px solid var(--border);
|
|
968
|
+
border-radius: 10px;
|
|
969
|
+
overflow: hidden;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
.diff-panels > * { min-width: 0; overflow-wrap: break-word; }
|
|
973
|
+
|
|
974
|
+
.diff-panel__header {
|
|
975
|
+
font-family: var(--font-mono);
|
|
976
|
+
font-size: 11px;
|
|
977
|
+
font-weight: 600;
|
|
978
|
+
text-transform: uppercase;
|
|
979
|
+
letter-spacing: 1px;
|
|
980
|
+
padding: 10px 16px;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
.diff-panel__header--before {
|
|
984
|
+
background: var(--red-dim, rgba(239, 68, 68, 0.08));
|
|
985
|
+
color: var(--red, #ef4444);
|
|
986
|
+
border-bottom: 2px solid var(--red, #ef4444);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.diff-panel__header--after {
|
|
990
|
+
background: var(--green-dim, rgba(5, 150, 105, 0.08));
|
|
991
|
+
color: var(--green, #059669);
|
|
992
|
+
border-bottom: 2px solid var(--green, #059669);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.diff-panel__body {
|
|
996
|
+
padding: 16px;
|
|
997
|
+
background: var(--surface);
|
|
998
|
+
font-size: 13px;
|
|
999
|
+
line-height: 1.6;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/* Highlight changed items within a panel */
|
|
1003
|
+
.diff-changed {
|
|
1004
|
+
background: var(--accent-dim);
|
|
1005
|
+
border-radius: 3px;
|
|
1006
|
+
padding: 0 3px;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
@media (max-width: 768px) {
|
|
1010
|
+
.diff-panels { grid-template-columns: 1fr; }
|
|
1011
|
+
}
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
```html
|
|
1015
|
+
<div class="diff-panels">
|
|
1016
|
+
<div class="diff-panel__header diff-panel__header--before">Before</div>
|
|
1017
|
+
<div class="diff-panel__header diff-panel__header--after">After</div>
|
|
1018
|
+
<div class="diff-panel__body">Previous implementation...</div>
|
|
1019
|
+
<div class="diff-panel__body">New implementation...</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
## Collapsible Sections
|
|
1024
|
+
|
|
1025
|
+
Native `<details>/<summary>` with styled disclosure. Zero JS, accessible. For lower-priority content: file maps, decision logs, reference sections.
|
|
1026
|
+
|
|
1027
|
+
```css
|
|
1028
|
+
details.collapsible {
|
|
1029
|
+
border: 1px solid var(--border);
|
|
1030
|
+
border-radius: 10px;
|
|
1031
|
+
overflow: hidden;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
details.collapsible summary {
|
|
1035
|
+
padding: 14px 20px;
|
|
1036
|
+
background: var(--surface);
|
|
1037
|
+
font-family: var(--font-mono);
|
|
1038
|
+
font-size: 12px;
|
|
1039
|
+
font-weight: 600;
|
|
1040
|
+
cursor: pointer;
|
|
1041
|
+
list-style: none;
|
|
1042
|
+
display: flex;
|
|
1043
|
+
align-items: center;
|
|
1044
|
+
gap: 8px;
|
|
1045
|
+
color: var(--text);
|
|
1046
|
+
transition: background 0.15s ease;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
details.collapsible summary:hover {
|
|
1050
|
+
background: var(--surface-elevated, var(--surface));
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
details.collapsible summary::-webkit-details-marker { display: none; }
|
|
1054
|
+
|
|
1055
|
+
/* Chevron indicator */
|
|
1056
|
+
details.collapsible summary::before {
|
|
1057
|
+
content: '▸';
|
|
1058
|
+
font-size: 11px;
|
|
1059
|
+
color: var(--text-dim);
|
|
1060
|
+
transition: transform 0.15s ease;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
details.collapsible[open] summary::before {
|
|
1064
|
+
transform: rotate(90deg);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
details.collapsible .collapsible__body {
|
|
1068
|
+
padding: 16px 20px;
|
|
1069
|
+
border-top: 1px solid var(--border);
|
|
1070
|
+
font-size: 13px;
|
|
1071
|
+
line-height: 1.6;
|
|
1072
|
+
}
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
```html
|
|
1076
|
+
<details class="collapsible">
|
|
1077
|
+
<summary>File Map (14 files changed)</summary>
|
|
1078
|
+
<div class="collapsible__body">
|
|
1079
|
+
<!-- content here -->
|
|
1080
|
+
</div>
|
|
1081
|
+
</details>
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
## Generated Images
|
|
1085
|
+
|
|
1086
|
+
For AI-generated illustrations embedded as base64 data URIs via `surf gemini --generate-image`. Use sparingly — hero banners, conceptual illustrations, educational diagrams, decorative accents.
|
|
1087
|
+
|
|
1088
|
+
### Hero Banner
|
|
1089
|
+
|
|
1090
|
+
Full-width image cropped to a fixed height with a gradient fade into the page background. Place at the top of the page before the title, or between the title and the first content section.
|
|
1091
|
+
|
|
1092
|
+
```css
|
|
1093
|
+
.hero-img-wrap {
|
|
1094
|
+
position: relative;
|
|
1095
|
+
border-radius: 12px;
|
|
1096
|
+
overflow: hidden;
|
|
1097
|
+
margin-bottom: 24px;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
.hero-img-wrap img {
|
|
1101
|
+
width: 100%;
|
|
1102
|
+
height: 240px;
|
|
1103
|
+
object-fit: cover;
|
|
1104
|
+
display: block;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/* Gradient fade into page background */
|
|
1108
|
+
.hero-img-wrap::after {
|
|
1109
|
+
content: '';
|
|
1110
|
+
position: absolute;
|
|
1111
|
+
bottom: 0;
|
|
1112
|
+
left: 0;
|
|
1113
|
+
right: 0;
|
|
1114
|
+
height: 50%;
|
|
1115
|
+
background: linear-gradient(to top, var(--bg), transparent);
|
|
1116
|
+
pointer-events: none;
|
|
1117
|
+
}
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
```html
|
|
1121
|
+
<div class="hero-img-wrap">
|
|
1122
|
+
<img src="data:image/png;base64,..." alt="Descriptive alt text">
|
|
1123
|
+
</div>
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
Generate with `--aspect-ratio 16:9` for hero banners.
|
|
1127
|
+
|
|
1128
|
+
### Inline Illustration
|
|
1129
|
+
|
|
1130
|
+
Centered image with border, shadow, and optional caption. Use within content sections for conceptual or educational illustrations.
|
|
1131
|
+
|
|
1132
|
+
```css
|
|
1133
|
+
.illus {
|
|
1134
|
+
text-align: center;
|
|
1135
|
+
margin: 24px 0;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.illus img {
|
|
1139
|
+
max-width: 480px;
|
|
1140
|
+
width: 100%;
|
|
1141
|
+
border-radius: 10px;
|
|
1142
|
+
border: 1px solid var(--border);
|
|
1143
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.illus figcaption {
|
|
1147
|
+
font-family: var(--font-mono);
|
|
1148
|
+
font-size: 11px;
|
|
1149
|
+
color: var(--text-dim);
|
|
1150
|
+
margin-top: 8px;
|
|
1151
|
+
}
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
```html
|
|
1155
|
+
<figure class="illus">
|
|
1156
|
+
<img src="data:image/png;base64,..." alt="Descriptive alt text">
|
|
1157
|
+
<figcaption>How the message queue routes events between services</figcaption>
|
|
1158
|
+
</figure>
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
Generate with `--aspect-ratio 1:1` or `--aspect-ratio 4:3` for inline illustrations.
|
|
1162
|
+
|
|
1163
|
+
### Side Accent
|
|
1164
|
+
|
|
1165
|
+
Small image floated beside a section. Use when the illustration supports but doesn't dominate the content.
|
|
1166
|
+
|
|
1167
|
+
```css
|
|
1168
|
+
.accent-img {
|
|
1169
|
+
float: right;
|
|
1170
|
+
max-width: 200px;
|
|
1171
|
+
margin: 0 0 16px 24px;
|
|
1172
|
+
border-radius: 10px;
|
|
1173
|
+
border: 1px solid var(--border);
|
|
1174
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
@media (max-width: 768px) {
|
|
1178
|
+
.accent-img {
|
|
1179
|
+
float: none;
|
|
1180
|
+
max-width: 100%;
|
|
1181
|
+
margin: 0 0 16px 0;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
```html
|
|
1187
|
+
<img class="accent-img" src="data:image/png;base64,..." alt="Descriptive alt text">
|
|
1188
|
+
```
|