# Drawing-card JSON schema (T-317 / D-91 / D-103) — draft Status: **draft for build**, SVG-substrate model (D-103), refined from the T-317 wireframe set, 2026-06-28. ## Model — what this is, and what it is NOT - **SVG is the substrate.** The card's primitive / scene-graph layer **is SVG**; the clide-owned `CustomPaint` **SVG renderer (T-320) is the engine** the rest builds on (D-103). - It is **not** the HTML Canvas 2D API and **not** a third-party package. SVG is a document *format* we render ourselves — "own the rendering stack" holds. ("HTML ``" in D-91 was only a mental model, chosen to reject Obsidian's `.canvas` schema; never an API to port.) - The card is **two layers**: 1. **SVG content** — painted by the SVG renderer. 2. A thin **Flutter overlay** — the clide chrome that is *not* content (per-object label/description captions, lightbox affordance), anchored to SVG elements via `data-*` attributes. - **Display-only** (D-78); re-rendered from the document. The **graph template is the live-widget exception** (below). ## Document envelope ```json { "card": { "label": "Build pipeline", "description": "…" }, // optional caption (overlay) "template": "icon", // optional → template sugar "…template fields…": "…", "svg": "" // primitive mode = raw SVG // or "svgPath": "diagram.svg" } ``` - **Template mode** — `template` names a component; clide lowers it to SVG (+ overlay anchors). - **Primitive mode** — `svg` (inline) or `svgPath` — arbitrary SVG, the escape hatch. One less invented format; external SVG / graphviz / mermaid render free. - Card size comes from the SVG `viewBox` (or `width`/`height`); the painter scales to the pane width. ## Primitive layer = a bounded SVG subset Grounded in a real d2 sample + our own templates — **not** a full SVG engine: - **structure:** `` (viewBox/width/height, incl. nested ``), `` (transform, opacity, class), ``, `` (+ marker-start/mid/end, `orient="auto"`, refX/refY, viewBox) — edge **arrowheads** - **shapes:** `rect` (rx/ry), `circle`, `ellipse`, `line`, `polyline`, `polygon`, `path` (full data — `M L H V C S Q T A Z` + relatives) - **text:** `text` + `tspan` (x/y/dx/dy, font-family incl. **Phosphor**, font-size/weight, text-anchor, dominant-baseline) - **raster:** `image` (`href`/`xlink:href`, x/y/w/h, preserveAspectRatio) - **styling:** presentation attrs (fill, fill-opacity, stroke, stroke-width, stroke-linecap/linejoin, stroke-dasharray, opacity, color), `transform` (translate/scale/rotate/matrix); `class=` resolved by the normalizer below - **deferred v1:** `` (d2 masks connections for clean edge/node joins) — ignore and lean on node-over-edge paint order; add only if output looks wrong - **out:** `foreignObject`, filters, ``/SMIL, scripting, ``/``, gradients, patterns, `clipPath` → **mermaid is not a launch target** (it leans on `foreignObject`) `color` everywhere is an **arbitrary value** (hex / named) — content, not a clide `SurfaceTokens` token (D-7 governs clide chrome, not rendered content). ### Class styling → inline-normalize (not a render-time CSS engine) d2 / graphviz emit a `