--- name: tttk-authoring description: Author and edit TTRPG game design documents for the tttk (Tabletop Toolkit) CLI. Use when writing or modifying markdown content that uses tttk custom elements (:md-dice, :md-table, :md-deck, :md-pins, :md-commander, etc.), custom fenced code blocks (role=spark-table / declare / file / tag), companion CSV data files, or when previewing content with `tttk serve` and validating it with `tttk check`. --- # Authoring tttk game design documents `tttk` (Tabletop Toolkit) renders a folder of markdown into an interactive TTRPG web app. A game design document is **ordinary GFM markdown** plus exactly two extensions: 1. **Custom elements** written with `marked-directive` syntax: `:name[content]{attrs}`. 2. **Custom fenced code blocks** whose info string carries a `role=` attribute. Everything else — headings, lists, tables, blockquotes, alerts, mermaid — is standard markdown. Do not invent element names, props, or roles; check `reference.md` when unsure. Deeper topics have their own files: `yaml-tag.md` (deck config), `csv.md` (data files), `layout.md` (columns), and `markdown.md` (alerts and mermaid). ## Workflow - Content lives in a directory (commonly `content/`). Preview with hot reload: `npx tttk serve ./content` (default port 3000, override with `-p 8080`). - **Validate after every edit:** `npx tttk check ./content`. It exits `1` when there are errors and prints `file:line` diagnostics. Use `--json` for machine-readable output and `--quiet` for errors only. This is the primary verification loop: write → check → fix. - The scanner picks up `.md`, `.csv`, `.yarn`, and `.svg` files. - All paths in directives are **relative to the current markdown file** (`./data.csv`, `../shared/npcs.csv`, `./images/map.png`). - A typical game/adventure folder: ``` my-adventure/ ├── design.md # design notes (plain markdown) ├── adventure.md # the document players/GM read ├── locations.csv # data referenced by :md-table ├── cast.csv └── route.csv ``` ## Directive syntax ``` :name[content]{key="value" .class #id} ``` - `[content]` is the directive body — a path, dice formula, color, etc. - `{key="value"}` sets attributes. `{.class}` and `{#id}` are shorthands. - Container directives use `::: name` … `:::`; nest deeper with `::::`, `:::::`. Use the **registered element names**: `:md-dice`, not `:dice`. The full list is in `reference.md`. ## Code block roles Fenced blocks are dispatched by the `role=` attribute in the info string: | Info string | Behavior | |---|---| | ` ```csv role=spark-table ` | Body is CSV; replaced with `:md-table[./csv_]`. First column header must be a dice formula (`d6`, `2d6+1`, …). | | ` ```markdown role=spark-table ` | Body is a GFM pipe table; converted to CSV, then same as above. | | ` ```csv role=declare ` | Defines reactive variables / tag modifiers. Block is stripped. | | ` ``` role=file ` | Body stored as text content, block stripped, referenced by its derived id. | | ` ```yaml role=tag ` | Kept intact; rendered as a custom element from YAML (`tag:`, `body:`, …). | | ` ``` ` (no role) | Rendered as a normal visible code block. | | unknown role | Warned about and stripped. | ## Rules that prevent broken output - Quote any attribute value containing spaces or `"`: `{pins="A:30,40 B:10,30"}`. - In CSV, wrap fields containing `,`, `"`, or a newline in double quotes and escape `"` as `""`. Lines starting with `#` are comments. - `label` and `body` are the conventional CSV columns. Put titles inside `body` as markdown headings; use **bold labels** for sub-fields, not headings. - A `group` column in a table CSV automatically produces group tabs — no attribute needed. - Reference each CSV once per document; don't repeat the same `:md-table`. - For card decks, use the `yaml role=tag` form with `tag: md-deck` (see `yaml-tag.md`). The `:md-deck[...]{...}` directive is legacy and can't express template layers or card backs. - Keep CSV files clean — header row plus data. Put deck config and templates in the YAML block's `data-config`, not in CSV front matter (see `csv.md`). - `{{prop}}` in a CSV `body` interpolates another column (or front-matter key) from the same row; with `{remix}` it pulls from a random row instead. ## Validation `npx tttk check ./content` validates against the same content registry the app uses, so a clean check means the app resolves the content too. Errors exit `1`: | Code | Meaning | |---|---| | `TTTK-S002` | container directive `:::` never closed | | `TTTK-D001` | unknown `:md-*` element name | | `TTTK-D002` | malformed attribute block (e.g. an unquoted value with a space) | | `TTTK-R001` | unknown code-block `role=` (the block is silently dropped) | | `TTTK-R003` | `role=declare` failed to parse | | `TTTK-R005` | `role=tag` YAML is invalid | | `TTTK-R006` | `role=tag` missing or naming an unknown element | | `TTTK-C001` | content reference does not resolve | | `TTTK-C004` | `:md-embed` cycle | | `TTTK-L001` | link target file does not exist | | `TTTK-V001` | CSV failed to parse (e.g. an unclosed quote) | Warnings are usually real too: | Code | Meaning | |---|---| | `TTTK-R002` | spark-table first header is not a dice formula | | `TTTK-R004` | `role=declare` row skipped (empty `key` or `expr`) | | `TTTK-C002` | reference resolves to an unexpected file type | | `TTTK-L002` | `#section` matches no heading | | `TTTK-A001` | icon missing under `assets/` | | `TTTK-A002` | image missing | | `TTTK-V002` | CSV row column count differs from the header | | `TTTK-V003` | duplicate `label` values | | `TTTK-V004` | `{{prop}}` is not a CSV column or front-matter key | | `TTTK-V005` | spark-table roll value is not a number or range | `TTTK-C003` (info) flags the same CSV referenced twice by `:md-table` in one document. Icon checks assume no `assets/` directory exists above the checked directory. The full code catalog lives in `reference.md`. ## Reference - `reference.md` — **generated** catalog of every custom element, inline directive, code block role, and dice notation. Do not edit by hand; regenerate from the repo with `npm run gen:skill-reference`. - `yaml-tag.md` — `role=tag` blocks, `data-config` layers, card backs, and the legacy `:md-deck` layer syntax. - `csv.md` — CSV authoring: columns, `{{prop}}` interpolation, quoting, and the discouraged front-matter form. - `layout.md` — column layout markers. - `markdown.md` — GitHub alerts and mermaid diagrams. - `examples/adventure.md` and `examples/encounters.csv` — a complete minimal document and its data file.