- name
- docs-svg-kit
- description
- Author SVG figures for Grida docs — diff-able, version-controlled vector diagrams embedded in doc pages instead of screenshots. Provides reusable primitives (selection chrome, size badges, anchor pins, resize cursors, click ripples), color/typography tokens, a starter template, and finished examples to crib from. Canvas user docs (docs/editor/) are the first consumer; the kit is meant to generalize to any product's docs. Use when drawing diagrams that explain UI behaviour — gestures, alignment, before/after states — that a screenshot alone can't capture. Trigger phrases: "svg diagram", "draw a figure", "visual for docs", "explain this gesture visually", "before/after diagram".
# Docs SVG Kit
A snippet library for drawing SVG figures embedded in Grida docs. Canvas
user docs are the kit's first consumer; the same primitives and
conventions are meant to carry over to any product's docs as they adopt
SVG figures.
**First consumer / companion:**
[`docs-canvas`](../docs-canvas/SKILL.md). Use that skill for the prose,
this one for the visuals embedded inside it.
## Why SVG, not a screenshot
Screenshots are easy to capture but expensive over time:
- **Stale by default.** UI shifts; the screenshot keeps showing the old state until someone re-captures it. There is no diff, no warning, no test that fails.
- **Repo weight.** Every re-capture is a new binary blob in git history. WebP at 960 × 960 is ~30–80 KB each; PNG is multiples of that. Across years of captures it compounds, and old blobs never leave history.
- **Frozen in time.** A screenshot shows one moment of one configuration. A diagram explains the rule across configurations.
So: **when SVG can carry the meaning, prefer SVG.** SVGs are text — diff-able, hand-editable, version-controlled like code. Reserve screenshots for cases where the actual rendered chrome (real fonts, real pixels) is the point.
## When to draw an SVG figure
Reach for a custom SVG when a screenshot can't carry the story alone:
- A **gesture** (the clickable target isn't visible at rest — double-click, drag, hover).
- A **before / after** that needs the two states side-by-side.
- A **rule** that hinges on an invisible anchor or pivot (alignment, snapping, hit-testing).
If a single screenshot of the editor would communicate the whole point, use a screenshot. Don't redraw real UI in SVG when you don't have to.
## Hard constraints
These are baked into the kit. Don't fight them.
- **Inline `<style>` only.** Never reference an external stylesheet. Docusaurus serves docs SVGs via `<img src>`, which sandboxes the SVG: external CSS, scripts, and cross-file `<use href>` are all blocked.
- **No cross-file `<use>`.** Every reused fragment must physically live inside the SVG's own `<defs>`. Copy from `snippets/`, don't import.
- **Self-contained = SEO-safe.** Inline-styled SVGs are indexed as images. Inline React components in MDX are not — they become HTML.
- **Output size: 960 × 960.** The viewport for all figures, matching `docs/AGENTS.md` screenshot conventions. Use `viewBox="0 0 960 960" width="960" height="960"`.
- **Watermark every kit-produced SVG** (see below).
- **No emojis. No marketing voice.** Same neutral tone as the prose.
## Watermark every kit-produced SVG
So we can enumerate, audit, and migrate kit assets later without reading each file, **every SVG produced via this kit must carry two markers**:
1. A top-level XML comment immediately before the root `<svg>` (grep-friendly):
```xml
<!-- @generated-by: docs-svg-kit v1 -->
```
2. A `<metadata>` element as the first child of `<svg>` (SVG-spec-native, survives minification and SVGO passes that strip comments):
```xml
<metadata>docs-svg-kit/v1</metadata>
```
Both markers are present in `snippets/template.svg`, so they propagate automatically when you copy from the template. If you start a figure from scratch, paste both before composing anything else.
**Versioning.** Bump `v1 → v2` only on a breaking change to tokens or primitives (e.g. selection blue changes hex, or anchor pin geometry changes). Same-version figures are guaranteed visually consistent.
**Enumerate kit assets:**
```sh
grep -rl "docs-svg-kit" docs/editor/
```
When the kit changes, walk the grep output and update each file.
## Workflow
1. **Start from the template.** Copy [`snippets/template.svg`](snippets/template.svg) — it has the canvas, fonts, all `<defs>`, all class tokens, and shadow filters wired up.
2. **Browse [`snippets/primitives.svg`](snippets/primitives.svg)** for the bird's-eye catalog of every primitive. For deeper inspection of a specific family, open the focused chunk file (see _Snippet files_ below).
3. **Compose your figure** inside the marked region of the template, using the existing classes. Don't reinvent stroke widths, colors, or fonts — that's how visuals drift.
4. **Crib structure from [`examples/`](examples/).** Two finished figures live there:
- `text-node-auto-size-edges.svg` — two-column before/after with hot edges, ripples, cursors, size badges, and a legend card.
- `text-node-auto-size-alignment.svg` — three-column comparison with anchor pins and ghost outlines.
5. **Save to `docs/editor/resources/`.** Name: `<feature>-<description>.svg`, kebab-case.
6. **Embed via Markdown.** `` from a doc under `docs/editor/features/`. The `alt` should describe what the figure shows, not name the file.
## Snippet files
`snippets/` is structured as **one overview catalog plus focused per-family files**:
| File | Role |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `template.svg` | **Starter** — copy this to begin a new figure. Has every CSS class, def, filter, and symbol pre-wired. |
| `primitives.svg` | **Index / overview catalog** — every primitive in one tall canvas. Skim here first. |
| `cursors.svg` | **Cursors** — pointer, move, grab, grabbing, rotate, h-resize, v-resize, crosshair, text. With sizes and hotspot offsets. |
| `meters.svg` | **Meters** — `.badge` (size, blue) and `.badge-distance` (measurement, red) variants side-by-side. |
| `handles.svg` | **Handles & pins** — square `.handle`, `#ft-handle` (circle), `#anchor-pin`. |
| `keycap.svg` | **Keycap** — modifier-key pill variants (single letter, glyph, multi-char, combos). |
**When to look in which:**
- Quick scan of "what exists" → `primitives.svg`.
- Picking a specific cursor / badge variant / handle / keycap → the per-family file. They show variants, sizes, hotspots, and source references that the index can only hint at.
> **Note on canvas size.** Snippet files exceed the 960 × 960 figure mandate (e.g. `cursors.svg` is 960 × 720, `primitives.svg` is 960 × 1400). That mandate applies to **doc figures** — kit reference catalogs are not embedded in docs and may be tall.
## What's in the kit
### Primitives (in `snippets/template.svg` defs)
| Primitive | How to use | Where it shines |
| ------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| **Selection chrome** | `<rect class="sel-outline">` + four `<rect class="handle">` corners + `class="baseline"` | Showing a selected node |
| **Size badge** | `<rect class="badge">` + `<text class="badge-text">` with `filter="url(#badge-shadow)"` | Live `w × h` readout under selection |
| **Anchor pin** | `<g transform="translate(cx,cy)"><use href="#anchor-pin"/></g>` + `filter="url(#pin-shadow)"` | A point that stays fixed across states |
| **Resize cursor** | `<use href="#cursor-h">` or `<use href="#cursor-v">` | Edges/handles that respond to a drag |
| **Click ripple** | Two concentric `<circle class="ripple">` / `class="ripple-2">` | Marking a tap or double-click target |
| **Hot edge** | `<line class="edge-hot">` | The edge a gesture targets |
| **Ghost outline** | `<rect class="ghost">` | Where the bounds used to be |
| **Arrow (gray)** | `<line class="arrow" marker-end="url(#ah)">` | Generic flow / step transitions |
| **Arrow (red)** | `<line class="arrow-red" marker-end="url(#ah-red)">` | Motion or change being highlighted |
| **Legend card** | `<rect class="legend-box">` rounded panel | Symbol key at the bottom of a figure |
| **Distance meter** | `<rect class="badge-distance">` + `<text class="badge-text">` with `filter="url(#badge-shadow)"` | Measurement readout (red variant of size badge) |
| **Keycap** | `<rect class="kbd-bg">` + `<text class="kbd-text">` (h≈20, rx=3, px=8) | Modifier-key pill (Alt, Shift, ⌘) in interaction figures |
| **Free-transform handle** | `<g transform="translate(cx,cy)"><use href="#ft-handle"/></g>` | Circular corner grip — rotate / scale gesture target |
| **Cursor — pointer** | `<use href="#cursor-pointer">` | Default arrow cursor |
| **Cursor — move** | `<use href="#cursor-move">` | 4-direction translate gesture |
| **Cursor — grab** | `<use href="#cursor-grab">` / `<use href="#cursor-grabbing">` | Drag affordance (open/closed hand) |
| **Cursor — rotate** | `<use href="#cursor-rotate">` (wrap in `transform="rotate(θ)"` to orient) | Rotation drag at a corner |
### Color tokens
| Class / value | Hex | Use |
| ------------- | --------- | ----------------------------------------------------------------- |
| canvas bg | `#ECECEC` | Page background (matches editor canvas) |
| selection | `#0D99FF` | Selection outline, handles, baseline, size badge |
| hot | `#FF3B30` | Hot edges, anchor pin, motion arrows |
| measurement | `#f44336` | Distance badge — `WorkbenchColors.red`, the canonical product red |
| text | `#0a0a0a` | Headings, node text content |
| caption | `#6b7280` | Subtitles, captions, legend captions |
| ghost | `#b5b5b5` | Dashed outline of original/previous bounds |
| keycap bg | `#f1f5f9` | Keycap pill background (`bg-muted` from kbd.tsx) |
| keycap border | `#cbd5e1` | Keycap pill border |
| keycap text | `#475569` | Keycap label text (`text-muted-foreground`) |
| panel border | `#e5e5e5` | Legend card border, cell borders in catalog |
> **Slight drift, intentional.** The kit's `selection` (`#0D99FF`) is Figma blue; the product's canonical `WorkbenchColors.sky` is `#00a6f4`. Same for `hot` (`#FF3B30`) vs `WorkbenchColors.red` (`#f44336`). The new `measurement` token uses the canonical product red. A future kit pass may harmonise these — for now, the difference is visually negligible (~3% off) and keeps existing figures stable.
### Typography tokens
| Class | Spec | Use |
| -------------- | -------------------------------------------- | ----------------------------- |
| `.doc-heading` | 26px, 600, `-0.3px` letter-spacing | Figure title |
| `.col-title` | 16–18px, 600 | Column or section heading |
GitHubで見る