Docs Reference
Answer pages
An answer page explains one question on one printable page: the answer first, then the
evidence. Use it when someone asks why, how, which or what, or wants a summary,
comparison, decision record or explainer they can share. Use a diagram (see
authoring.md) when the main thing to show is how parts connect; embed one in a page when
both matter.
Schema: schemas/yantra-page.schema.json. Examples: examples/answer-checkout-saga.json
(architecture decision), examples/answer-incident-review.json (incident review: timeline,
checklist, facts, FAQ), examples/answer-how-to-choose-database.json (decision with an
interactive diagram), examples/answer-quarterly-review.json (charts and sparklines) and
examples/answer-launch-plan.json (gantt plan, 2x2 matrix, risk heat map). Chart use cases:
examples/chart-*.json (sales, marketing, budget against actual, survey, portfolio, service
levels, headcount). More explainers: answer-card-payment-journey.json,
answer-what-happens-url.json, answer-safe-feature-rollout.json,
answer-build-or-buy-crm.json, answer-black-friday-slowdown.json.
Pages suit any purpose: explainers, decisions, comparisons, how-tos, incident reports,
study notes, product specs and process docs. Set meta.kicker to say which.
node bin/yantra.js new answer.json --page # starter
node bin/yantra.js validate answer.json
node bin/yantra.js build answer.json # -> answer.html (offline, prints to A4)
node bin/yantra.js screenshot answer.json # -> answer-light.png, then look at it
node bin/yantra.js export answer.json -f md # Markdown copy for chat, tickets or docsShape
{
"kind": "page",
"meta": { "question": "…?", "title": "Short title", "kicker": "Explainer", "author": "…", "date": "20/10/2026" },
"answer": { "headline": "The conclusion in 1-2 sentences.", "text": "Short context.", "points": ["…", "…", "…"] },
"blocks": [ { "type": "points", "title": "…", "items": [ … ] } ]
}Writing the answer
- Headline (required, under 220 characters): the conclusion itself, not a teaser. "Use a saga, because…" beats "There are several considerations…".
- Text: one short paragraph of context. Points: 3-5 supporting facts.
- Then 3-8 blocks, ordered by what the reader needs next. A page should be readable in two minutes; the validator warns above 12 blocks.
Choosing blocks
| The content is… | Block | Fields |
|---|---|---|
| A few parallel ideas, reasons, risks | points |
items: [{ title, text?, tone? }] |
| A sequence or procedure | steps |
items: [{ title, text? }] |
| Numbers that matter | metrics |
items: [{ label, value, note?, tone?, trend? }] (trend: [numbers] adds a sparkline) |
| A ranking or breakdown | chart |
data: [{ label, value, tone? }], unit?, prefix? (horizontal bars; see Charts) |
| Change over time, a comparison across groups, parts of a whole | chart |
kind: column | line | area | pie | donut, categories + series for several series |
| A project plan or schedule | gantt |
items: [{ title, start?, end? | days?, after?, group?, milestone?, progress? }], today?, workdays?, holidays? |
| Priorities (impact / effort) or a risk heat map | matrix |
items: [{ label, x, y }], x, y, quadrants? or grid: 3-5 |
| Options to choose between | compare |
options: [{ name, summary?, pros?, cons?, verdict?, recommended? }] |
| Structured facts across items | table |
columns: [...], rows: [[...]] (one cell per column) |
| How parts connect | diagram |
src: "file.json" or diagram: {…}, step?, mode?, level?, caption? |
| A warning, tip or key caveat | callout |
body, label?, tone: info | good | warn | bad |
| Prose that does not fit above | text |
body |
| A command, config or snippet | code |
code, language? |
| Where the facts come from | sources |
items: [{ title, url?, note? }] |
| Events in order (history, incident, roadmap) | timeline |
items: [{ date, title, text?, tone? }]; dates shown as written (14/10/2026, 14/10 14:02) |
| Questions readers will ask | faq |
items: [{ q, a }]: collapsible on screen, all open when printed |
| Actions, requirements, a to-do or readiness list | checklist |
items: [{ text, done?, note? }] (static ticks) |
| A line worth remembering | quote |
text, by?, source? |
| A screenshot, photo or chart image | image |
src (https: URL or data:image/… URI), alt (required), caption?, width? |
| Short facts about one thing (owner, status, version) | kv |
items: [{ k, v }] |
| A heading over a group of blocks (a part of a long page) | section |
blocks: [...], title?, intro? |
span: "half"places two consecutive blocks side by side on wide screens (pair astepswith acallout, or twopoints);span: "third"places three (threemetrics, threecallouts). Below 760px every block takes the full width.sectiongroups blocks under a heading (anh2, with an optionalintroparagraph) in their own row, sohalfandthirdwork inside it. Sections go one level deep (no section inside a section), and the blocks inside them count towards the 12-block budget and the 2 interactive diagrams.tonecolours:infoblue,goodgreen,warnamber,badrose,neutralslate.- Text fields accept light markdown: paragraphs,
-and1.lists,**bold**,*italic*,`code`,[link](https://…). HTML is escaped. - An embedded diagram is validated like any diagram (geometry included) and drawn as a
static figure in both themes;
stephighlights that walkthrough step. interactive: trueon a diagram embeds the full viewer (step-through, inspector) in a sandboxed frame,heightpixels tall (default 560); the static figure is still what prints (and shows without JavaScript). All embeds on a page share one copy of the viewer (about 270 kB), filled in at load; each embed then adds only its diagram. Still use at most two per page: every live viewer costs memory and attention.- A diagram with detail diagrams (drill-down, see
authoring.md) embeds as one viewer with every level;level: "payments"shows that level as the figure and opens the viewer there. Asrcdiagram's$reflevels are loaded relative to its own file, inside the page's folder. - Contents: a row of links to every titled block appears when
meta.tocistrue, or by itself from 6 titled blocks (meta.toc: falsehides it). Anchors are the slug of the block title (#follow-up-actions), with-2,-3for repeats, so links stay stable; Latin accents are dropped (Café->cafe) and other scripts keep their letters (#مسار-الطلب). A link scrolls smoothly (instantly when reduced motion is preferred), moves focus to the section heading and, in a built file, updates the URL; in the viewer's gallery it scrolls inside the page frame only. The chip of the section being read is highlighted (aria-current). On long pages the row sticks to the top of the window when it fits in a few lines (not on phones), and sections stop just below it. Long titles end in an ellipsis (full title on hover). The row is left out of print; the Markdown export starts with the same list, linked to<a id>anchors before each heading. meta.updatedadds "Updated …" to the byline next tometa.date.- Reading modes: the header switches between scroll (the page as one long page),
book (one screen at a time with previous / next, a progress bar and text size) and
present (one row of blocks per slide, larger, for a meeting).
meta.reading(scroll,bookorpresent) sets how the page opens; readers can switch, and their mode and place are kept in their browser. Pages break between blocks, never inside one. Keys, links and print are the same as for books: seebooks.md(Reading modes). - Several pages read in order are a book (
"kind": "book"): seebooks.md.
Charts
Charts are drawn as inline SVG that scales with the page, follows the light and dark theme, prints in colour and carries a visually hidden data table for screen readers.
{ "type": "chart", "kind": "line", "title": "Monthly churn", "unit": "%",
"categories": ["30/04/2026", "31/05/2026", "30/06/2026"],
"series": [ { "name": "Starter", "values": [2.8, 3.1, 3.2], "tone": "bad" },
{ "name": "Team", "values": [1.9, 1.8, null] } ] }kind |
Use for | Data |
|---|---|---|
bar (default) |
Rankings, long labels | data, or categories + series (grouped; stacked: true) |
column |
Few categories, values that can be negative | as bar |
line |
Trends; several series compared | categories + series (or data); null leaves a gap |
area |
Totals over time; stacked: true shows how parts add up |
as line |
pie, donut |
Parts of one whole (2-8 slices); a donut shows the total in its centre | data only, values ≥ 0 |
- Values are numbers;
prefixgoes before them (£),unitafter (%,ms,k). target(a number) draws a dashed reference line labelled with its value ("Target 160 ms") on column, line and area charts, and a vertical one on bar charts; the axis stretches to include it, and the accessible name, data table and Markdown list it. A bar chart with a target is drawn as SVG, not the HTML bar list. Pie and donut charts take no target.- Series take colours in a colour-blind-safe order (blue, orange, teal, violet, rose,
amber…); line series also get distinct marker shapes.
toneon a series or data item overrides its colour. More than 6 series, 24 bars, 60 line points or 8 slices is a warning. - Categories that are all dates (
YYYY-MM-DDorDD/MM/YYYY, increasing) are spaced by time on line and area charts, and shown as written. - A plain
databar chart with no negative values keeps the original HTML bar list. span: "half"draws a narrower chart;captionadds a note under any chart, gantt or matrix. In inspector panels charts are drawn at the panel's width, so they fit without scrolling. On pages, column, bar, line and area charts, gantt plans and matrices also carry narrower drawings (320, 420 and 560 px, inert<template>s); the page script swaps in the widest one that fits the block on load and on resize, so phones do not scroll sideways. Without JavaScript, and in print, the static drawing stays.- Clicking a legend item (or Enter / Space on it) hides that series, or takes a slice out of a pie or donut and redraws the others with their new shares and total. Without JavaScript (and in print) the chart is shown as written.
Gantt. Each item is a task with a title; dates are YYYY-MM-DD or DD/MM/YYYY, and
numbers are days from the block's start date (or day 0 when the plan has no dates). Give
start plus days or end (the last day, inclusive). Without start, a task begins when
everything in its after (an id, a title, or a list) has ended, else when the task above
it ends. milestone: true draws a diamond; group adds a heading row with a span bracket;
progress (0-100) fills part of the bar; today draws a dashed line. Arrows show after.
In narrow containers (inspector panels, phones, the 320 / 420 / 560 px page drawings) task
names that would be cut short in a label column go on their own line above their bars, which
then take the full width; a name that still does not fit ends in an ellipsis, with the full
name in its tooltip and the data table.
{ "type": "gantt", "title": "Plan", "today": "20/10/2026", "items": [
{ "id": "api", "title": "API", "group": "Build", "start": "05/10/2026", "days": 25, "progress": 60 },
{ "id": "pay", "title": "Payments", "group": "Build", "after": "api", "days": 14 },
{ "title": "Launch", "milestone": true, "after": "pay", "tone": "good" } ] }workdays: true counts working days: days, numeric starts and a numeric today skip
Saturdays, Sundays and every date in holidays (["2026-12-25", "28/12/2026"]), a task that
follows another (by after or by position) starts on the next working day, and weekends and
holidays are shaded. Explicit dates and end are kept as written; a milestone marks the moment
its after tasks end. It needs dates (a start date); tooltips count working days. See
examples/answer-launch-plan.json.
Matrix. grid: 2 (default) is a 2x2: items are placed by x and y scores on
axis.min..axis.max (default 0-10), quadrants names the four corners (top left, top
right, bottom left, bottom right; { title, tone } tints one). grid: 3-5 is a heat map:
x and y are cell numbers (1 = left / bottom), cells are coloured low to high (heat: false turns that off) and x.labels / y.labels name the columns and rows. An axis is a
label string or { label, low?, high?, labels?, min?, max? }.
Rules
- Answer the question that was asked. If you are unsure of a fact, say so in a
calloutrather than presenting it as certain, and citesourceswhen you have them. - Prefer a
comparewith arecommendedoption over a vague "it depends". - Numbers need units and context (
note). meta.watermark: falseremoves the "Made with Yantra" badge; a string replaces its text.meta.lang(BCP 47) sets<html lang dir>, the fixed labels (Answer, Contents, Updated …, Recommended, the badge) and number formats inmetricsandchartvalues (numbers given as numbers, not strings). Arabic, Hebrew, Persian and Urdu, ormeta.dir: "rtl", lay the page out right to left; diagram figures keep their own layout direction. Diagram blocks without their ownmeta.langinherit the page's.meta.stringsoverrides labels by key. Seeauthoring.md(Languages and right to left) andexamples/answer-order-fulfilment-arabic.json.
Validation codes
| Code | Fix |
|---|---|
page/missing-answer |
Add answer.headline. |
page/unknown-block |
Use one of the block types above. |
page/nested-section |
A section inside a section: move its blocks up, or make it a sibling section. |
page/block-invalid |
Supply the fields listed in the message. |
page/diagram-unresolved |
src must be a relative .json path inside the page's own folder (no .., no absolute paths); fix the path or inline the diagram. |
page/unknown-level |
level must be a key of the diagram's diagrams map. |
page/image-src |
Use an https: URL or a data:image/… URI. |
page/image-alt |
Describe the image in alt. |
page/chart-kind |
kind is one of bar, column, line, area, pie, donut; pie and donut take data, not series. |
page/chart-mismatch |
Give one value per category in every series; quadrants has 4 entries; x.labels / y.labels one per row or column. |
page/chart-target |
target is a number (the value its dashed line marks); pie and donut charts draw none (warning). |
page/chart-negative |
Pie and donut values must be 0 or more (and not all 0); use a column chart for negatives. |
page/chart-too-many-series, page/chart-too-many-points (warnings) |
Cut to the limits above: merge small items into "Other", or use a table. |
page/chart-out-of-range (warning) |
A matrix item lies outside its axis range (it is drawn at the edge), or a gantt progress is outside 0-100. |
page/gantt-date |
Use YYYY-MM-DD or DD/MM/YYYY (or a number of days), and give every task days or end. holidays are dates and need workdays: true; workdays needs a dated plan (warnings). |
page/gantt-dependency |
after must name a task's id or title, ids must be unique, and dependencies cannot loop. |
page/gantt-order |
A task ends before it starts (error), or starts before a task in its after ends (warning). |
page/heavy-embed (warning) |
More than 2 interactive diagrams; make the rest static. |
page/bad-height, page/bad-toc, page/bad-span (warnings) |
height is a number ≥ 240; meta.toc is true or false; span is full, half or third. |
page/bad-reading |
meta.reading is scroll, book or present. |
page/headline-too-long, page/too-many-blocks |
Cut. |
i18n/* (warnings) |
See authoring.md: meta.lang is a BCP 47 tag, meta.dir is ltr/rtl, meta.strings keys exist. |
diagram codes (geometry/*, ref/*, …) |
Fix the embedded diagram; see authoring.md. |