yanta

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 habitYanta-native form
Header, intro, narrative sectionsMarkdown headings and paragraphs
Disclosure widgets<Toggle title="…">
Source links and referencesMarkdown links or <LinkCard />
CSV-backed tabular data<DataTable src="data/file.csv" sortable /> in a pushed folder
Small formulas$…$ or $$…$$
Process sketchesmermaid fences
Dense repeated records or dashboardsRepeated 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.

mdx
# 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:

jsx
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:

  1. A Markdown claim.
  2. A small instrument that lets the reader inspect that claim.
  3. A short interpretation.
  4. 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.

jsx
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:

mdx
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
failed
message appears, and inspect both desktop and narrow mobile widths for text overflow or accidental horizontal scrolling.

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-ember only for live or unsaved state. Do not use accent color as ornament.
  • Keep corners at 3px or 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-machine for 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-500 or rounded-xl. There is no utility framework in document instruments.
  • Global selectors such as p { … } or h1 { … } 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.
  • mermaid fences for monochrome diagrams.

DataTable needs its CSV to travel with the document. Push a folder when a page depends on data/ files:

text
report/
  index.mdx
  data/tasks.csv
sh
yanta 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:

NeedBest form
A teammate should edit sentences and bulletsMarkdown
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 simulateCustom JSX instrument
A generated report needs cards, anchors, and compact metadataMarkdown sections with repeated local instruments
A legacy HTML page needs to be preserved exactlyYanta 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

sh
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 URL

The 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