Files
John Lamb f524c1b9d8 feat(excalidraw): improve diagram quality with canvas measurement, validation, and conventions
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>
2026-02-26 17:19:14 -06:00

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 corners
  • backgroundColor: "#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] } }
]