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.
bashnpx astryx integration add doc deploying# Read it the way an app willnpx astryx docs deploying
textdoc contribution added[ok] deployingDeclare 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 that
astryx docsdoes not already list: no Core topic, and no section such astokensorlayout. To take over or add to a Core topic, seeastryx 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.
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`']]},],}],};
The sample sets a section id, so package.json needs the CLI peer "@astryxdesign/cli": ">=0.6.4". Published 0.6.3 and earlier reject id and hide every doc topic your package ships, and integration verify fails without that peer; see astryx docs cli/integrations/ship/versioning.
- Replace the
Overviewplaceholder thatintegration addwrites. - Every field is in
astryx docs authoring reference-doc.
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.
javascriptexport 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
placementis 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.
bashnpx astryx integration add doc deploying --parent acme# Open your sectionnpx astryx docs acme
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
titleandsummary: readers see them in the docs list and at the top of your section. - The guide,
docs/deploying.doc.mjs, getsplacement: {parent: 'namespace:acme', slot: 'guides'}. Later runs with--parent acmereuse the namespace doc. package.jsongets the optional peer"@astryxdesign/cli": ">=0.6.4", because an older CLI does not read sections; seeastryx 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.
javascriptplacement: {parent: 'namespace:acme', slot: 'guides', order: 10}, // route: acme/deploying
- Every field is in
astryx docs authoring authored-doc-graph-fields. - A placed guide opens only by its route,
acme/deploying. Its bare name no longer opens it.
bashnpx 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.
bashnpx astryx doctor integration docs
textseverity: [fail]code: invalid_doc_graphmessage: @acme/astryx-widgets/deploying.doc.mjs: placement.parent "namespace:cli" names no namespace; @acme/astryx-widgets declares "acme".
- A
parentthat your package does not ship, such asnamespace:cli, "names no namespace". - A
slotthat the namespace does not declare "is not a slot of namespace"; the message lists the slots it does declare. - The check exits 1; see
astryx docs cli/integrations/building-blocks/docs/check-your-docs.
Link another doc
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
genericfor a topic or guide,namespacefor a docs section,commandorfunctionfor a CLI command or API function,schemaorenumfor a schema or a fixed set of values,componentfor a component, andpageorblockfor a template. - Name a component by its export name and a template by its id. Its link reads as the command that opens it, such as
astryx component AcmeCarousel --package @acme/widgets. - Inside backticks or a code block, link syntax prints as written.
Link another package's docs
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
extendsanother package's topic, your sections still resolve against your package, so a link to the base topic's docs needs its provider.
Fix a link that names no doc
A link that names no doc prints as written, and doctor integration docs warns. The warning names a search that finds the right target.
bashnpx astryx doctor integration docs
textseverity: [warn]code: invalid_doc_graphmessage: 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>`.
- The warning keeps exit code 0, so read the report before you ship; see
astryx docs cli/integrations/building-blocks/docs/check-your-docs. - Published 0.6.4 and later read links. An older CLI, such as published 0.6.3, prints each one as written; see
astryx docs cli/integrations/ship/versioning.
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.
bashnpx astryx integration add doc acme-getting-started --replaces getting-started# The old name now opens your topicnpx astryx docs getting-started
javascriptexport 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 replaces the base section it matches, and a section that matches none is added at the end.
bashnpx astryx integration add doc acme-theming --extends themenpx astryx docs theme --index
javascriptexport 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 */]},],};
- Read the base topic's keys with
--index, and give a section that replaces one that key as itsid. A section without anidmatches only a base section with exactly its title, or one whoseidis the key its title makes, soIntegration Themesis added beside a baseIntegration themesthat has noid. - The topic keeps the base's title and description, and readers open it by the base's name.
- Replace the
Overviewplaceholder thatintegration addwrites, or it is added to the base topic. - Extend a topic to correct or add to it. A copy made with
replacesstops 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.
bashnpx astryx doctor integration docs
textseverity: [info]topic: acme-getting-startedrelationship: replacescoreTopic: getting-startedmessage: Intentional override: "acme-getting-started" replaces the Core topic "getting-started".severity: [fail]topic: colorrelationship: accidentalcoreTopic: colormessage: Accidental conflict: "color" is already a Core topic. Rename it, declare replaces: 'color' to take it over, or declare extends: 'color' 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 listnpx astryx docs acme/deploying# One sectionnpx astryx docs acme/deploying check-before-you-ship# Everythingnpx 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.
textbuild-before-you-ship Build before you ship - Build the app, then upload the `dist` folder.
Make docs findable
In a topic with more than one section, search ranks a section whose title matches the query, or that names it 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: in a topic with more than one section, 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 with search
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.
bashnpx astryx search "place a doc" --type doc
- This query returns the section of these guides that shows how to place a doc.
- Run the same search in an app that installs your package; see
astryx docs cli/integrations/ship/test-in-an-app.
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.
bashnpx astryx doctor integration docs# In an app, check an installed packagenpx astryx doctor integration docs @acme/astryx-widgets
textChecking 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, a reference block that names no doc, 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.
| Problem | Reported as | Exit code |
|---|---|---|
| A doc that does not load, such as an unknown section field or block type | [fail] invalid_doc | 1 |
A topic with a Core topic's name and no replaces or extends | [fail] accidental | 1 |
| A placement that fails, which hides the doc | [fail] invalid_doc_graph | 1 |
A reference block whose target names no doc | [fail] invalid_doc_reference | 1 |
| A link that names no doc | [warn] invalid_doc_graph | 0 |
A topic that sets replaces or extends | [info] replaces or extends | 0 |
Read the warnings before you publish. With --json, they are in data.issues, with severity: "warning".
Check each doc against its type
doctor integration validate reads every doc your package ships against the type @astryxdesign/cli/authoring publishes for its kind, the way tsc would: a hidden doc and a second doc for one name too. Test fixtures under __tests__ in the components or docs root, which those readers skip, are counted, not checked. It reports a field the type does not have (unknown_field), a missing required field (missing_field), and a value outside the field's type (wrong_type).
bashnpx astryx doctor integration validate
textChecking 1 doc(s) against the authoring types (TypeScript 6.0.3)severity: [warn]doc: components/AcmeCarousel.doc.mjsline: 7export: defaulttype: ComponentDocfield: summaryproblem: unknown_fieldmessage: Object literal may only specify known properties, and 'summary' does not exist in type 'SingleComponentDoc'.fix: Remove `summary`: SingleComponentDoc has no field `summary`.
- The folder decides the type:
ComponentDocin your components root,IntegrationTemplateDocin your templates root,ThemeDocin your themes root, and in your docs root the type the doc'stypenames. Keep the@typecomment above each doc's export too, so your editor shows the same problems as you type. - Every finding is a warning, so the exit code stays 0. With
--json, they are indata.docTypes.findings. - It uses the
typescriptyour package installs. Wheretypescriptis missing or has no JavaScript compiler API, as TypeScript 7 does, it uses@typescript/typescript6, the TypeScript 6 API package, if your package installs it. Without either, it prints[skip] Docs not type-checkedand the reason. - Every field of every doc type is in
astryx docs authoring.
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.
bashnpx astryx doctor
textid: docs-progressive-disclosurestatus: [warn]label: Documentation navigation and sizemessage: 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.
bashnpx 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 --parentwrites the peer for you.integration verifypasses a hidden guide and an accidental Core overlap; onlydoctor integration docscatches them.- Everything else it checks is in
astryx docs cli/commands/integration-verifyandastryx docs cli/integrations/ship/checks.