Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of…

MITAuto-check passedFrontend & Design

Install Doc Authoring

skills CLI
$ npx skills add simonliu-ai-product/open-doc --skill doc-authoring -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install simonliu-ai-product/open-doc doc-authoring --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/simonliu-ai-product/open-doc.git skills-src && mkdir -p .claude/skills && cp -r skills-src/packages/core/skills/doc-authoring .claude/skills/doc-authoring && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
doc-authoring
GitHub stars
108
Token cost
~6.8k tokens
SKILL.md length
3,263 words
Files
6 (incl. references)
Skills in repo
7
Repo updated
First seen
Licence
MIT

At a glance

Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of…

  • Phrases like edit the report
  • SKILL.md covers Primitive references, Themes, Hard rules and File contract, plus 15 more sections
  • Calls node
  • Change the margins

What it does

Doc Authoring is an agent skill from simonliu-ai-product/open-doc. Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of contents, page numbers, running headers/footers, and assets. Consult this whenever you are about to write or modify any file under docs/<id/, including from inside the create-doc workflow, or for any ad-hoc document edit. Triggers on phrases like "edit the report", "fix this page", "add a section", "change the…

Its SKILL.md is about 6.8k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `references/assets.md`, `references/design-system.md` and `references/long-form.md`).

It sits in Frontend & Design, covering Typography. The repository describes itself as: The document framework built for agents — write reports as React, get real A4 pages, an outline, page numbers, and a PDF. The licence is MIT.

When your agent uses it

  • Phrases like edit the report
  • Change the margins
  • Table of contents
  • How do documents work here

Example prompts

  • “edit the report”
  • “fix this page”
  • “add a section”
  • “/doc-authoring”

What it can do on your machine

Read from SKILL.md and the folder at commit af6095d. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • node

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Doc Authoring loads about 6.8k tokens when it runs, and up to ~13k if it reads all its reference files. Until then it costs about 155 tokens; SKILL.md has 3,263 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~155
When it runs · the whole SKILL.md, loaded when a task matches
~6.8k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~13k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from simonliu-ai-product/open-doc at commit af6095d, republished under its MIT licence (© simonliu-ai-product). 3,263 words, ~6,782 tokens.

Download SKILL.mdSave it as .claude/skills/doc-authoring/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.
name
doc-authoring
description
Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of contents, page numbers, running headers/footers, and assets. Consult this whenever you are about to write or modify any file under `docs/<id>/`, including from inside the `create-doc` workflow, or for any ad-hoc document edit. Triggers on phrases like "edit the report", "fix this page", "add a section", "change the margins", "add a table", "page numbers", "table of contents", "how do documents work here".

Authoring open-doc pages

This skill is the technical reference for everything inside docs/<id>/index.tsx. It owns no workflow:

  • create-doc owns "draft a new document" — it asks the scoping questions, then delegates the how to this skill.
  • Any ad-hoc edit (fix a table, retitle a section, adjust margins) should also read this first.

Primitive references

Read the matching reference before using a primitive:

PrimitiveRead beforeFile
design const + var(--od-*) tokenswriting any new documentreferences/design-system.md
flow() auto-paginationany body content (the default)references/pagination.md
Vertical budget (fixed pages)laying out a cover or divider by handreferences/pagination.md
Tables, stat rows, inline chartsrendering data of any kindreferences/tables-and-charts.md
Assets + <ImagePlaceholder>importing images or leaving a placeholderreferences/assets.md
Footnotes, <Figure>, <Ref>, <DataTable>any note, numbered figure, cross-reference, or .csvreferences/long-form.md

Themes

If themes/<id>.md exists at the project root and the document is meant to follow it, the theme file overrides the defaults in this skill — its palette, typography, page setup, and paste-ready components are authoritative. Read the theme file end-to-end before applying anything else here, and set meta.theme: '<id>' so the document back-links to it (chip on the document card, listing on /themes/<id>).

Themes are produced by the create-theme skill and are pure documentation: copy the design const and the Title / Footer / Table components straight into the document. mode: dark in a theme's frontmatter applies to its cover, not to body pages — anything that prints stays light.

Hard rules

  • Put the document under docs/<kebab-case-id>/.
  • Entry is docs/<id>/index.tsx. Images/fonts go under docs/<id>/assets/.
  • Do not touch package.json, open-doc.config.ts, or other documents.
  • Do not add dependencies. Only react, @open-document/core, and standard web APIs are available.
  • A document is one index.tsx plus assets/ — nothing else. Helper components and constants live inside index.tsx; no sibling .tsx files, no README.md.

File contract

tsx
// docs/<id>/index.tsx
import type { DocMeta, DocPage } from '@open-document/core';

const Cover: DocPage = () => <div>…</div>;
const Body: DocPage = () => <div>…</div>;

export const meta: DocMeta = {
  title: 'Q3 Infrastructure Review',
  subtitle: 'Platform team',
  author: 'Platform Engineering',
  pageSize: 'A4',
  orientation: 'portrait',
  createdAt: '2026-08-15T13:44:40.268Z',
};
export default [Cover, Body] satisfies DocPage[];
  • export default is a non-empty array of entries. An entry is either a zero-prop React component (one fixed page) or a flow(<>…</>) section the framework paginates by measuring. Mix them freely — the usual shape is a fixed cover, a fixed contents page, then one flow section for the body.
  • Default to flow() for body content. Hand-splitting prose into fixed pages produces documents where every heading starts a half-empty page. Read references/pagination.md before writing either kind.
  • meta.pageSize is 'A4' | 'B4' | 'A3' (default 'A4') and meta.orientation is 'portrait' | 'landscape' (default portrait). These six combinations are the only sheets there are — there is no Letter, no A5, no custom size, and no way to set a page's dimensions by hand. The same value drives the on-screen page, the @page size when printing, and the HTML export.
  • meta.lang is the document's language as a BCP 47 tag — set it for anything not in English: 'zh-Hant-TW', 'zh-Hans', 'ja', 'ko'. Every sheet carries it, so it decides the glyph shapes of shared Han characters, the line-breaking rules (no ,。」 at the start of a line), and the language exported HTML and Word declare. The sheet already sets CJK text properly — strict kinsoku, and an eighth-em gap where a CJK character meets a Latin letter or digit with no space typed — so don't hand-insert spaces for that; a block that must not get the gap takes textAutospace: 'no-autospace'.
  • One document per row of data (certificates, letters, quotes): export const records = rows (an imported .csv) makes the document print once per row. Pages read the current row with useRecord() or print one value with <Field name="company" />; meta.recordName: 'certificate-{name}' names each row's file ({#} is the row number) and meta.recordLabel names rows in the viewer's picker. open-doc export <id> --each writes one file per row. A flow section's blocks are fixed when the module loads, so content that varies by row goes inside blocks — a block can render nothing for a row — never as a different number of blocks.
  • meta.createdAt is an ISO 8601 string literal set once when the doc is scaffolded — the home page sorts on it. Immediately before writing the file, run node -e "console.log(new Date().toISOString())" and paste the exact output. It must stay a plain string literal (no new Date(...)): the framework reads it with a regex at build time, it never evaluates the module.

Two ways to fill pages

tsx
import { flow, type DocEntry } from '@open-document/core';

const Body = flow(
  <>
    <h1 style={h1}>1. Findings</h1>
    <p style={p}>…</p>
    <Table>…</Table>
  </>,
  { footer: Footer },
);

export default [Cover, Contents, Body] satisfies DocEntry[];

Each direct child of the fragment is one atomic block. Blocks never split across pages; headings glue to what follows them; a caption marked data-od-keep-with-previous stays with its figure. Everything else — where the breaks land, how many pages the section becomes, the running footer on each — is the framework's job.

Fixed DocPage components remain the right tool for the cover, a contents page, or a divider whose layout is the content. Those pages are subject to the vertical budget below.

The page canvas

SizePortrait px (96dpi)Text block at 76px margins
A4 (210 × 297mm)794 × 1123642 × 971
B4 (JIS, 257 × 364mm)971 × 1376819 × 1224
A3 (297 × 420mm)1123 × 1587971 × 1435

Landscape swaps the two numbers. B4 and A3 are for wide tables, plans, and posters — a body of prose set the full width of an A3 sheet is unreadable, so give a large sheet columns or wider margins.

You design as if the viewport is literally the page in CSS pixels. The viewer only scales the whole sheet.

  • Use absolute pixel values for font-size, padding, and positioning. No rem, no vw/vh, no % for type.
  • Each page's root element must fill the sheet: width: '100%'; height: '100%'.
  • Prefer inline style={{ … }}. Any CSS you load is global — scope classnames carefully.
  • The viewer's CSS reset strips list markers. A <ul>/<ol> needs an explicit listStyle: 'disc outside' / 'decimal outside' or it renders as unindented plain lines.
  • 1pt ≈ 1.333px. Body copy at 14px prints as ~10.5pt; anything under 12px (9pt) is uncomfortable in print, and under 10px (7.5pt) is unreadable.
Print type scale (start here)
ElementSizeNotes
Cover title40–52pxCover page only
H1 / section opener26–32pxOne per section
H2 / subsection18–22px
H3 / run-in heading15–17pxOften bold body size
Body13–15px1.5–1.65 line-height
Caption / table cell10–12pxTables can go to 11px
Footnote / footer9–10px
Margins
  • Standard report: 72–96px (19–25mm) on all four sides.
  • Bound/printed double-sided: add ~24px to the inner edge.
  • Running header/footer live inside the margin band, not in the text block.

Vertical budget — for fixed pages only

A DocPage does not scroll or reflow: anything past the bottom edge is silently cropped, so do the math before writing JSX. (Inside a flow() section the framework measures for you — this arithmetic is exactly what it removes.)

Usable height = page_height − 2 × margin (A4 @ 76px margins → 971px). Text height = font_size × line_height × line_count. A paragraph that wraps to 4 lines counts as 4. Lines per page ≈ 971 / (14 × 1.55) ≈ 44 lines of body copy on A4 — that is the whole budget for one page.

references/pagination.md has the worked example, the character-count estimate for how many lines a paragraph will wrap to, and the rules for where to split.

Headings and the outline

The framework builds the document outline by scanning rendered pages for h1, h2, h3 (or any element carrying data-od-heading). That outline powers the sidebar and <TableOfContents />.

  • Use real <h1>/<h2>/<h3> elements for section titles, styled inline. Don't fake a heading with a <div> — it disappears from the outline and the TOC.
  • Conversely, don't use heading tags for decorative text (a cover eyebrow, a stat label). Mark those <div data-od-outline="skip"> if you must use a heading tag for styling reasons.
  • The cover title and the word "Contents" both carry data-od-outline="skip". A contents list that opens with the cover and lists itself reads as a bug.
  • Override the listed text with data-od-heading="Short title" when the visible heading is long or contains markup.

Bare elements on the sheet start from a base drawn from the design, not from a blank reset: headings are bold at --od-size-h1/h2/h3 in the heading font, paragraphs, lists, quotes and tables have a bottom margin, lists have their markers and indent, links take --od-accent with an underline, code/pre use --od-font-mono, and a quote carries a rule on its leading edge. Anything you set inline replaces it, so style the elements that matter to the layout explicitly — margins above all, since they decide where a page breaks.

Footnotes, numbering, and data

Four primitives resolve themselves from the rendered pages, the same way the contents list does. Read references/long-form.md before using any of them.

  • <Footnote> — numbered by position, printed at the foot of the page its marker landed on, and its height is taken out of that page's budget before the packer breaks. On a fixed page, add <Footnotes /> where they should print.
  • <Figure caption id> — a numbered figure (or kind="table"), caption and content in one unbreakable block. <ListOfFigures /> / <ListOfTables /> build the lists.
  • <Ref to="id" /> — "Figure 3", plus the page when the target is elsewhere.
  • <DataTable rows={…}> — a print-shaped table from an imported .csv.
  • <Diagram chart={…} caption> — an architecture or flow drawing from an imported .mmd. Given a caption it numbers as a figure, like <Figure>.
  • <Chart data x y caption> — a bar, line or pie chart from rows, in the document's colours; numbers as a figure. See references/tables-and-charts.md.

meta.labels sets what they are called (圖, 表) — the numbering itself is structural.

Diagrams

Write the drawing as Mermaid-flavoured text in docs/<id>/<name>.mmd, import it, and hand it to <Diagram>:

mermaid
%% docs/my-doc/architecture.mmd
flowchart TD
  Client[使用者] -->|HTTPS| Gate{驗證}
  Gate -->|通過| App[應用伺服器]
  Gate -.->|拒絕| Deny([401])
  App --> DB[資料庫]
tsx
import { Diagram } from '@open-document/core';
import architecture from './architecture.mmd';

<Diagram chart={architecture} caption="請求路徑" width={420} />

It is compiled to SVG at build time and drawn with the document's own theme variables, so it prints with the same ink and faces as the prose around it. Never reach for an image of a diagram when the diagram can be written.

Supported: flowchart/graph with TD or LR; nodes as A[box], A(round), A([stadium]), A[(database)], A{decision}, A((circle)); links -->, ---, -.->, ==> with optional |labels|; chains A --> B --> C; %% comments. A link back to an earlier step is drawn round the side, clear of the steps in between. Anything else in Mermaid's syntax — subgraphs, class diagrams, sequence diagrams — is not supported, and a bad diagram fails the build with the line to fix.

Keep width inside the text block: a drawing wider than the column is a layout fault, and open-doc check reports it as one.

Table of contents

tsx
import { TableOfContents } from '@open-document/core';

const Contents: DocPage = () => (
  <div style={page}>
    <h1 style={h1}>Contents</h1>
    <TableOfContents maxLevel={2} />
  </div>
);

Page numbers come from the scan, so they are always correct — never hand-write a contents list. The outline fills in after the first render pass; that is expected and it is resolved before PDF/HTML export serializes the pages.

Page numbers, headers, footers

tsx
import { useDocPageCount, useDocPageNumber } from '@open-document/core';

const Footer = () => {
  const page = useDocPageNumber();
  const total = useDocPageCount();
  return (
    <div style={{ position: 'absolute', left: 76, right: 76, bottom: 40, display: 'flex', justifyContent: 'space-between', fontSize: 10, color: 'var(--od-muted)' }}>
      <span>Q3 Infrastructure Review</span>
      <span style={{ fontVariantNumeric: 'tabular-nums' }}>{page} / {total}</span>
    </div>
  );
};
  • Never hardcode 3 / 12. Both hooks are 1-based and return 0 outside a page.
  • Define the header/footer once as a local component. A flow() section takes both — flow(<>…</>, { header: Header, footer: Footer }) — and prints them on every page it expands into; on a fixed page, drop the component in yourself. Cover pages normally omit them.
  • Headers and footers are absolutely positioned inside the margin band (top: 32 for a header, bottom: 40 for a footer); they do not consume the text block's vertical budget — but keep at least 24px of clearance between the body copy and either one.

Starter template

tsx
import { type DesignSystem, type DocMeta, type DocPage, useDocPageCount, useDocPageNumber } from '@open-document/core';

export const design: DesignSystem = {
  palette: {
    bg: '#ffffff',
    text: '#16181d',
    muted: '#6b7280',
    accent: '#1d4ed8',
    rule: '#e5e7eb',
  },
  fonts: {
    heading: '-apple-system, BlinkMacSystemFont, "Inter", system-ui, sans-serif',
    body: '-apple-system, BlinkMacSystemFont, "Inter", system-ui, sans-serif',
    mono: 'ui-monospace, "SF Mono", Menlo, monospace',
  },
  typeScale: { title: 44, h1: 28, h2: 20, h3: 16, body: 14, caption: 10 },
  margin: 76,
  leading: 1.55,
  radius: 6,
};

const page = {
  width: '100%',
  height: '100%',
  boxSizing: 'border-box' as const,
  padding: 'var(--od-margin)',
  background: 'var(--od-bg)',
  color: 'var(--od-text)',
  fontFamily: 'var(--od-font-body)',
  fontSize: 'var(--od-size-body)',
  lineHeight: 'var(--od-leading)',
  position: 'relative' as const,
};

const h1 = {
  fontFamily: 'var(--od-font-heading)',
  fontSize: 'var(--od-size-h1)',
  lineHeight: 1.2,
  fontWeight: 650,
  margin: '0 0 20px',
};

const Footer = () => {
  const n = useDocPageNumber();
  const total = useDocPageCount();
  return (
    <div
      style={{
        position: 'absolute',
        left: 'var(--od-margin)',
        right: 'var(--od-margin)',
        bottom: 40,
        display: 'flex',
        justifyContent: 'space-between',
        fontSize: 'var(--od-size-caption)',
        color: 'var(--od-muted)',
      }}
    >
      <span>Q3 Infrastructure Review</span>
      <span style={{ fontVariantNumeric: 'tabular-nums' }}>
        {n} / {total}
      </span>
    </div>
  );
};

const Cover: DocPage = () => (
  <div style={{ ...page, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
    <p style={{ fontSize: 12, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--od-accent)', margin: 0 }}>
      Platform Engineering
    </p>
    <h1 style={{ ...h1, fontSize: 'var(--od-size-title)', margin: '16px 0 12px' }}>
      Q3 Infrastructure Review
    </h1>
    <p style={{ color: 'var(--od-muted)', margin: 0 }}>August 2026</p>
  </div>
);

const Section: DocPage = () => (
  <div style={page}>
    <h1 style={h1}>1. Summary</h1>
    <p style={{ margin: '0 0 14px' }}>
      One paragraph per idea. Keep the page inside its vertical budget.
    </p>
    <Footer />
  </div>
);

export const meta: DocMeta = {
  title: 'Q3 Infrastructure Review',
  subtitle: 'Platform team',
  pageSize: 'A4',
  createdAt: '2026-08-15T13:44:40.268Z',
};
export default [Cover, Section] satisfies DocPage[];

Editing an existing document

A finished report commonly runs 800–2000 lines. When you only need one page, don't read the whole file — locate it first:

bash
grep -n ": DocPage = " docs/<id>/index.tsx

Then Read with offset + limit (~150 lines covers a page plus its helpers). Read the whole file only for cross-page work (renumbering sections, palette audit, reordering).

Renumbering is a real cost. If sections are numbered ("3.2 Findings"), inserting a page means editing every downstream heading. Prefer appending, or use unnumbered headings when the document is still churning.

Prose discipline

A document is not a slide deck. Long-form copy is the point — but it still has rules:

  • One idea per paragraph, 2–5 sentences. A paragraph over ~8 lines should be split.
  • Lead each section with its conclusion, then the evidence. Readers skim reports.
  • Tables beat bullet lists for anything with two or more dimensions.
  • Cite numbers with their source and date inline (Q3 billing export, 2026-08-01) — a report that can't be traced gets ignored.
  • Don't invent data. If a number must come from the user, leave <ImagePlaceholder> for a chart or an explicit TODO: marker in the copy and tell them at hand-off.
Show full SKILL.md (1,273 more words)Show less

Runtime behavior you get for free

  • Home page lists every folder under docs/ with a live thumbnail of page 1.
  • Document view: vertical scroll of real-size pages, a left rail that switches between page thumbnails, the outline, and the document's assets, zoom (actual size / fit width / fit page), page counter, and fullscreen reading (F).
  • Export PDF (print pipeline, correct @page size) and export HTML (self-contained, printable).
  • Hot reload: edit index.tsx and the pages update live.
  • Assets panel (/assets in the dev UI): upload, rename, and delete files in the global assets/ folder or any document's assets/ folder, with an "unused" badge and a copy-ready import line. Files you reference in source are what it scans, so an import you write by hand shows up there immediately.
  • Inspect mode (the "Inspect" button, dev only): click any element on a page to edit its text in place — the change is written straight back into docs/<id>/index.tsx — or leave a note for the agent, which is stored as a @doc-comment marker and processed by the apply-comments skill.
  • Download menu — PDF (true page size) and self-contained HTML.
  • Headless render — open-doc export <id> --format pdf|html|png|docx produces the same output from a script, and open-doc check <id> reports layout faults. Both drive the real viewer in a headless browser, so what they produce is what the Download menu produces. A PDF exported this way also carries the outline as bookmarks and is tagged for screen readers — the same headings the sidebar and <TableOfContents /> list, so a heading marked data-od-outline="skip" stays out and data-od-heading sets the bookmark's text. (The Download menu prints through the browser's dialog, which writes no bookmarks.)
  • What changed — open-doc diff <id> [--since <rev>] (MCP: diff_document) renders the document as it is and as it was at a git revision (default HEAD), pairs the pages by content, and lists each page as same, changed, added or removed, with the lines of text that moved. It also writes out/<id>-diff.html, a self-contained before/after report with the changed regions outlined — hand that to a reviewer. Run it before handing back an edit, so your summary of what changed is what actually changed on the page.
  • Word (DOCX) — for review that runs in Word. It carries structure, not page breaks: headings become Word heading styles, <TableOfContents /> a TOC field, <Footnote> real footnotes, a flow() header and footer a Word header and footer with page-number fields, and the theme's CJK font the East Asian font. Write headings as real h1–h3 and tables as <table>/<DataTable> so they arrive as structure; anything drawn with boxes (a chart made of divs) arrives as a picture, and absolutely positioned layouts on fixed pages flow as plain paragraphs.
  • Design panel (the "Design" button in the document view, dev only): live-tweaks the design const — palette, fonts, type scale, margin, leading, radius — previewing on the real pages and writing the values back into docs/<id>/index.tsx on save.
Writing for the inspector

Inspect mode edits the literal text runs of an element, and nothing else:

  • A single run (<p style={p}>copy</p>) is editable in place.
  • Mixed content (<p>對外端點為 <code>/mcp</code>,另外自訂 …</p>) is split into one field per run, with the markup shown as read-only chips. Each run is written back on its own, so the markup between them survives untouched.
  • Text passed into a local helper (<Td>FastMCP</Td>) is editable: the inspector walks the React tree to the call site, which is where the words actually live. The helper's own definition stays untouched.
  • Text produced by code ({entry.text}, a map, a hook) is refused — there is nothing literal to rewrite. Edit whatever feeds it.
  • Every resolution is checked against the text on screen, and every write against the text the panel read. A mismatch is refused rather than guessed at, because the alternative is silently rewriting a different element.
  • Comments anchor inside the clicked element, so self-closing elements (<img />, <ImagePlaceholder />) cannot host one; the user has to click the wrapper.

Practical consequence for authors: put text in its own element. <Td>{value}</Td> renders the same as <Td>text</Td> but only the latter can be edited from the page.

Writing for the Design panel

The panel rewrites the design object in place through an AST edit, so keep its initializer in the shape it can read:

  • export const design: DesignSystem = { … } (or const design = …) at module level, object literal only — string/number literals, nested objects. No spreads, no satisfies on the inner object, no computed keys, no values pulled from other constants.
  • Values you want tweakable live in the const. A hex you inline into a style is invisible to the panel.
  • If the document has no design const, saving from the panel creates one (and adds the DesignSystem type import). Anything the panel can't parse is reported in the panel instead of being silently overwritten.

Check the layout before you call it done

You cannot see the pages you just wrote. The framework can:

bash
open-doc check <id>     # every document if you omit the id; exits non-zero on errors

It renders each sheet at true page size and reports what a reader would call a mistake — content clipped by the page edge, a blank sheet, a heading stranded at the foot of a page, type too small to print, an image that never loaded — each with the line:column in your source. Agents driving the MCP server call check_layout for the same report, and render_page for a PNG of one sheet.

Run it after writing a document and after any edit that changes how much text is on a page. The checklist below is what you reason about; check is what confirms it.

Self-review before finishing

  • open-doc check <id> reports no errors.
  • docs/<id>/index.tsx export defaults a non-empty DocEntry[], with body content in a flow() section rather than hand-split pages.
  • Every page's root fills 100% × 100% and sets boxSizing: 'border-box' with the margin as padding.
  • For every fixed page, sum (font_size × line_height × lines) + gaps + 2×margin ≤ page height. If close, split — or move the content into the flow section. No overflow: auto escape hatches.
  • No block inside a flow() section is taller than one page (a long table has to be split by hand — the framework never splits a block).
  • Body type ≥ 13px; nothing on the page under 9px.
  • Section titles are real h1/h2/h3 elements, so the outline and TOC pick them up.
  • Contents page uses <TableOfContents />, not a hand-written list.
  • Page numbers come from useDocPageNumber() / useDocPageCount().
  • Document declares a top-level export const design: DesignSystem and pages consume var(--od-*).
  • Tables have a header row, aligned numerals (fontVariantNumeric: 'tabular-nums'), and fit the text block width.
  • Numbers that refer to other things — figures, tables, notes, pages — come from <Ref> / <Figure> / <Footnote>, never typed in.
  • Any data that exists as a file is imported, not retyped into JSX.
  • All imported assets exist on disk (docs/<id>/assets/, or root assets/ via @assets/...).
  • Every <ImagePlaceholder> marks a real image the user must supply — not decorative filler.
  • Nothing outside docs/<id>/ was edited.

Anti-patterns

  • ❌ Overflowing the page. Cropped content is invisible — split instead.
  • ❌ overflow: auto / scroll / hidden to "fit" more. The sheet doesn't scroll; you've hidden the bug.
  • ❌ Shrinking body type below 13px or margins below 60px to cram content in.
  • ❌ Hand-written contents lists or hardcoded page numbers — they go stale the moment a page is added.
  • ❌ "See Figure 3 on page 12" written by hand, or a figure caption numbered Figure 3 in the copy. Use <Ref> and <Figure>.
  • ❌ Retyping a CSV the user already has into a JSX table.
  • ❌ Fake headings (<div> styled like a title) — they vanish from the outline.
  • ❌ A slide-deck voice: 6-word bullets and 100px type. This is a document.
  • ❌ Tables built from % widths that overflow the text block, or with more than ~7 columns on portrait A4.
  • ❌ Installing packages, editing package.json / open-doc.config.ts / other documents.
  • ❌ Inventing data, sources, or citations.

© simonliu-ai-product, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 5 other files (references) in packages/core/skills/doc-authoring of simonliu-ai-product/open-doc.

  • SKILL.md
  • references/assets.md
  • references/design-system.md
  • references/long-form.md
  • references/pagination.md
  • references/tables-and-charts.md

Open the folder on GitHubat commit af6095d

Compare with similar skills

Doc Authoring next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Doc Authoring compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Doc Authoring this skillsimonliu-ai-product/open-doc108—~6.8kAutomated safety check: PassMIT
Impeccablebestofjs/bestofjs3.1k26 repos~2.6kAutomated safety check: PassMIT
Tailwindcss Developmentanonaddy/anonaddy4.9k10 repos~865Automated safety check: PassMIT
Design SystemOhh-889/skyroc79511 repos~1.7kAutomated safety check: PassMIT
Make Interfaces Feel Bettersamuelclay/NewsBlur7.6k10 repos~1.5kAutomated safety check: PassMIT
UI UX Pro Maxsaoudi-h/solar-icons19018 repos~11kAutomated safety check: NotesCustom licence

Similar skills

  • Impeccable

    bestofjs/bestofjs

    A skill your agent uses when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a…

    3.1k GitHub starsUsed in 26 repos~2.6k tokens
    Frontend & DesignAuto-check passed
  • Tailwindcss Development

    anonaddy/anonaddy

    Always invoke when the user's message includes 'tailwind' in any form.

    4.9k GitHub starsUsed in 10 repos~865 tokens
    Frontend & DesignAuto-check passed
  • Design System

    Ohh-889/skyroc

    Token architecture, component specifications, and slide generation.

    795 GitHub starsUsed in 11 repos~1.7k tokens
    Frontend & DesignAuto-check passed
  • Make Interfaces Feel Better

    samuelclay/NewsBlur

    Design engineering principles for making interfaces feel polished.

    7.6k GitHub starsUsed in 10 repos~1.5k tokens
    Frontend & DesignAuto-check passed
  • UI UX Pro Max

    saoudi-h/solar-icons

    UI/UX design intelligence for web and mobile. An agent skill from saoudi-h/solar-icons.

    190 GitHub starsUsed in 18 repos~11k tokens
    Frontend & DesignAuto-check: notes
  • Baseline UI

    ibelick/ui-skills

    Applies a fixed set of UI rules for stack, components, interaction, animation, typography and layout, or reviews a file against them with concrete fixes.

    9.6k GitHub starsUsed in 8 repos~855 tokens
    Frontend & DesignAuto-check passed

More from simonliu-ai-product/open-doc

  • Apply Comments

    simonliu-ai-product/open-doc

    A skill your agent uses when the user asks to apply, process, or clear the comments they left in the open-doc inspector — phrases like "apply the comments", "apply my edits", "I left notes on the…

    108 GitHub stars~948 tokensUpdated 4 days ago
    Auto-check passed
  • Create Doc

    simonliu-ai-product/open-doc

    A skill your agent uses when the user wants to create, draft, author, or generate a new document, report, whitepaper, proposal, memo, or spec in this open-doc repo.

    108 GitHub stars~2.4k tokensUpdated 4 days ago
    Auto-check passed
  • Current Doc

    simonliu-ai-product/open-doc

    Resolve which document, page, and (optionally) selected element the user is currently viewing in the open-doc dev server.

    108 GitHub stars~1.8k tokensUpdated 4 days ago
    Auto-check passed
  • Doc Runtime Patterns

    simonliu-ai-product/open-doc

    Implementation patterns for the @open-doc/core runtime — the split between the viewer and the published bundle, virtual modules, the flow pipeline, the ops layer, dev-only plugins, and the…

    108 GitHub stars~2.1k tokensUpdated 4 days ago
    Auto-check passed
  • Print Layout Review

    simonliu-ai-product/open-doc

    Reviews page layout, typography, and pagination code in open-doc against a print craft bar — the sheet is the deliverable, not the screen.

    108 GitHub stars~2k tokensUpdated 4 days ago
    Auto-check passed
  • Viewer UI Guidelines

    simonliu-ai-product/open-doc

    Design and accessibility rules for the open-doc viewer chrome — the browser shell, sidebars, thumbnail rail, outline, assets and design panels, inspector overlay, and menus.

    108 GitHub stars~1.6k tokensUpdated 4 days ago
    Auto-check passed

Questions about Doc Authoring

What does Doc Authoring do?

Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of…. Doc Authoring is an agent skill from simonliu-ai-product/open-doc. Technical reference for writing or editing open-doc pages — file contract, the A4/B4/A3 page canvas, print type scale, the vertical budget that decides where a page breaks, tables, charts, table of contents, page numbers, running headers/footers, and assets.

When should I use Doc Authoring?

Doc Authoring fits situations like: phrases like edit the report; change the margins; table of contents; how do documents work here.

How do I install Doc Authoring in Claude Code?

Run `npx skills add simonliu-ai-product/open-doc --skill doc-authoring -a claude-code`. Or copy the skill folder (packages/core/skills/doc-authoring in simonliu-ai-product/open-doc) into .claude/skills/doc-authoring in your project. Claude Code loads it when a task matches its description.

How do I install Doc Authoring in Codex?

Run `npx skills add simonliu-ai-product/open-doc --skill doc-authoring -a codex`. Or copy the skill folder (packages/core/skills/doc-authoring in simonliu-ai-product/open-doc) into .agents/skills/doc-authoring in your project. Codex loads it when a task matches its description.

Can I use Doc Authoring in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add simonliu-ai-product/open-doc --skill doc-authoring -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/doc-authoring, .gemini/skills/doc-authoring, .github/skills/doc-authoring and .opencode/skills/doc-authoring in your project.

What does Doc Authoring need to run?

Going by SKILL.md and its folder, Doc Authoring needs the command-line tools its instructions call (node).

Does Doc Authoring access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Doc Authoring safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Doc Authoring use?

Doc Authoring is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Doc Authoring use?

About 6.8k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 5.9k tokens, read only when the agent opens those files.

What are the alternatives to Doc Authoring?

Skills that share tags, products or a category with Doc Authoring: Impeccable (bestofjs/bestofjs, 3.1k stars), Tailwindcss Development (anonaddy/anonaddy, 4.9k stars), Design System (Ohh-889/skyroc, 795 stars) and Make Interfaces Feel Better (samuelclay/NewsBlur, 7.6k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Doc Authoring?

simonliu-ai-product (a GitHub organization) maintains it in simonliu-ai-product/open-doc, which has 108 GitHub stars. The repository holds 7 skills in this directory. The repository was last updated on October 7, 2026.

Source: simonliu-ai-product/open-doc on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.