← LeaderboardThe Skill Leaderboard

baoyu-diagram

Create professional diagrams with configurable dark/light theme via CSS custom properties. Use this skill whenever the user asks for any kind of technical or conceptual diagram, visualization of a system, process flow, data flow, component relationship, network topology, decision tree, org chart, state machine, or any visual representation of structure/logic/process. Also trigger when the user says "画个图" "画一个架构图" "diagram" "flowchart" "sequence diagram" "draw me a ..." or uploads content and asks to visualize it. Outputs a dark-theme SVG, a light-theme SVG, and a @2x PNG of each.

Published by JimLiu ↗JimLiu/baoyu-skills

Try your first task

A source-grounded starter task has not been verified for this skill yet.

SKILL.md

name: baoyu-diagramdescription: Create professional diagrams with configurable dark/light theme via CSS custom properties. Use this skill whenever the user asks for any kind of technical or conceptual diagram, visualization of a system, process flow, data flow, component relationship, network topology, decision tree, org chart, state machine, or any visual representation of structure/logic/process. Also trigger when the user says "画个图" "画一个架构图" "diagram" "flowchart" "sequence diagram" "draw me a ..." or uploads content and asks to visualize it. Outputs a dark-theme SVG, a light-theme SVG, and a @2x PNG of each.version: 1.118.0

Diagram Generator

Create SVG diagrams. Each SVG embeds its own styles, uses CSS custom properties for theming, and loads nothing over the network.

Output Files

For a diagram saved as <name>.svg, produce exactly these files:

FileWhat it isMade by
<name>.svgSource SVG, CSS variables with dark theme valuesYou (the LLM)
<name>.light.svgSame markup with light theme valuestheme-switch.ts --theme=light
<name>@2x.pngDark PNG at 2x scalemain.ts
<name>.light@2x.pngLight PNG at 2x scalemain.ts --theme light

If the user asks for only one theme, or only SVG, produce just those files.

Themes

All colors are CSS custom properties defined in the :root block of the SVG's <style>; markup references them as var(--bg), var(--color-primary-fill), etc. Never hardcode color values in SVG attributes.

Theme values live in {baseDir}/themes/<name>.json — one flat JSON object mapping CSS property names (keys starting with --) to colors. Other keys (e.g. "description") are metadata, ignored by scripts. Built-in: dark.json, light.json.

Custom themes: drop a new .json into themes/ — no script changes needed:

// themes/my-brand.json
{
  "description": "Corporate brand theme",
  "--bg": "#ffffff",
  "--text": "#1a1a2e",
  "--text-muted": "#6b7280",
  "--arrow": "#9ca3af",
  "--color-primary-fill": "rgba(37,99,235,0.12)",
  "--color-primary-stroke": "#2563eb",
  "--color-secondary-fill": "rgba(22,163,74,0.12)",
  "--color-secondary-stroke": "#16a34a"
}

Apply with --theme=my-brand (theme-switch.ts, main.ts and flatten-vars.ts all support it). Variables omitted from a custom theme are not replaced. When generating SVGs for a custom theme, use its values in the :root block.

CSS Custom Properties

VariableDark Theme DefaultLight Theme ValueUsage
--bg#0f172a#f8fafcPage background
--bg-grid#1e293b#e2e8f0Grid pattern stroke
--mask#0f172a#f8fafcOpaque mask fill (same as --bg)
--textwhite#0f172aPrimary text color
--text-muted#94a3b8#475569Secondary/sublabel text
--arrow#64748b#64748bArrow/connector lines (unchanged)
--color-primary-fillrgba(8,51,68,0.4)rgba(6,182,212,0.12)Frontend, user-facing, inputs
--color-primary-stroke#22d3ee#0891b2Primary border
--color-secondary-fillrgba(6,78,59,0.4)rgba(5,150,105,0.12)Backend, services, processing
--color-secondary-stroke#34d399#059669Secondary border
--color-tertiary-fillrgba(76,29,149,0.4)rgba(124,58,237,0.12)Database, storage, persistence
--color-tertiary-stroke#a78bfa#7c3aedTertiary border
--color-accent-fillrgba(120,53,15,0.3)rgba(217,119,6,0.12)Cloud, infrastructure, regions
--color-accent-stroke#fbbf24#d97706Accent border
--color-alert-fillrgba(136,19,55,0.4)rgba(225,29,72,0.12)Security, errors, warnings
--color-alert-stroke#fb7185#e11d48Alert border
--color-connector-fillrgba(251,146,60,0.3)rgba(234,88,12,0.12)Buses, queues, middleware
--color-connector-stroke#fb923c#ea580cConnector border
--color-neutral-fillrgba(30,41,59,0.5)rgba(100,116,139,0.12)External, generic, unknown
--color-neutral-stroke#94a3b8#64748bNeutral border
--color-highlight-fillrgba(59,130,246,0.3)rgba(59,130,246,0.12)Active state, focus, current step
--color-highlight-stroke#60a5fa#3b82f6Highlight border

Design System

Typography

Use a locally installed monospace font stack. Do not @import web fonts or reference any remote resource: the SVG must render the same offline, and the PNG converter cannot fetch fonts.

<style>
  text { font-family: 'JetBrains Mono', 'SF Mono', 'Cascadia Code', 'Noto Sans Mono', 'DejaVu Sans Mono', monospace; }
</style>

Font sizes by role:

  • Title: 16px, weight 700, fill: var(--text)
  • Component name: 11-12px, weight 600, fill: var(--text)
  • Sublabel / description: 9px, weight 400, fill: var(--text-muted)
  • Annotation / note: 8px, weight 400
  • Tiny label (on arrows): 7-8px

Core Visual Elements

Background + Grid:

<defs>
  <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--bg-grid)" stroke-width="0.5"/>
  </pattern>
</defs>
<rect width="100%" height="100%" fill="var(--bg)"/>
<rect width="100%" height="100%" fill="url(#grid)"/>

Arrowhead marker (standard):

<marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
  <polygon points="0 0, 10 3.5, 0 7" fill="var(--arrow)"/>
</marker>

Arrowhead marker (colored) — create per-color as needed:

<marker id="arrow-primary" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
  <polygon points="0 0, 10 3.5, 0 7" fill="var(--color-primary-stroke)"/>
</marker>

Open arrowhead (for async/return messages):

<marker id="arrow-open" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
  <polyline points="0 0, 10 3.5, 0 7" fill="none" stroke="var(--arrow)" stroke-width="1.5"/>
</marker>

SVG Structure & Layering

Draw in this order (SVG paints back-to-front):

  1. Background fill + grid pattern
  2. Region/group boundaries (dashed outlines)
  3. Connection arrows and lines
  4. Opaque masking rects (same position as component boxes, fill="var(--mask)")
  5. Component boxes (semi-transparent fill + stroke via CSS variables)
  6. Text labels
  7. Legend (bottom-right or bottom area, outside all boundaries)
  8. Title block (top-left)

The opaque mask rect is required — semi-transparent component fills otherwise show arrows underneath:

<!-- Mask layer: opaque background to hide arrows -->
<rect x="100" y="100" width="160" height="60" rx="6" fill="var(--mask)"/>
<!-- Visual layer: styled component -->
<rect x="100" y="100" width="160" height="60" rx="6" fill="var(--color-primary-fill)" stroke="var(--color-primary-stroke)" stroke-width="1.5"/>
<text x="180" y="125" fill="var(--text)" font-size="11" font-weight="600" text-anchor="middle">API Gateway</text>
<text x="180" y="141" fill="var(--text-muted)" font-size="9" text-anchor="middle">Kong / Nginx</text>

Spacing Rules

Follow strictly to prevent overlaps:

  • Component box height: 50-70px (standard), 80-120px (large/complex)
  • Minimum gap between components: 40px vertical, 30px horizontal
  • Arrow label clearance: 10px from any box edge
  • Region boundary padding: 20px inside edges around contained components
  • Legend placement: At least 20px below the lowest diagram element
  • Title block: 20px from top-left, outside diagram content area
  • viewBox: Always extend to fit all content + 30px padding on all sides

Component Patterns

Standard box (service/process):

<rect x="X" y="Y" width="160" height="60" rx="6" fill="var(--mask)"/>
<rect x="X" y="Y" width="160" height="60" rx="6" fill="var(--color-primary-fill)" stroke="var(--color-primary-stroke)" stroke-width="1.5"/>
<text x="CX" y="Y+24" fill="var(--text)" font-size="11" font-weight="600" text-anchor="middle">Name</text>
<text x="CX" y="Y+40" fill="var(--text-muted)" font-size="9" text-anchor="middle">description</text>

Decision diamond (flowchart):

<g transform="translate(CX, CY)">
  <polygon points="0,-35 50,0 0,35 -50,0" fill="var(--mask)"/>
  <polygon points="0,-35 50,0 0,35 -50,0" fill="var(--color-accent-fill)" stroke="var(--color-accent-stroke)" stroke-width="1.5"/>
  <text y="4" fill="var(--text)" font-size="10" font-weight="600" text-anchor="middle">Condition?</text>
</g>

Database cylinder:

<g transform="translate(X, Y)">
  <rect x="0" y="10" width="120" height="50" rx="2" fill="var(--mask)"/>
  <ellipse cx="60" cy="10" rx="60" ry="12" fill="var(--mask)"/>
  <ellipse cx="60" cy="60" rx="60" ry="12" fill="var(--mask)"/>
  <rect x="0" y="10" width="120" height="50" fill="var(--color-tertiary-fill)"/>
  <ellipse cx="60" cy="10" rx="60" ry="12" fill="var(--color-tertiary-fill)" stroke="var(--color-tertiary-stroke)" stroke-width="1.5"/>
  <ellipse cx="60" cy="60" rx="60" ry="12" fill="var(--color-tertiary-fill)" stroke="var(--color-tertiary-stroke)" stroke-width="1.5"/>
  <line x1="0" y1="10" x2="0" y2="60" stroke="var(--color-tertiary-stroke)" stroke-width="1.5"/>
  <line x1="120" y1="10" x2="120" y2="60" stroke="var(--color-tertiary-stroke)" stroke-width="1.5"/>
  <text x="60" y="40" fill="var(--text)" font-size="11" font-weight="600" text-anchor="middle">PostgreSQL</text>
</g>

Region boundary:

<rect x="X" y="Y" width="W" height="H" rx="12" fill="none" stroke="var(--color-accent-stroke)" stroke-width="1" stroke-dasharray="8,4"/>
<text x="X+12" y="Y+16" fill="var(--color-accent-stroke)" font-size="9" font-weight="600">AWS us-east-1</text>

Security group:

<rect x="X" y="Y" width="W" height="H" rx="8" fill="none" stroke="var(--color-alert-stroke)" stroke-width="1" stroke-dasharray="4,4"/>
<text x="X+10" y="Y+14" fill="var(--color-alert-stroke)" font-size="8" font-weight="500">VPC / Security Group</text>

Diagram Types & Layout

Determine this SKILL.md file's directory path as {baseDir}. Before layout, read the type's reference in {baseDir}/references/ if one exists.

TypeWhen to useReferenceLayout key points
ArchitectureSystem components & relationshipsreferences/architecture.mdLTR or TTB data flow; group related services in region boundaries; buses/connectors between layers; databases at bottom or right
FlowchartDecision logic, process stepsreferences/flowchart.mdTop-to-bottom primary flow; diamonds with Yes/No labels on exit arrows; rounded rects for start/end; Highlight color for the happy path
SequenceTime-ordered interactions between actorsreferences/sequence.mdActor boxes at top, vertical dashed lifelines; horizontal messages (solid=sync, dashed=return); time flows down; activation bars; number messages if complex
StructuralClass/ER diagrams, org chartsreferences/structural.mdCompartmented boxes (name / attributes / methods); filled diamond=composition, empty diamond=aggregation, dashed arrow=dependency, solid triangle=inheritance
Mind MapBrainstorming, topic exploration—Radiating layout from a central concept; cubic-bezier <path> branches; vary branch colors; font size decreases outward
TimelineChronological events—Horizontal or vertical axis; circle/diamond event markers, period spans; descriptions on alternating sides; color by event type
State MachineState transitions, lifecycle—Rounded-rect states, double border for composite; filled circle = initial, bullseye = final; curved self-transitions; label transitions event [guard] / action
IllustrativeConceptual explanations, comparisons—Free-form layout, icons, annotations, visual metaphors
Data FlowData transformation pipelines—Process bubbles, data stores, external entities

Output Rules

  1. Each SVG is a single file with no external resources — no @import, web fonts, remote images or scripts
  2. Set viewBox to fit all content with 30px padding; do NOT set fixed width/height attributes
  3. Include xmlns="http://www.w3.org/2000/svg" on the root <svg>
  4. Put all <style>, <defs>, markers, and patterns at the top
  5. Use text-anchor="middle" for centered labels; text must not overflow boxes
  6. Chinese text: use font-family: 'JetBrains Mono', 'Noto Sans SC', 'PingFang SC', sans-serif' and widen boxes — CJK characters are wider
  7. CSS variables required: every color is a var(--...) reference so theme switching works
  8. Save location: input is a file → {inputFileDir}/diagram/; otherwise {projectDir}/diagram/{topic-slug}/. Create the directory if missing

Scripts

Determine this SKILL.md file's directory path as {baseDir}. Scripts live in {baseDir}/scripts/:

  • theme-switch.ts — write the SVG with another theme's variable values
  • main.ts — convert an SVG to a @2x PNG
  • flatten-vars.ts — inline CSS variables into literal colors (only for other non-browser tools; main.ts does this itself)

Resolve ${BUN_X} runtime: if bun installed → bun; if npx available → npx -y bun; else suggest installing bun.

Theme switch

${BUN_X} {baseDir}/scripts/theme-switch.ts <svg-path> --theme=light [--output=path/to/output.svg]
${BUN_X} {baseDir}/scripts/theme-switch.ts <svg-path> --theme=dark
${BUN_X} {baseDir}/scripts/theme-switch.ts <svg-path> --theme=my-brand   # {baseDir}/themes/<name>.json

Replaces the :root { ... } definitions with the target theme's values; var(--...) references stay unchanged and the source is not modified. Without --output, writes <name>.<theme>.svg next to the input. The source already carries dark values, so --theme=dark on it is unnecessary.

Dependencies

main.ts needs sharp, pinned in {baseDir}/scripts/package.json with a lockfile. Install once:

cd {baseDir}/scripts && ${BUN_X} install

If sharp is missing, main.ts stops with an error that gives this command; it never installs anything itself. Then deliver the SVG files and tell the user how to enable PNG output. theme-switch.ts and flatten-vars.ts have no dependencies.

SVG → @2x PNG

${BUN_X} {baseDir}/scripts/main.ts <svg-path> [options]

Options:

  • -t, --theme <name> — Theme used to resolve CSS variables (default: dark; any {baseDir}/themes/<name>.json works)
  • -s, --scale <n> — Scale factor (default: 2)
  • -o, --output <path> — Custom output path (default: <input>@2x.png)
  • --json — JSON output

sharp (librsvg) does not understand CSS var(), so main.ts replaces every var(--xxx) with the theme's literal value and drops the :root block. It fails if the SVG references a variable that neither the theme nor the SVG defines. SVGs with hardcoded colors (legacy diagrams) are converted as-is.

To flatten without converting (e.g. for another renderer):

${BUN_X} {baseDir}/scripts/flatten-vars.ts <svg-path> <output-path> [--theme=<name>]

Process

  1. Identify the diagram type; read its reference file if one exists
  2. Plan the layout: list components, grouping and flow direction, calculate positions
  3. Write the SVG in the layering order above, colors via CSS variables only, with the :root { ... } block using the dark defaults from the CSS Custom Properties table
  4. Verify spacing rules — no overlaps, legends outside boundaries, viewBox large enough
  5. Save as <name>.svg (the dark version)
  6. Run ${BUN_X} {baseDir}/scripts/theme-switch.ts <name>.svg --theme=light → <name>.light.svg
  7. Run ${BUN_X} {baseDir}/scripts/main.ts <name>.svg → <name>@2x.png
  8. Run ${BUN_X} {baseDir}/scripts/main.ts <name>.svg --theme light -o <name>.light@2x.png → <name>.light@2x.png
  9. Present the files listed in Output Files to the user