Living product docs — Playwright tour → docs site + screen specs
This recipe walks the full living-product-docs flow against a tiny example app. The result: one MDX file produces both an Astro docs page and an Excel screen-specifications spreadsheet, with drift between MDX + live UI caught at lint time.
pnpm add @ingcreators/annot-product-docspnpm add @ingcreators/annot-product-docs-astropnpm add @ingcreators/annot-product-docs-xlsxpnpm add -D @playwright/testpnpm exec playwright install chromiumScaffold:
pnpm annot-docs initThree files land in the repo:
annot-docs.config.tstests/docs/example.spec.tsdocs/books/example/SC-001-login.mdx1. Author the MDX
Section titled “1. Author the MDX”Edit docs/books/example/SC-001-login.mdx:
---annot: id: SC-001 title: Login screen xlsx: book: Screen specifications sheet: SC-001 Login role: screen---
import Screen from "@ingcreators/annot-product-docs-astro/components/Screen.astro";import Overlay from "@ingcreators/annot-product-docs-astro/components/Overlay.astro";
# Login screen
The screen where users sign in with email and password.
<Screen id="login" src="./shots/login.png">
<Overlay match={{ role: "textbox", name: "Email" }} intent="required" number={1}>**Email** — Enter your registered email address.</Overlay>
<Overlay match={{ role: "textbox", name: "Password" }} intent="required" number={2}>**Password** — 8+ characters with upper, lower, and a digit.</Overlay>
<Overlay match={{ role: "button", name: "Sign in" }} intent="action" number={3}>After client-side validation, sends the auth request to the server.</Overlay>
</Screen>2. Wire the tour
Section titled “2. Wire the tour”tests/docs/auth.spec.ts:
import { test } from "@ingcreators/annot-product-docs";
test("login flow", async ({ page, productDocs }) => { await page.goto("/login"); await productDocs.sync({ id: "login", mdxPath: "docs/books/example/SC-001-login.mdx", });});Run it against the dev server:
pnpm dev &pnpm exec playwright test tests/docs/auth.spec.tsThe tour:
- Captures
docs/books/example/shots/login.png - Rewrites the
annot:snapshotblock in the MDX with the current aria-snapshot YAML +[box=...]markers - Rewrites the
annot:attributesblock with the per-refHTML attribute extraction
The diff on second run (no UI change) is empty.
3. Render the docs site
Section titled “3. Render the docs site”Wire productDocsIntegration() into your astro.config.mjs
and build:
import { defineConfig } from "astro/config";import mdx from "@astrojs/mdx";import { productDocsIntegration } from "@ingcreators/annot-product-docs-astro";
export default defineConfig({ integrations: [mdx(), productDocsIntegration()],});pnpm buildThe output includes a page that renders the screenshot with three numbered overlay callouts composited at the exact bbox coordinates the tour captured.
4. Render the screen-specifications spreadsheet
Section titled “4. Render the screen-specifications spreadsheet”import { defineConfig } from "@ingcreators/annot-product-docs";
export default defineConfig({ meta: { projectName: "Customer management system", customerName: "Acme Corp.", }, xlsx: { defaultBook: "Screen specifications", books: { "Screen specifications": { // No `template` — use the OSS default layout. }, }, },});pnpm annot-docs-xlsx render --book "Screen specifications"Output: dist/xlsx/Screen specifications.xlsx with cover /
history / screen-list / per-screen sheets, the annotated PNG
embedded on the per-screen sheet, the overlay table populated
from the three <Overlay> entries.
For a customer-supplied template, swap the defineConfig
above with the template + templateSheets form shown on
the xlsx-templates page.
5. Lint for drift in CI
Section titled “5. Lint for drift in CI”Add the bundled GitHub Actions workflow to .github/workflows/docs.yml:
on: pull_request: paths: - "packages/web/**" - "docs/books/**" - "tests/docs/**"jobs: drift: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 - uses: actions/setup-node@v6 with: node-version-file: .nvmrc cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm dev & - run: npx wait-on http://localhost:5173 - run: pnpm exec playwright install chromium - run: pnpm exec playwright test tests/docs/ - run: pnpm annot-docs lint --ci --json --url http://localhost:5173 > drift.json || true - run: node node_modules/@ingcreators/annot-product-docs/dist/cli/annotate-pr.js < drift.jsonA PR that adds a new button — without updating the MDX — gets a yellow warning annotation on the PR diff:
⚠ added — button "Cancel" appears under <form name="sign in"> but is notdocumented in docs/books/example/SC-001-login.mdxThe author either:
- Adds an
<Overlay match>for the new button, runs the tour, the warning clears. - Runs
annot-docs lint --fixif the new button is intentionally undocumented (it gets recorded but produces no warning).
See also
Section titled “See also”- Concepts — MDX format reference.
- Drift detection — the lint flow in detail.
- Astro integration — the Image Service + per-component reference.
- Xlsx templates — customer template authoring guide.