|
1 | 1 | # Excalidraw Diagram Skill |
2 | 2 |
|
3 | | -Create beautiful, professional, animated Excalidraw diagrams with progressive camera reveals, color-coded zones, and polished visual design. Use this skill whenever a user asks to diagram, visualize, map, chart, illustrate, or draw anything — including architecture diagrams, flowcharts, sequence diagrams, concept explainers, system maps, process flows, and technical overviews. Also trigger for requests like "show me how X works", "draw a diagram of", "create a visual for", "make an Excalidraw of", or any time a visual explanation would be clearer than text alone. |
| 3 | +Create clean, simple Excalidraw diagrams with progressive camera reveals. Use this skill when a user asks to diagram, visualize, flowchart, or illustrate anything. |
4 | 4 |
|
5 | 5 | --- |
6 | 6 |
|
7 | 7 | ## Step 1 — Always call read_me first |
8 | 8 |
|
9 | | -Before emitting ANY elements, call `Excalidraw:read_me`. Do not skip this step, even for simple diagrams. It provides the color palette, camera sizes, font rules, and element syntax required to produce clean output. |
10 | | - |
11 | | -``` |
12 | | -Excalidraw:read_me() |
13 | | -``` |
14 | | - |
15 | | -Then proceed directly to `Excalidraw:create_view` with your elements array — no narration about the read_me call. |
| 9 | +Before creating any elements, call `Excalidraw:read_me()` to get the color palette, camera sizes, and element syntax. Then proceed directly to `Excalidraw:create_view`. |
16 | 10 |
|
17 | 11 | --- |
18 | 12 |
|
19 | | -## Step 2 — Plan the diagram before writing elements |
| 13 | +## Step 2 — Planning (critical for spacing) |
20 | 14 |
|
21 | | -Before writing elements, mentally sketch: |
| 15 | +Before writing elements: |
22 | 16 |
|
23 | | -1. **What are the layers / zones?** (e.g. Frontend / Backend / Database, or Input / Process / Output) |
24 | | -2. **What color grammar makes sense?** Assign one color per layer and keep it consistent throughout |
25 | | -3. **How many camera positions do I need?** Plan 3–6 camera stops minimum for a reveal effect |
26 | | -4. **What's the reading order?** Left-to-right or top-to-bottom; pick one and stick to it |
| 17 | +1. **Sketch zones/layers** — What are the main sections? (Frontend/Backend, Input/Process/Output, etc.) |
| 18 | +2. **Assign colors** — One color per zone, used consistently |
| 19 | +3. **Plan camera positions** — 3–6 stops for the reveal effect |
| 20 | +4. **Plan spacing** — Draw it on paper first. Leave **minimum 60px vertical gaps** between rows, **40px horizontal gaps** between columns |
27 | 21 |
|
28 | 22 | --- |
29 | 23 |
|
30 | | -## Step 3 — Core design rules (MUST follow) |
31 | | - |
32 | | -### Camera rules |
33 | | -- **Always start with `cameraUpdate` as the first element** |
34 | | -- Camera sizes MUST be exact 4:3 ratios: `400x300`, `600x450`, `800x600`, `1200x900`, `1600x1200` |
35 | | -- Use **multiple cameraUpdates** throughout the array — pan to each section as you draw it |
36 | | -- Leave padding: if content is 500px wide, use 800x600 camera |
37 | | -- Final element should be a wide cameraUpdate showing the full diagram |
38 | | - |
39 | | -### Color grammar (use consistently) |
40 | | - |
41 | | -| Zone / Role | Fill | Stroke | |
42 | | -|---------------------|---------------|-----------| |
43 | | -| UI / Frontend | `#dbe4ff` | `#4a9eed` | |
44 | | -| Logic / Agent | `#e5dbff` | `#8b5cf6` | |
45 | | -| Data / Storage | `#d3f9d8` | `#22c55e` | |
46 | | -| External / API | `#ffd8a8` | `#f59e0b` | |
47 | | -| Error / Alert | `#ffc9c9` | `#ef4444` | |
48 | | -| Notes / Decisions | `#fff3bf` | `#f59e0b` | |
49 | | - |
50 | | -Zone background rectangles: use `opacity: 40`, `fillStyle: "solid"` |
51 | | - |
52 | | -Node shapes: use pastel fills (`#a5d8ff`, `#b2f2bb`, `#d0bfff`, `#ffd8a8`, `#c3fae8`, `#eebefa`) |
53 | | - |
54 | | -### Typography rules |
55 | | -- Title: `fontSize: 26–28`, `strokeColor: "#1e1e1e"` |
56 | | -- Subtitle / annotation: `fontSize: 16`, `strokeColor: "#757575"` |
57 | | -- Shape labels: `fontSize: 16–18` via `label` property on the shape |
58 | | -- NEVER use fontSize below 14 |
59 | | -- NEVER use light gray on white backgrounds (minimum text color: `#757575`) |
60 | | - |
61 | | -### Shape rules |
62 | | -- Use `label: { "text": "...", "fontSize": 16 }` directly on shapes — no separate text elements |
63 | | -- Minimum shape size: `120x60` for labeled boxes |
64 | | -- Add `roundness: { type: 3 }` for rounded corners (preferred for nodes) |
65 | | -- Leave 20–30px gaps between elements |
66 | | - |
67 | | -### Drawing order (z-order, critical) |
68 | | -Emit in this sequence per section: |
69 | | -1. Zone background rectangle (drawn first = sits behind) |
| 24 | +## Step 3 — Core rules (MUST follow) |
| 25 | + |
| 26 | +### Camera |
| 27 | +- Start with `cameraUpdate` as the **first element** |
| 28 | +- Use exact 4:3 ratios: `400x300`, `600x450`, `800x600`, `1200x900`, `1600x1200` |
| 29 | +- Pan to each section as you draw it |
| 30 | +- End with a wide view showing the full diagram |
| 31 | + |
| 32 | +### Spacing (prevent overlaps) |
| 33 | +- **Minimum vertical gap between rows: 60px** |
| 34 | +- **Minimum horizontal gap between columns: 40px** |
| 35 | +- Check all y-coordinates: if row 1 ends at y=180, row 2 starts at y=240 or later |
| 36 | +- Zone label at top-left (y+8px from zone top), nodes start 40px below label |
| 37 | +- Never place text directly on shape edges — add 8–10px padding |
| 38 | + |
| 39 | +### Color grammar |
| 40 | +| Zone | Fill | Stroke | |
| 41 | +|------|------|--------| |
| 42 | +| UI / Frontend | `#dbe4ff` | `#4a9eed` | |
| 43 | +| Logic / Agent | `#e5dbff` | `#8b5cf6` | |
| 44 | +| Data / Storage | `#d3f9d8` | `#22c55e` | |
| 45 | +| External / API | `#ffd8a8` | `#f59e0b` | |
| 46 | +| Error / Alert | `#ffc9c9` | `#ef4444` | |
| 47 | +| Notes | `#fff3bf` | `#f59e0b` | |
| 48 | + |
| 49 | +### Typography |
| 50 | +- Title: `fontSize: 28`, `strokeColor: "#1e1e1e"` |
| 51 | +- Labels: `fontSize: 16–18` via `label` property |
| 52 | +- **Never use fontSize below 14** |
| 53 | +- Minimum text color: `#757575` (never light gray on white) |
| 54 | + |
| 55 | +### Drawing order (z-order) |
| 56 | +Per section, emit in this order: |
| 57 | +1. Zone background rectangle (sits behind) |
70 | 58 | 2. Zone label text |
71 | 59 | 3. Node shapes (with labels) |
72 | 60 | 4. Arrows between nodes |
73 | | -5. Then next section |
74 | 61 |
|
75 | | -NEVER dump all rectangles, then all text, then all arrows. |
| 62 | +**Never dump all rectangles first, then all text.** |
| 63 | + |
| 64 | +### Shapes |
| 65 | +- Use `label: { "text": "...", "fontSize": 16 }` on shapes — no separate text elements |
| 66 | +- Minimum size: `120x60` for labeled boxes |
| 67 | +- Add `roundness: { type: 3 }` for rounded corners |
76 | 68 |
|
77 | | -### Arrow rules |
78 | | -- Always include `endArrowhead: "arrow"` for directional flow |
79 | | -- Use `strokeStyle: "dashed"` for responses, return values, optional paths |
80 | | -- Keep arrow labels short (under 20 chars) or omit — long labels overflow |
81 | | -- Use `startBinding` / `endBinding` with `fixedPoint` to attach to shapes |
| 69 | +### Arrows |
| 70 | +- Include `endArrowhead: "arrow"` for direction |
| 71 | +- Use `strokeStyle: "dashed"` for responses/optional paths |
| 72 | +- Keep labels **under 15 characters** or omit |
| 73 | +- Use `startBinding` / `endBinding` with `fixedPoint` to attach cleanly |
82 | 74 |
|
83 | 75 | --- |
84 | 76 |
|
85 | 77 | ## Step 4 — Diagram type patterns |
86 | 78 |
|
87 | | -### Architecture / System Diagram |
88 | | -Zones as swim lanes (left-to-right or top-to-bottom). Each zone = one architectural layer. Arrows show data/request flow between layers. End with a full-width cameraUpdate. |
89 | | - |
90 | | -**Camera pattern:** Title zoom (M) → pan right zone by zone (S/M) → final overview (XL) |
| 79 | +**Architecture** — Zones as swim lanes (left-to-right). Arrows show data flow. |
91 | 80 |
|
92 | | -### Sequence / Flow Diagram |
93 | | -Actors as header boxes with dashed vertical lifelines. Horizontal arrows show messages. Pan camera downward as messages progress. |
| 81 | +**Sequence** — Actors as header boxes with lifelines. Horizontal arrows = messages. Pan down as flow progresses. |
94 | 82 |
|
95 | | -**Camera pattern:** Title (M) → pan right per actor drawing header + lifeline → zoom out (L) → pan down per message group → final overview (XL) |
| 83 | +**Process/Flowchart** — Top-to-bottom. Diamonds for decisions, rectangles for steps. Color-code by stage. |
96 | 84 |
|
97 | | -### Concept Explainer |
98 | | -Start zoomed on the title, then reveal parts of the concept one at a time. Use annotations (`#fff3bf` boxes) as callouts. Simple left-to-right flow. |
99 | | - |
100 | | -**Camera pattern:** Title zoom (S) → zoom out (M) → pan section by section → final (L) |
101 | | - |
102 | | -### Process / Flowchart |
103 | | -Diamonds for decisions, rectangles for steps. Top-to-bottom flow. Color-code by stage (e.g. initiation=blue, processing=purple, output=green). |
104 | | - |
105 | | -**Camera pattern:** Top zoom → pan down per stage group → final overview |
| 85 | +**Concept** — Start zoomed on title, reveal parts progressively. Use annotation boxes as callouts. |
106 | 86 |
|
107 | 87 | --- |
108 | 88 |
|
109 | | -## Step 5 — The camera reveal technique (what makes diagrams feel alive) |
| 89 | +## Step 5 — The reveal animation |
110 | 90 |
|
111 | | -The secret to great Excalidraw diagrams is **drawing section by section with camera moves**: |
| 91 | +Draw section by section with camera moves: |
112 | 92 |
|
113 | 93 | ```json |
114 | | -// 1. Start with title, zoomed in |
| 94 | +// 1. Title, zoomed |
115 | 95 | {"type":"cameraUpdate","width":600,"height":450,"x":100,"y":0}, |
116 | 96 | {"type":"text","id":"t1","x":200,"y":20,"text":"My Diagram","fontSize":28}, |
117 | 97 |
|
118 | | -// 2. Pan to first zone and draw it |
| 98 | +// 2. Pan to zone 1, draw it |
119 | 99 | {"type":"cameraUpdate","width":400,"height":300,"x":20,"y":60}, |
120 | | -{"type":"rectangle","id":"zone1", ...zone background...}, |
121 | | -{"type":"rectangle","id":"node1", ...node with label...}, |
| 100 | +{"type":"rectangle","id":"zone1","x":20,"y":80,"width":220,"height":380, ...}, |
| 101 | +{"type":"rectangle","id":"node1","x":60,"y":130, ...}, |
122 | 102 |
|
123 | | -// 3. Pan to second zone |
| 103 | +// 3. Pan to zone 2 |
124 | 104 | {"type":"cameraUpdate","width":400,"height":300,"x":280,"y":60}, |
125 | | -{"type":"rectangle","id":"zone2", ...}, |
126 | | -{"type":"rectangle","id":"node2", ...}, |
| 105 | +{"type":"rectangle","id":"node2","x":320,"y":130, ...}, |
127 | 106 |
|
128 | | -// 4. Draw connecting arrows (camera stays or pans to show both ends) |
| 107 | +// 4. Draw arrows |
129 | 108 | {"type":"cameraUpdate","width":800,"height":600,"x":0,"y":40}, |
130 | | -{"type":"arrow","id":"a1", ...arrow from node1 to node2...}, |
| 109 | +{"type":"arrow","id":"a1", ...}, |
131 | 110 |
|
132 | | -// 5. Final wide overview |
| 111 | +// 5. Final wide view |
133 | 112 | {"type":"cameraUpdate","width":1200,"height":900,"x":-20,"y":-10} |
134 | 113 | ``` |
135 | 114 |
|
136 | | -This creates the "drawing itself" animation effect users love. |
137 | | - |
138 | 115 | --- |
139 | 116 |
|
140 | | -## Step 6 — Common mistakes to avoid |
| 117 | +## Step 6 — Overlap prevention checklist |
141 | 118 |
|
142 | | -- **No cameraUpdate first** → diagram appears un-framed, elements clip |
143 | | -- **Wrong aspect ratio** → `700x500` causes distortion; use `800x600` |
144 | | -- **All elements at once, no panning** → loses the reveal animation |
145 | | -- **Overlapping elements** → check y-coordinates leave 60–80px between rows |
146 | | -- **Long arrow labels** → overflow the arrow; keep under 20 chars or use a note box instead |
147 | | -- **Emoji in text** → don't render in Excalidraw's font |
148 | | -- **Light text on white** → `#b0b0b0` on white is invisible; minimum `#757575` |
149 | | -- **Zone label covered by nodes** → put zone label text at top-left of zone (y + 8px from zone top), nodes start 40px below |
150 | | -- **Title not centered** → estimate `text.length x fontSize x 0.5` for width, then set `x = diagramCenterX - estimatedWidth/2` |
| 119 | +- [ ] All shapes have **at least 60px vertical separation** |
| 120 | +- [ ] Zone labels don't overlap with nodes (40px minimum gap below label) |
| 121 | +- [ ] Arrows don't cross unrelated zones |
| 122 | +- [ ] Text is 8–10px inside shape bounds (not on edges) |
| 123 | +- [ ] Zone backgrounds are drawn BEFORE nodes |
| 124 | +- [ ] Arrows drawn AFTER both source and target |
| 125 | +- [ ] No shape dimensions smaller than `100x50` |
| 126 | +- [ ] All camera sizes are valid 4:3 ratios |
| 127 | +- [ ] Final element is a wide cameraUpdate |
| 128 | +- [ ] Minimum 3 camera positions for animation |
151 | 129 |
|
152 | 130 | --- |
153 | 131 |
|
154 | | -## Step 7 — Quality checklist before emitting |
| 132 | +## Step 7 — Common mistakes |
155 | 133 |
|
156 | | -- [ ] `Excalidraw:read_me` called |
157 | | -- [ ] First element is `cameraUpdate` |
158 | | -- [ ] All camera sizes are valid 4:3 ratios |
159 | | -- [ ] Minimum 3 camera positions used (more = better animation) |
160 | | -- [ ] Color grammar is consistent across zones |
161 | | -- [ ] All shape labels use `label` property, not separate text elements |
162 | | -- [ ] No font sizes below 14 |
163 | | -- [ ] Zone backgrounds are drawn BEFORE the nodes inside them |
164 | | -- [ ] Arrows drawn AFTER both source and target shapes |
165 | | -- [ ] Final element is a wide cameraUpdate revealing the full diagram |
166 | | -- [ ] No emoji in any text strings |
| 134 | +- **Overlapping text** — Check y-coordinates strictly; use 60px gaps minimum |
| 135 | +- **No cameraUpdate first** — Elements clip and look wrong |
| 136 | +- **All elements at once** — Loses the animation; use multiple camerUpdates |
| 137 | +- **Long arrow labels** — Overflow; keep under 15 chars |
| 138 | +- **Light text on white** — Use `#757575` minimum |
| 139 | +- **Zone label covered by nodes** — Put label 8px from zone top, nodes 40px below |
| 140 | +- **Shapes touching edges** — Leave padding; awkward layout |
167 | 141 |
|
168 | 142 | --- |
169 | 143 |
|
170 | | -## Reference: Element snippets |
| 144 | +## Reference: Snippets |
171 | 145 |
|
172 | 146 | **Zone background:** |
173 | 147 | ```json |
174 | | -{"type":"rectangle","id":"zone_bg","x":20,"y":80,"width":220,"height":380,"backgroundColor":"#dbe4ff","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#4a9eed","strokeWidth":1,"opacity":40} |
175 | | -``` |
176 | | - |
177 | | -**Zone label:** |
178 | | -```json |
179 | | -{"type":"text","id":"zone_lbl","x":40,"y":88,"text":"FRONTEND","fontSize":14,"strokeColor":"#2563eb"} |
| 148 | +{"type":"rectangle","id":"z1","x":20,"y":80,"width":220,"height":380,"backgroundColor":"#dbe4ff","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#4a9eed","strokeWidth":1,"opacity":40} |
180 | 149 | ``` |
181 | 150 |
|
182 | 151 | **Node:** |
183 | 152 | ```json |
184 | | -{"type":"rectangle","id":"n1","x":60,"y":130,"width":150,"height":55,"backgroundColor":"#a5d8ff","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#4a9eed","strokeWidth":2,"label":{"text":"API Gateway","fontSize":16}} |
| 153 | +{"type":"rectangle","id":"n1","x":60,"y":130,"width":150,"height":55,"backgroundColor":"#a5d8ff","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#4a9eed","strokeWidth":2,"label":{"text":"API","fontSize":16}} |
185 | 154 | ``` |
186 | 155 |
|
187 | | -**Arrow (solid, directed):** |
| 156 | +**Arrow:** |
188 | 157 | ```json |
189 | 158 | {"type":"arrow","id":"a1","x":210,"y":157,"width":100,"height":0,"points":[[0,0],[100,0]],"strokeColor":"#1e1e1e","strokeWidth":2,"endArrowhead":"arrow","startBinding":{"elementId":"n1","fixedPoint":[1,0.5]},"endBinding":{"elementId":"n2","fixedPoint":[0,0.5]}} |
190 | 159 | ``` |
191 | 160 |
|
192 | | -**Arrow (dashed, response):** |
193 | | -```json |
194 | | -{"type":"arrow","id":"a2","x":310,"y":157,"width":-100,"height":0,"points":[[0,0],[-100,0]],"strokeColor":"#757575","strokeWidth":2,"strokeStyle":"dashed","endArrowhead":"arrow"} |
195 | | -``` |
196 | | - |
197 | | -**Annotation note:** |
198 | | -```json |
199 | | -{"type":"rectangle","id":"note1","x":80,"y":200,"width":200,"height":36,"backgroundColor":"#fff3bf","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#f59e0b","strokeWidth":1,"opacity":80,"label":{"text":"Caches for 5 min","fontSize":14}} |
200 | | -``` |
201 | | - |
202 | | -**Title text:** |
203 | | -```json |
204 | | -{"type":"text","id":"title","x":150,"y":15,"text":"System Architecture","fontSize":28,"strokeColor":"#1e1e1e"} |
205 | | -``` |
206 | | - |
207 | | -**Stick figure (user icon):** |
| 161 | +**Annotation:** |
208 | 162 | ```json |
209 | | -{"type":"ellipse","id":"fig_head","x":58,"y":110,"width":20,"height":20,"backgroundColor":"#a5d8ff","fillStyle":"solid","strokeColor":"#4a9eed","strokeWidth":2}, |
210 | | -{"type":"rectangle","id":"fig_body","x":57,"y":132,"width":22,"height":26,"backgroundColor":"#a5d8ff","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#4a9eed","strokeWidth":2} |
| 163 | +{"type":"rectangle","id":"note1","x":80,"y":200,"width":200,"height":36,"backgroundColor":"#fff3bf","fillStyle":"solid","roundness":{"type":3},"strokeColor":"#f59e0b","strokeWidth":1,"opacity":80,"label":{"text":"Note here","fontSize":14}} |
211 | 164 | ``` |
0 commit comments