Skip to content
Development
Skill

/excalidraw

Create hand-drawn style diagrams using Excalidraw JSON format. Generate .excalidraw files for architecture diagrams, flowcharts, sequence diagrams, concept maps, and more. Files can be opened at excalidraw.com or uploaded for shareable links.

From plugin
zorro-agent
1078 skills

Claude Code

$ npx -y skills add braxtonROSE4/zorro-agent --skill excalidraw --agent claude-code

SKILL.md

excalidraw.SKILL.md
name: excalidraw
description: Create hand-drawn style diagrams using Excalidraw JSON format. Generate .excalidraw files for architecture diagrams, flowcharts, sequence diagrams, concept maps, and more. Files can be opened at excalidraw.com or uploaded for shareable links.
version: 1.0.0
author: Zorro Agent
license: MIT
dependencies: []
metadata:
  zorro:
    tags: [Excalidraw, Diagrams, Flowcharts, Architecture, Visualization, JSON]
    related_skills: []

Excalidraw Diagram Skill

Create diagrams by writing standard Excalidraw element JSON and saving as `.excalidraw` files. These files can be drag-and-dropped onto [excalidraw.com](https://excalidraw.com) for viewing and editing. No accounts, no API keys, no rendering libraries -- just JSON.

Workflow

1. **Load this skill** (you already did) 2. **Write the elements JSON** -- an array of Excalidraw element objects 3. **Save the file** using `write_file` to create a `.excalidraw` file 4. **Optionally upload** for a shareable link using `scripts/upload.py` via `terminal`

Saving a Diagram

Wrap your elements array in the standard `.excalidraw` envelope and save with `write_file`:

{
  "type": "excalidraw",
  "version": 2,
  "source": "zorro-agent",
  "elements": [ ...your elements array here... ],
  "appState": {
    "viewBackgroundColor": "#ffffff"
  }
}

Save to any path, e.g. `~/diagrams/my_diagram.excalidraw`.

Uploading for a Shareable Link

Run the upload script (located in this skill's `scripts/` directory) via terminal:

python skills/diagramming/excalidraw/scripts/upload.py ~/diagrams/my_diagram.excalidraw

This uploads to excalidraw.com (no account needed) and prints a shareable URL. Requires the `cryptography` pip package (`pip install cryptography`).

---

Element Format Reference

Required Fields (all elements)

`type`, `id` (unique string), `x`, `y`, `width`, `height`

Defaults (skip these -- they're applied automatically)

  • `strokeColor`: `"#1e1e1e"`
  • `backgroundColor`: `"transparent"`
  • `fillStyle`: `"solid"`
  • `strokeWidth`: `2`
  • `roughness`: `1` (hand-drawn look)
  • `opacity`: `100`

Canvas background is white.

Element Types

**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 }

**Labeled shape (container binding)** -- create a text element bound to the shape:

> **WARNING:** Do NOT use `"label": { "text": "..." }` on shapes. This is NOT a valid > Excalidraw property and will be silently ignored, producing blank shapes. You MUST > use the container binding approach below.

The shape needs `boundElements` listing the text, and the text needs `containerId` pointing back:

{ "type": "rectangle", "id": "r1", "x": 100, "y": 100, "width": 200, "height": 80,
  "roundness": { "type": 3 }, "backgroundColor": "#a5d8ff", "fillStyle": "solid",
  "boundElements": [{ "id": "t_r1", "type": "text" }] },
{ "type": "text", "id": "t_r1", "x": 105, "y": 110, "width": 190, "height": 25,
  "text": "Hello", "fontSize": 20, "fontFamily": 1, "strokeColor": "#1e1e1e",
  "textAlign": "center", "verticalAlign": "middle",
  "containerId": "r1", "originalText": "Hello", "autoResize": true }
  • Works on rectangle, ellipse, diamond
  • Text is auto-centered by Excalidraw when `containerId` is set
  • The text `x`/`y`/`width`/`height` are approximate -- Excalidraw recalculates them on load
  • `originalText` should match `text`
  • Always include `fontFamily: 1` (Virgil/hand-drawn font)

**Labeled arrow** -- same container binding approach:

{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0,
  "points": [[0,0],[200,0]], "endArrowhead": "arrow",
  "boundElements": [{ "id": "t_a1", "type": "text" }] },
{ "type": "text", "id": "t_a1", "x": 370, "y": 130, "width": 60, "height": 20,
  "text": "connects", "fontSize": 16, "fontFamily": 1, "strokeColor": "#1e1e1e",
  "textAlign": "center", "verticalAlign": "middle",
  "containerId": "a1", "originalText": "connects", "autoResize": true }

**Standalone text** (titles and annotations only -- no container):

{ "type": "text", "id": "t1", "x": 150, "y": 138, "text": "Hello", "fontSize": 20,
  "fontFamily": 1, "strokeColor": "#1e1e1e", "originalText": "Hello", "autoResize": true }
  • `x` is the LEFT edge. To center at position `cx`: `x = cx - (text.length * fontSize * 0.5) / 2`
  • Do NOT rely on `textAlign` or `width` for positioning

**Arrow**:

{ "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 200, "height": 0,
  "points": [[0,0],[200,0]], "endArrowhead": "arrow" }
  • `points`: `[dx, dy]` offsets from element `x`, `y`
  • `endArrowhead`: `null` | `"arrow"` | `"bar"` | `"dot"` | `"triangle"`
  • `strokeStyle`: `"solid"` (default) | `"dashed"` | `"dotted"`

Arrow Bindings (connect arrows to shapes)

{
  "type": "arrow", "id": "a1", "x": 300, "y": 150, "width": 150, "height": 0,
  "points": [[0,0],[150,0]], "endArrowhead": "arrow",
  "startBinding": { "elementId": "r1", "fixedPoint": [1, 0.5] },
  "endBinding": { "elementId": "r2", "fixedPoint": [0, 0.5] }
}

`fixedPoint` coordinates: `top=[0.5,0]`, `bottom=[0.5,1]`, `left=[0,0.5]`, `right=[1,0.5]`

Drawing Order (z-order)

  • Array order = z-order (first = back, last = front)
  • Emit progressively: background zones → shape → its bound text → its arrows → next shape
  • BAD: all rectangles, then all texts, then all arrows
  • GOOD: bg_zone → shape1 → text_for_shape1 → arrow1 → arrow_label_text → shape2 → text_for_shape2 → ...
  • Always place the bound text element immediately after its container shape

Sizing Guidelines

**Font sizes:**

  • Minimum
Read more
@braxtonrose4
Ships withzorro-agent

A self-evolving CLI agent. Most agents treat memory as an afterthought — a flat text file that grows until it's useless.

Stars on GitHub

Momentum

stars in total
10
Star history of braxtonROSE4/zorro-agent on GitHub, a chart by star-history.com

Other skills on zorro-agent.

claude-code

claude-code

Delegate coding tasks to Claude Code (Anthropic's CLI agent). Use for building features, refactoring, PR reviews, and iterative…

codex

codex

Delegate coding tasks to OpenAI Codex CLI agent. Use for building features, refactoring, PR reviews, and batch issue fixing.…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about excalidraw.

Which coding agents can use excalidraw?
excalidraw installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.