Replace the charCount * fontSize * 0.55 text sizing heuristic with canvas-based measurement (graceful fallback when native deps unavailable). Add validate.mjs for automated spatial checks (text overflow, arrow-text collisions, element overlap). Update element format reference with sizing rules, label guidelines, and arrow routing conventions. Add verification step to SKILL.md workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.5 KiB
Excalidraw Element Format Reference
This reference documents the element JSON format accepted by the Excalidraw MCP create_view tool and the export_png.mjs script.
Color Palette
Primary Colors
| Name | Hex | Use |
|---|---|---|
| Blue | #4a9eed |
Primary actions, links |
| Amber | #f59e0b |
Warnings, highlights |
| Green | #22c55e |
Success, positive |
| Red | #ef4444 |
Errors, negative |
| Purple | #8b5cf6 |
Accents, special |
| Pink | #ec4899 |
Decorative |
| Cyan | #06b6d4 |
Info, secondary |
Fill Colors (pastel, for shape backgrounds)
| Color | Hex | Good For |
|---|---|---|
| Light Blue | #a5d8ff |
Input, sources, primary |
| Light Green | #b2f2bb |
Success, output |
| Light Orange | #ffd8a8 |
Warning, pending |
| Light Purple | #d0bfff |
Processing, middleware |
| Light Red | #ffc9c9 |
Error, critical |
| Light Yellow | #fff3bf |
Notes, decisions |
| Light Teal | #c3fae8 |
Storage, data |
Element Types
Required Fields (all elements)
type, id (unique string), x, y, width, height
Defaults (skip these)
strokeColor="#1e1e1e", backgroundColor="transparent", fillStyle="solid", strokeWidth=2, roughness=1, opacity=100
Shapes
Rectangle: { "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 100 }
roundness: { type: 3 }for rounded cornersbackgroundColor: "#a5d8ff",fillStyle: "solid"for filled
Ellipse: { "type": "ellipse", "id": "e1", "x": 100, "y": 100, "width": 150, "height": 150 }
Diamond: { "type": "diamond", "id": "d1", "x": 100, "y": 100, "width": 150, "height": 150 }
Labels
Labeled shape (preferred): Add label to any shape for auto-centered text.
{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 80, "label": { "text": "Hello", "fontSize": 20 } }
Standalone text (titles, annotations only):
{ "type": "text", "id": "t1", "x": 150, "y": 138, "text": "Hello", "fontSize": 20 }
Arrows
{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0, "points": [[0,0],[200,0]], "endArrowhead": "arrow" }
Bindings connect arrows to shapes:
"startBinding": { "elementId": "r1", "fixedPoint": [1, 0.5] }
fixedPoint: top=[0.5,0], bottom=[0.5,1], left=[0,0.5], right=[1,0.5]
Labeled arrow: "label": { "text": "connects" }
Camera (MCP only, not exported to PNG)
{ "type": "cameraUpdate", "width": 800, "height": 600, "x": 0, "y": 0 }
Camera sizes must be 4:3 ratio. The export script filters these out automatically.
Sizing Rules
Container-to-text ratios
- Box width >= estimated_text_width * 1.4 (40% horizontal margin)
- Box height >= estimated_text_height * 1.5 (50% vertical margin)
- Minimum box size: 150x60 for single-line labels, 200x80 for multi-line
Font size constraints
- Labels inside containers: max fontSize 14
- Service/zone titles: fontSize 18-22
- Standalone annotations: fontSize 12-14
- Never exceed fontSize 16 inside a box smaller than 300px wide
Padding
- Minimum 15px padding on each side between text and container edge
- For multi-line text, add 8px vertical padding per line beyond the first
General
- Leave 20-30px gaps between elements
Label Content Guidelines
Keep labels short
- Maximum 2 lines per label inside shapes
- Maximum 25 characters per line
- If label needs 3+ lines, split: short name in box, details as annotation below
Label patterns
- Service box: "Service Name" (1 line) or "Service Name\nBrief role" (2 lines)
- Component box: "Component Name" (1 line)
- Detail text: Use standalone text elements positioned below/beside the box
Bad vs Good
BAD: label "Auth-MS\nOAuth tokens, credentials\n800-1K req/s, <100ms" (3 lines, 30+ chars) GOOD: label "Auth-MS\nOAuth token management" (2 lines, 22 chars max) + standalone text below: "800-1K req/s, <100ms p99"
Arrow Routing Rules
Gutter-based routing
- Define horizontal and vertical gutters (20-30px gaps between service zones)
- Route arrows through gutters, never over content areas
- Use right-angle waypoints along zone edges
Waypoint placement
- Start/end points: attach to box edges using fixedPoint bindings
- Mid-waypoints: offset 20px from nearest box edge
- For crossing traffic: stagger parallel arrows by 10px
Vertical vs horizontal preference
- Prefer horizontal arrows for same-tier connections
- Prefer vertical arrows for cross-tier flows (consumer -> service -> external)
- Diagonal arrows only when routing around would add 3+ waypoints
Label placement on arrows
- Arrow labels should sit in empty space, not over boxes
- For vertical arrows: place label to the left or right, offset 15px
- For horizontal arrows: place label above, offset 10px
Example: Two Connected Boxes
[
{ "type": "cameraUpdate", "width": 800, "height": 600, "x": 50, "y": 50 },
{ "type": "rectangle", "id": "b1", "x": 100, "y": 100, "width": 200, "height": 100, "roundness": { "type": 3 }, "backgroundColor": "#a5d8ff", "fillStyle": "solid", "label": { "text": "Start", "fontSize": 20 } },
{ "type": "rectangle", "id": "b2", "x": 450, "y": 100, "width": 200, "height": 100, "roundness": { "type": 3 }, "backgroundColor": "#b2f2bb", "fillStyle": "solid", "label": { "text": "End", "fontSize": 20 } },
{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 150, "height": 0, "points": [[0,0],[150,0]], "endArrowhead": "arrow", "startBinding": { "elementId": "b1", "fixedPoint": [1, 0.5] }, "endBinding": { "elementId": "b2", "fixedPoint": [0, 0.5] } }
]