Writing docs

Write docs that ship with your package and that people and agents find by search or one level at a time.

Add a topic with the CLI

Run integration add doc in your package to add a topic. It writes the topic file and declares the docs root in astryx.integration.mjs.

bash
npx astryx integration add doc deploying
# Read it the way an app will
npx astryx docs deploying
text
doc contribution added
​
[ok] deploying
​
Declare doc root ./docs in astryx.integration.mjs.
​
- docs/deploying.doc.mjs
- astryx.integration.mjs
  • Name the topic in lowercase kebab-case, such as deploying. Readers type the name as a command argument, so it holds only letters, digits, _, and -.
  • Keep the name stable: readers and links find the topic by it.
  • Pick a name no Core topic uses. To take over or add to a Core topic, see astryx docs cli/integrations/building-blocks/docs/extend-or-replace.

Write the sections

A topic is a plain object with type: 'generic', a name, a title, a one-sentence description, and sections. Each section has a title and a list of content blocks.

docs/deploying.doc.mjs
javascript
/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
export default {
type: 'generic',
name: 'deploying',
title: 'Deploying',
description: 'Ship an app built with Acme widgets.',
sections: [{
id: 'build-before-you-ship',
title: 'Build before you ship',
content: [
{type: 'prose', text: 'Build the app, then upload the `dist` folder.'},
{type: 'code', lang: 'bash', code: 'npm run build'},
{type: 'list', style: 'unordered', items: ['Keep `dist` out of git.']},
{type: 'table', headers: ['Variable', 'Value'], rows: [['`NODE_ENV`', '`production`']]},
],
}],
};
  • Content blocks are prose, code, list, and table, as shown; heading, with a level from 3 to 6 and a text; and token-ref, which inlines a token table from another topic.
  • id is optional: a stable key for the section. Without it, the key comes from the title. A stable CLI before 0.6.4 cannot read id; see astryx docs cli/integrations/ship/versioning.
  • Replace the Overview placeholder that integration add writes. Every field is in astryx docs authoring.

Pick the doc kind

Every doc is a .doc.mjs file whose type says what it describes. integration add writes the right type and file for each kind.

You document`type`File that `integration add` writes
A guide or topic'generic'docs/deploying.doc.mjs
Your package's docs section'namespace'docs/acme.doc.mjs
A component'component'components/AcmeCarousel.doc.mjs, beside AcmeCarousel.tsx
A template'page' or 'block'templates/acme-dashboard.doc.mjs, beside acme-dashboard.tsx
A theme'theme'themes/ocean/oceanTheme.doc.mjs, beside oceanTheme.ts

Only guides, topics, and your docs section go in the docs root. The others have their own guides: astryx docs cli/integrations/building-blocks/components, astryx docs cli/integrations/building-blocks/templates, and astryx docs cli/integrations/building-blocks/themes.

Keep topics in the docs root

The docs field in astryx.integration.mjs names the folder that holds your topics. The CLI reads every .doc.mjs file under it, in subfolders too.

astryx.integration.mjs
javascript
export default {
docs: './docs'
};
  • Readers open a topic by its name, not its file name. Name the file after the doc, <name>.doc.mjs, so each one is easy to find.
  • A topic with no placement is a flat topic: the docs list shows it under Topics, and readers open it by its name.
  • To give your package its own section in the docs tree instead, see astryx docs cli/integrations/building-blocks/docs/sections-and-placement.

Add a docs section

Give your package its own section in the docs tree with integration add doc <name> --parent <section>. The first run also writes the section's namespace doc.

bash
npx astryx integration add doc deploying --parent acme
# Open your section
npx astryx docs acme
docs/acme.doc.mjs
javascript
/** @type {import('@astryxdesign/cli/authoring').NamespaceDoc} */
export default {
type: 'namespace',
name: 'acme',
title: 'Acme',
summary: 'Guides for Acme.',
slots: {
guides: {title: 'Guides', accepts: {kinds: ['generic']}},
},
};
  • Edit its title and summary: readers see them in the docs list and at the top of your section.
  • The guide, docs/deploying.doc.mjs, gets placement: {parent: 'namespace:acme', slot: 'guides'}. Later runs with --parent acme reuse the namespace doc.
  • package.json gets the optional peer "@astryxdesign/cli": ">=0.6.4", because an older CLI does not read sections; see astryx docs cli/integrations/ship/versioning.

Place a doc

A guide names its one home with placement: a namespace of your package, a slot in it, and an order. Its route is the section name, then the guide name.

javascript
placement: {parent: 'namespace:acme', slot: 'guides', order: 10}, // route: acme/deploying
  • parent is namespace:<name>, a namespace that your own package ships. You cannot place a doc in the CLI's sections or in another package's.
  • slot is a slot that the namespace declares for the doc's kind. You can leave it out when the namespace has only one slot.
  • order is an integer that sorts the guides in the slot and sets their Previous and Next moves. Guides without one come last, by name.
  • A placed guide opens only by its route, acme/deploying. Its bare name no longer opens it.
bash
npx astryx docs acme/deploying

Fix a failed placement

A failed placement hides the doc: it gets no route and does not show in the docs list. doctor integration docs fails with invalid_doc_graph and names what to fix.

bash
npx astryx doctor integration docs
text
severity: [fail]
code: invalid_doc_graph
message: @acme/astryx-widgets/deploying.doc.mjs: placement.parent "namespace:cli" names no namespace; @acme/astryx-widgets declares "acme".

Write {@link [provider:]kind:name} in prose, list items, and table cells to link another doc. The CLI prints the command that opens it, so the link keeps working when the doc moves.

javascript
{type: 'prose', text: 'Before you ship, read {@link generic:deploying}.'},
{type: 'list', style: 'unordered', items: ['All guides: {@link namespace:acme}.']},
{type: 'table', headers: ['Task', 'Guide'], rows: [['Ship', '{@link generic:deploying}']]},

Each link reads as a command. This link, astryx docs cli/integrations/building-blocks/docs/extend-or-replace, opens the next guide.

  • The kind is generic for a topic or guide, namespace for a docs section, and command or function for a CLI command or API function.
  • A link to a component or a template does not resolve. Write its name in backticks instead, such as AcmeCarousel.
  • Inside backticks or a code block, link syntax prints as written.

A link without a provider resolves against your own package. To link the CLI's docs, or another package's, start the target with that package's name, such as @astryxdesign/cli:.

javascript
// Resolves: the CLI's doctor command
{type: 'prose', text: 'Check the app with {@link @astryxdesign/cli:command:doctor}.'},
// Does not resolve: looks for a doctor command in your package
{type: 'prose', text: 'Check the app with {@link command:doctor}.'},
  • Name a CLI command the way you type it, spaces included, such as @astryxdesign/cli:command:doctor integration docs.
  • In a topic that extends another package's topic, your sections still resolve against your package, so a link to the base topic's docs needs its provider.

A link that names no doc prints as written, and doctor integration docs warns. The warning names a search that finds the right target.

bash
npx astryx doctor integration docs
text
severity: [warn]
code: invalid_doc_graph
message: acme/deploying § check-before-you-ship: "command:doctor" names no doc. Find it with `astryx search doctor --type doc`, then name it as `[<provider>:]<kind>:<name>`.

Replace a topic

Set replaces to take over an existing topic, such as Core's getting-started. Readers who open the old name get your topic.

bash
npx astryx integration add doc acme-getting-started --replaces getting-started
# The old name now opens your topic
npx astryx docs getting-started
docs/acme-getting-started.doc.mjs
javascript
export default {
type: 'generic',
name: 'acme-getting-started',
replaces: 'getting-started',
title: 'Acme getting started',
description: 'Install Acme widgets and render your first carousel.',
sections: [/* ... */],
};

The docs list shows your topic in place of the old one. Because your topic has its own name, the old name keeps resolving to it, so links and agents that learned the old name still land on your topic.

Extend a topic

Set extends to merge sections into an existing topic instead of owning it. A section with the same key replaces the base section, and a new section is added at the end.

bash
npx astryx integration add doc acme-theming --extends theme
npx astryx docs theme --index
docs/acme-theming.doc.mjs
javascript
export default {
type: 'generic',
name: 'acme-theming',
extends: 'theme',
title: 'Acme theming',
description: 'Theme an app that uses Acme widgets.',
sections: [
{id: 'quick-start', title: 'Quick Start', content: [/* replaces the base section */]},
{id: 'use-the-ocean-theme', title: 'Use the ocean theme', content: [/* added at the end */]},
],
};
  • A section's key is its id, or a key made from its title. Read the base topic's keys with --index.
  • The topic keeps the base's title and description, and readers open it by the base's name.
  • Replace the Overview placeholder that integration add writes, or it is added to the base topic.
  • Extend a topic to correct or add to it. A copy made with replaces stops getting the owner's fixes.

Check overlaps with Core topics

A topic sets replaces or extends, never both, and a placed guide sets neither. A topic that uses a Core topic's name with neither is an accidental conflict: apps keep reading the Core topic.

bash
npx astryx doctor integration docs
text
severity: [info]
topic: acme-getting-started
relationship: replaces
coreTopic: getting-started
message: Intentional override: "acme-getting-started" replaces the Core topic "getting-started".
​
severity: [fail]
topic: tokens
relationship: accidental
coreTopic: tokens
message: Accidental conflict: "tokens" is already a Core topic. Rename it, declare replaces: 'tokens' to take it over, or declare extends: 'tokens' to merge sections.
  • An intentional overlap prints as [info]. An accidental one fails with exit code 1.
  • A topic that sets both fails as invalid_doc, and so does a placed guide that sets either one.

Keep each read short

Readers open one section at a time, so give each section one idea and keep it to about 30 lines. npx astryx doctor warns on any read over 32 KB.

  • When a section needs a second idea, split it into two sections.
  • Keep a topic to a few sections. When it grows past five, split it into more guides in your docs section.
  • A topic with more than one section reads as its section list. Readers open one section by its key, or the whole topic with --full.
bash
# The section list
npx astryx docs acme/deploying
# One section
npx astryx docs acme/deploying check-before-you-ship
# Everything
npx astryx docs acme/deploying --full

Lead with the summary

A section's first prose block, or its first list item, is its summary in section lists and search results. Make it answer the section's question in one or two sentences.

  • The summary is cut at about 240 characters.
  • A code block first does not count: the summary comes from the next prose block.
  • Open with the answer, not with background.
text
build-before-you-ship Build before you ship - Build the app, then upload the `dist` folder to your host.

Make docs findable

Search ranks a query that matches a whole title, or an identifier in backticks, above words in body text. Title each section with the task a reader searches for.

  • Name the task in the words a reader types, such as "Deploy to production". Avoid titles such as "Overview" or "Details".
  • Write field names, file names, and error codes in backticks, such as deployTarget: search treats each one as a keyword.
  • Other words in the summary and body match too, but rank below titles and identifiers. The summary shows under each hit, so make it answer the query.

Test a doc the way a new reader finds it: search for the question, and check that the first hit answers it. Quote a query of more than one word.

bash
npx astryx search "place a doc" --type doc

Run the docs check

Run doctor integration docs in your package to check the docs tree, every link, and overlaps with Core topics. Pass a package name to check an installed package.

bash
npx astryx doctor integration docs
# In an app, check an installed package
npx astryx doctor integration docs @acme/astryx-widgets
text
Checking integration docs: @acme/astryx-widgets@1.0.0
​
[ok] The docs tree and every link in these docs check out.
​
[ok] No doc topics overlap with Core.

Its arguments and exit codes are in astryx docs cli/commands/doctor-integration-docs.

Know what fails the check

A doc that does not load, an accidental Core overlap, or a failed namespace or placement fails the check with exit code 1. A link that names no doc only warns, and the exit code stays 0.

ProblemReported asExit code
A doc that does not load, such as an unknown section field or block type[fail] invalid_doc1
A topic with a Core topic's name and no replaces or extends[fail] accidental1
A placement that fails, which hides the doc[fail] invalid_doc_graph1
A link that names no doc[warn] invalid_doc_graph0
A topic that sets replaces or extends[info] replaces or extends0

Read the warnings before you publish. With --json, they are in data.issues, with severity: "warning".

Check read size

npx astryx doctor also measures every read and warns on one over 32 KB. Run it in your package or in an app that installs it; doctor integration docs does not check size.

bash
npx astryx doctor
text
id: docs-progressive-disclosure
status: [warn]
label: Documentation navigation and size
message: acme/deploying build-before-you-ship: 46 KB, over the 32 KB one read may return

Split a section over the limit into smaller ones, each with its own key. See astryx docs cli/commands/doctor.

Check the CLI peer

integration verify fails a package that ships a docs section, a placed guide, or a doc section with an id without an @astryxdesign/cli peer of >=0.6.4. It does not run the docs check, so run both.

bash
npx astryx integration verify
text
- [fail] The package ships a namespace doc or a placed guide but declares no @astryxdesign/cli peer. A stable CLI before 0.6.4 does not read the docs tree, and can hide every doc topic the package ships. Declare "@astryxdesign/cli": ">=0.6.4" in peerDependencies (optional in peerDependenciesMeta, if the CLI is not required).
  • integration add doc --parent writes the peer for you.
  • integration verify passes a hidden guide and an accidental Core overlap; only doctor integration docs catches them.
  • Everything else it checks is in astryx docs cli/commands/integration-verify and astryx docs cli/integrations/ship/checks.