Docs
Docs document a component or a part of your design system the way a component library’s website does: a title and introduction, sections, live examples with captions and notes, props tables, and each example’s code. You write the docs in Markdown and the examples in your own source files. Workbench renders the examples with the same adapters as TypeScript previews.
Docs are a lens. Any page can have docs, read beside its design and its other
implementations in the lens switcher. A page whose src is a Markdown file is
a Markdown page: it is its own docs and has no design.
In a docs lens, the docs use the whole canvas on white, at most 960 pixels wide and centered, and scroll like a page in a browser.
Add docs to a page
Add docs to a page with the Markdown file, and declare a docs
implementation that says how to render its examples:
implementations:
web:
kind: docs
label: Web
adapter: react
styles:
- src/styles/theme.css
collections:
- name: Components
items:
- label: Card
src: src/components/card/card.workbench.ts
docs: docs/card.md
implementations:
web: src/components/card/examples/
docs/card.md places each example with a fenced block named example:
# Card
A plain bordered surface that groups related content.
## Basic
```example basic
caption: children
```
The surface alone.
And src/components/card/examples/basic.tsx is the example:
import { Card } from '../card';
export default function Basic() {
return <Card>This is a card</Card>;
}
Open Components › Card. The page opens on its design, here the TypeScript preview. Choose Web in the lens switcher: the docs fill the canvas, with the example mounted in its panel. Choose the design lens again to return to the artboard at the size it had.
A page with docs and no docs implementation mapped still has a docs lens:
the built-in Docs lens, keyed docs. Its Markdown renders, and each
example panel says there is no docs lens rendering examples.
A Markdown page
A page whose src is a Markdown file is its own docs:
collections:
- name: Design system
items:
- label: Card
src: docs/card.md
implementations:
web: src/components/card/examples/
A Markdown page has no design lens and opens on its own docs lens. In the page
list, it shows its collection’s icon, or its own icon, and nothing is listed
under it. It can map other implementations too, such as a url or
storybook lens, beside its docs lenses.
Write the Markdown
The Markdown is GitHub Flavored Markdown: headings, lists, tables, code blocks with syntax highlighting, inline code, links, and images. Front matter at the top of the file is not shown. Write props tables by hand as Markdown tables.
-
Place an example with a fenced block whose info string is
exampleand the example’s ID. The block’s lines set its presentation:Key Description captionA short monospaced line under the panel, such as the props the example shows -
Notes are the Markdown after the block.
-
Images and links with relative paths resolve against the Markdown file.
-
A link to another page’s Markdown (
[Button](button.md)) opens that page in a docs lens: the current one when the page has it, otherwise the page’s default docs lens. Other links behave as they do in previews: with Actions on, they are recorded under Actions instead of followed.
Example IDs are kebab-case. Placing the same ID twice shows an error in the second panel.
Examples
The value a page gives a docs lens is its example source, relative to the project root:
| Source | Example | ID |
|---|---|---|
A folder, written with a trailing / |
Each file directly inside it, its default export | The file name: with-icon.tsx is with-icon |
| A file | Each named export | The export name in kebab-case: withIcon is with-icon |
A folder suits one example per file, and Show code then shows the whole file. A file keeps several examples together, and Show code shows the export’s statement. Both forms can be used in one project; each lens chooses its own.
What an example exports depends on the adapter, as a preview’s source does: a
component for react and react-native-web, a component for vue, and a
function (canvas, context) that draws into canvas for html. With
astro, each .astro file in a folder is an example: Workbench renders it
in Node, as it renders Astro previews, and the docs load its
client scripts and assets. An Astro lens takes a folder, since an .astro
file holds one component. Examples get
the preview context: context.state, context.signal, context.action(name, ...values), and context.navigate(to). They have no inputs or controls.
Each example mounts in its own panel. An example that throws shows its error in
its panel and the others still render. The panel is the containing block for
fixed-position content, so a modal or toast example opens inside it. Content
your component moves to document.body is not contained.
The docs’ own styles stay out of example panels: an example inherits your stylesheets’ body styles, as it would on a blank page with your CSS.
Lenses
A docs implementation is declared once and used by any page with docs:
| Key | Type | Required | Description |
|---|---|---|---|
kind |
docs |
yes | |
adapter |
string | yes | html, react, vue, astro, react-native-web, or an adapter registered in workbench.config.ts |
label |
string | no | The lens’s label in the lens switcher. Defaults to the name in sentence case: react-native is React native. |
styles |
list of paths | no | Stylesheets loaded with the examples, relative to the project root |
environment |
path | no | An environment around the examples, relative to the project root. It wraps inside the project’s environment from workbench.config.ts. |
Each docs implementation a page maps is one docs lens, such as Web with
React and React Native Web with React Native Web. The Markdown is the same
in every docs lens; the lens says what renders the examples. The lens switcher
lists the page’s design lens, its other implementations, and its docs lenses.
| Page key | Description |
|---|---|
docs |
The page’s Markdown file, relative to the project root. Not on a Markdown page, which is its own docs. |
implementations |
Maps each docs lens to its example source |
lens |
A Markdown page only: the docs lens it opens with. Defaults to its first. |
The lens the top bar is on stays when you open another page that has it. A page without it shows its default lens: its design, or a Markdown page’s own docs lens.
An example the docs place but the current lens doesn’t have shows Not available in and the lens’s label, so switching lenses never moves the text.
On the canvas
A docs lens puts the canvas in its docs canvas mode: the docs fill the canvas with no artboard, and scroll. Switching to the design lens or another implementation restores the artboard and its size.
- Scrolling. The wheel scrolls the docs. ⌘ or Ctrl with the wheel, or a pinch, zooms at the pointer.
- Zoom scales the docs without reflowing them: zooming out shows more of them. Zoom to fit and 100% both return to 100%. Recenter view centers the docs and keeps their scroll position.
- The top bar shows the lens switcher when the page has two or more lenses, and the state switcher when it has states. The size switcher stays visible but is disabled, because the docs fill the canvas. Open the source lists the page’s design file, then Docs, its Markdown, then each docs lens’s example source. On a Markdown page, Docs is the first row.
- Annotations stay on the docs as they scroll and zoom.
- Screenshots capture the part of the docs in view.
- Handoffs name the Markdown, the lens, and the examples in view with their source files.
Saving the Markdown, an example, or anything an example imports reloads the docs at the same scroll position.
Show code and Copy code
Below each panel, Show code expands the example’s source and Copy code copies it. The code is the source as you wrote it, highlighted: the whole file for a folder example, and the export’s statement, with the comment directly above it, for a named export.
A named export’s statement is found by its layout: from its export at the
start of a line to the next statement that starts a line. An example
re-exported from another file with export * from has no statement in the
example source, so Show code says so.
Declare a Markdown page in a definition
A *.workbench.ts file can define a Markdown page and its docs lenses
together, with paths relative to the definition:
import { defineDocs } from '@canonic2/workbench';
export default defineDocs({
id: 'design-system/colors',
title: 'Design system/Colors',
docs: './colors.md',
lenses: {
web: { label: 'Web', adapter: 'react', examples: './colors/', styles: ['./theme.css'] },
native: { label: 'React Native Web', adapter: 'react-native-web', examples: './colors.native/' },
},
lens: 'web',
});
The page is discovered like a preview and placed by its title: the first
segment names the collection, the last the page, and any between are a group. A
title without / goes in Docs. In the page list, the page shows its
icon, a Lucide name, or book-open without one. If workbench.yaml also lists the same Markdown file, its
entry is used and the definition is skipped.
To give a page with a design its docs, use docs on its entry in
workbench.yaml, as in Add docs to a page.
States
In a docs lens, the page’s state applies to the whole of its docs: every
example receives it as context.state, and the state switcher in the top
bar’s breadcrumb changes it. A page with a design declares its states as
usual. A Markdown page can declare states in workbench.yaml or in
defineDocs (states: { loading: { label: 'Loading' } }), and the page list
doesn’t list them. Showing a component’s variations side by side in the docs
is usually clearer than states.
Addresses
The address names the page by its src, not its Markdown. In a docs lens it
has no width. ! names an example and ~ a lens:
#src/components/card/card.workbench.ts!with-custom-style~web
#docs/card.md!with-custom-style~native
The docs open scrolled to the example. !example applies only in a docs lens.
A Markdown page’s own docs lens is left out of its address, as the design lens
is for other pages, and scrolling doesn’t change the address. Because !
marks the example, no src can contain it.
Export
The design-system export captures, for each docs
lens of a page with docs, the whole docs at their 960-pixel layout and each
example the lens renders, cropped to its panel. These references are filed
under the page’s src. A page with a design also gets its usual design
references; a Markdown page has no others. The Markdown and the example
sources are in the export’s sources.
The export’s browser/ viewer lists each page’s docs as its own entry. Each
opens in each of its docs lenses, with its examples running and Show code
working, from any static HTTP server.
Requirements and problems
Examples run your project’s code, so they render only in a trusted workspace
and not when previews: false is set. The Markdown still renders, and each
panel says why its example is missing.
The problems list reports, with the page named: a missing Markdown file, an example block with an unknown key or a duplicate ID (with its line), an example a lens has that the docs never place, example file names that aren’t kebab-case, and lenses that fail to build. Problems that come from a lens’s examples appear once Workbench has read them, usually a moment after the list loads.
It also reports page entries that don’t fit the docs model:
docsthat isn’t a.mdfile inside the project, ordocson a Markdown page.- A
docsimplementation mapped by a page without Markdown. - An implementation named
docson a page with Markdown and no docs lens of its own, sincedocsnames its built-in Docs lens. sizesorlensLabelon a Markdown page, which has no artboard and no design lens.lensthat isn’t one of a Markdown page’s docs lenses, orlenson any other page.