Writing for Yanta
built into yanta
Yanta turns an MDX file into a living page: a document that reads well, edits in place, and can contain small live instruments when static prose is not enough.
MDX is not HTML with a different extension. It is Markdown prose plus JSX islands. The craft is deciding what belongs in the document flow, what belongs in a registered block, and what deserves a custom instrument.
The Native Shape
Start with the reader’s job. A good Yanta page is usually one of three shapes:
- A prose document with aids. Use headings, paragraphs, lists, quotes, tables, math, and a few registered blocks.
- A working explanation. Keep the argument in Markdown and place a calculator, simulator, or inspection panel where interaction changes the reader’s understanding.
- A dense internal report. Break the report into readable sections. Put a small instrument immediately after the prose it clarifies.
The default is Markdown. The exception is earned by density, interaction, or state.
The Conversion Rule
Raw HTML is not an input format. Pasted HTML renders as source because Yanta preserves documents, not websites. Convert the artifact by asking what each piece means:
| HTML habit | Yanta-native form |
|---|---|
| Header, intro, narrative sections | Markdown headings and paragraphs |
| Disclosure widgets | <Toggle title="…"> |
| Source links and references | Markdown links or <LinkCard /> |
| CSV-backed tabular data | <DataTable src="data/file.csv" sortable /> in a pushed folder |
| Small formulas | $…$ or $$…$$ |
| Process sketches | mermaid fences |
| Dense repeated records or dashboards | Repeated small instruments, each beside the section it serves |
Do not flatten useful visual structure into plain prose. Do not wrap a whole page in JSX just because JSX can reproduce the old layout. The useful boundary is the reader’s next task: read a claim, inspect the evidence beside it, then continue.
Prose First
Prose stays Markdown because Markdown is what humans can edit directly and agents can diff safely.
# IC Readiness Notes
The model is ready for committee review except for two open assumptions.
## Open Items
- Confirm the tax-rate bridge with accounting.
- Decide whether the downside case keeps revenue synergies at zero.
<Toggle title="Source checks">
The working model ties to the locked assumptions memo. The alternate draft
does not.
</Toggle>Use JSX only where Markdown would force the reader to do mechanical work: sort, compute, compare, reveal, annotate, or move through a large set.
Instruments
An instrument is a component defined in the document and used like a tag. React hooks such as useState, useEffect, useRef, and useMemo are already in scope. Do not import them.
This is the smallest useful pattern:
export const Meter = () => {
const [n, setN] = useState(0);
return (
<div className="mtr">
<style>{`
.mtr { border: 1px solid var(--color-hairline, #e8e7e3); border-radius: 3px; padding: 14px; margin: 1.25rem 0; }
.mtr .bar { height: 6px; background: var(--color-panel, #f5f5f3); border-radius: 3px; overflow: hidden; }
.mtr .fill { height: 100%; background: var(--color-ink, #191815); transition: width 160ms ease-out; }
.mtr button { font: inherit; font-size: 13px; margin-top: 10px; padding: 5px 11px; border: 1px solid var(--color-hairline, #e8e7e3); border-radius: 3px; background: var(--color-paper, #fbfbfa); cursor: pointer; }
.mtr .n { font-family: var(--font-machine, ui-monospace, monospace); font-size: 12px; color: var(--color-muted, #6e6b64); margin-left: 8px; }
`}</style>
<div className="bar"><div className="fill" style={{ width: `${Math.min(n * 10, 100)}%` }} /></div>
<button type="button" onClick={() => setN(n + 1)}>advance</button>
<span className="n">{n} of 10</span>
</div>
);
};And here it is, running:
warming…
Three things matter in that source. Styles are scoped under one unique class prefix. Every color and font comes from a Yanta token. The instrument fits the column and leaves the rest of the document alone.
Interleave Instruments
The best Yanta pages read in a rhythm:
- A Markdown claim.
- A small instrument that lets the reader inspect that claim.
- A short interpretation.
- The next claim.
This is different from an app shell. The instrument should sit where the reader needs it, not at the top as a control panel for everything below.
For repeated evidence, define a reusable component and place several instances through the document. Keep the data local to the example unless a true sidecar table is needed.
export const Delta = ({ title, before, after }) => (
<section className="dlt">
<style>{`
.dlt { border: 1px solid var(--color-hairline, #e8e7e3); border-radius: 3px; padding: 12px; margin: 1rem 0; }
.dlt h3 { margin: 0 0 8px; font-size: 15px; line-height: 1.2; }
.dlt .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.dlt .cell { border-top: 1px solid var(--color-hairline, #e8e7e3); padding-top: 8px; }
.dlt .label { font-family: var(--font-machine, ui-monospace, monospace); font-size: 11px; color: var(--color-muted, #6e6b64); }
.dlt p { margin: 3px 0 0; font-size: 14px; line-height: 1.4; }
`}</style>
<h3>{title}</h3>
<div className="grid">
<div className="cell"><div className="label">before</div><p>{before}</p></div>
<div className="cell"><div className="label">after</div><p>{after}</p></div>
</div>
</section>
);Then use it where the prose needs the comparison:
The revised view removes redundant labels and keeps the comparison intact.
<Delta
title="Data-ink edit"
before="Heavy grid, repeated labels, shaded background."
after="Direct labels, hairline axis, no redundant ink."
/>Large instruments are a last resort. If a section can be read as Markdown with a local visual example beside it, that is the better Yanta page.
Page Hygiene
The page chrome already carries the frontmatter title. In a pushed page, avoid repeating that title as the first # heading unless the duplication is intentional. Start the document with the opening paragraph or the first real section.
Keep module-scope JSX definitions together. A run of export const ... components, helpers, and shared styles is invisible in read mode, but the editor preserves it as a selectable definitions block. Group related definitions in one contiguous block near the top, then interleave the component uses through the prose. Do not scatter invisible definitions between sections unless the placement itself helps future editors.
Verify the living page after hydration, not at first paint. Live instruments may briefly show warming…; wait for them to render, check that no jsx message appears, and inspect both desktop and narrow mobile widths for text overflow or accidental horizontal scrolling.
failed
Visual Discipline
Yanta has one visual language. Use it with restraint.
- Maximize information signal, not decoration. If an element does not help the reader compare, decide, or navigate, remove it.
- Prefer position, grouping, weight, and whitespace before color.
- Use
--color-emberonly for live or unsaved state. Do not use accent color as ornament. - Keep corners at
3pxor below unless a registered component already owns its shape. - Let text breathe, but keep operational documents dense enough to scan.
- Avoid nested cards. A page section can have a hairline frame; repeated records can be cards. A card inside a card usually means the structure is confused.
- Make numbers align. Use
font-machinefor IDs, states, and tabular values. - Keep controls familiar: buttons for commands, toggles for disclosure, inputs for editable values, tables for comparisons.
The goal is not to make every document look the same. The goal is for every document to feel like it belongs to the same system.
Tokens And Scope
The page already has a complete design system. Instruments inherit it.
warming…
Always write tokens with a fallback, such as var(--color-hairline, #e8e7e3). If every document sticks to tokens, a future Yanta restyle improves old pages instead of fighting them.
Do not use:
- Utility classes such as
bg-blue-500orrounded-xl. There is no utility framework in document instruments. - Global selectors such as
p { … }orh1 { … }inside an instrument style block. - A private palette or fixed brand system.
- Fixed desktop widths. Use
width: 100%, grid tracks, and media queries.
Registered Blocks
Reach for registered blocks before writing JSX. They stay editable in the browser as structured data.
<Toggle title="…">for collapsible supporting detail.<LinkCard href="…" title="…" description="…" />for a rich reference.<DataTable src="data/things.csv" sortable />for tabular data that should remain a file.$math$and$$math$$for equations.mermaidfences for monochrome diagrams.
DataTable needs its CSV to travel with the document. Push a folder when a page depends on data/ files:
report/
index.mdx
data/tasks.csvyanta push report/If you push only index.mdx, Yanta receives only that file. Any <DataTable src="data/tasks.csv" /> in it will render as missing data.
Choosing The Form
Use this quick test before writing:
| Need | Best form |
|---|---|
| A teammate should edit sentences and bullets | Markdown |
| A long appendix should stay out of the way | <Toggle> |
| A table should be sortable or edited as rows | <DataTable> plus push <dir> |
| A page should calculate, filter, or simulate | Custom JSX instrument |
| A generated report needs cards, anchors, and compact metadata | Markdown sections with repeated local instruments |
| A legacy HTML page needs to be preserved exactly | Yanta is the wrong medium |
Fit the medium. Yanta pages are not less expressive than HTML; they are more opinionated. The constraint is what makes the document durable.
Push It
yanta push page.mdx # private by default; password printed once
yanta push --public page.mdx # public read, password still edits
yanta push report/ # page or collection plus data/ sidecars
yanta pull <url> --password <pw> --out report
yanta diff report --password <pw>
yanta update report --password <pw> # revise the same URLThe URL that comes back is the document. The password is the edit key. For private pages, readers need that password too. Use push when you want a new URL. Use pull and diff when you are revising an existing draft or preview, then update the bound folder; it saves through the same password gate as the browser editor and refuses if the remote moved since your pull.
yanta · this page ships with the site