name: docs-update
description: Author, restructure, or update documentation for the Astro/Starlight docs site. Covers ADDING a new docs page or route (MDX + sidebar registration in astro.config.mjs + llms.txt/llms-full.txt inclusion), which prose/MDX and API-reference docs to check after a code change, how to write them, the <Audience> and llmsFull curation knobs, and regenerating typedoc API docs. Use whenever you create, move, rename, or edit any file under apps/docs/, or after any feature, fix, or refactor that touches user-visible behaviour. (For building a new interactive demo component, use the astro-demo skill instead.)
argument-hint: optional what-changed description
Documentation Update Guide
Use this skill whenever you touch docs — authoring a brand-new page/route, restructuring or moving pages, or syncing existing docs after a code change. It is the authoritative reference for the docs site's structure, navigation, and the autogenerated LLM corpus — read it before writing, not only as a post-change checklist.
- Adding or moving a page? Start at Adding a new docs page — it covers the steps that are easy to miss (sidebar registration,
llmsFullinclusion, redirects). - Changed code and need to sync docs? Use the Quick Decision Matrix below.
llms.txt/llms-full.txtare autogenerated — never hand-edit them. Both are built at docs-build time by the Astro endpointsapps/docs/src/pages/llms.txt.tsandllms-full.txt.tsfrom the curated MDX/MD doc pages, the demo.astrosources, and JSDoc-derived TypeDoc MDX. You influence their content only by editing those sources — which makes keeping the MDX/curated/source docs accurate more important than before, not less. See Where LLM-file content comes from for the few curation knobs (page frontmatter, ordering, agent preamble). This skill does not manually update the rootllms.txt/llms-full.txt.
Adding a New Docs Page
Adding a page is more than dropping an .mdx file — the page is invisible until you register it in the sidebar, and it silently joins the LLM corpus unless you opt it out. Work through all five steps:
Create the MDX page under
apps/docs/src/content/docs/grid/(or a subfolder likegrid/plugins/,grid/guides/). Every page needs frontmattertitleanddescription(thedescriptionis reused as the page's link blurb inllms.txt). Follow the conventions in How to Write Each Doc Type.Register it in the sidebar — this is the step most easily forgotten. The navigation tree is hand-curated in
apps/docs/astro.config.mjsunderstarlight({ sidebar: [...] }). A new page does not appear automatically unless it lands in a folder covered by anautogenerate: { directory: ... }entry (currently onlygrid/guides,grid/plugins, and the TypeDocgrid/api/**trees). For a top-level or curated page, add an explicit entry to the matching group:// apps/docs/astro.config.mjs → integrations → starlight → sidebar → 'Grid' group { label: 'Introduction', slug: 'grid/introduction' }, { label: 'AI Assistance', slug: 'grid/ai' }, // ← your new page (slug = path under content/docs/, no extension)To create a brand-new collapsible section, push a
{ label, collapsed, items: [...] }object instead. Match the placement to the page's audience (top-level conceptual pages go in theGridgroup nearIntroduction/Getting Started).Decide LLM-corpus inclusion — by default every prose page is inlined into
llms-full.txtand linked inllms.txt. Opt a page out of the full corpus (kept in the index, removed from the inlined dump) with frontmatterllmsFull: false. Use this for pages that teach an agent nothing about building with the grid — e.g. release history. Current opt-outs:grid/changelog.mdxand the three adapterchangelog.mdxpages. For a page where only part of the content is human- or agent-only, prefer per-block<Audience>regions instead (see Audience-targeted content).Place it in the generated ordering (only if it's a new top-level slug or a new section).
apps/docs/src/pages/_llm-sources.tscontrols how the generated files group and order pages:sectionOf()classifies a slug into a section, andCORE_ORDERsets the reading order for top-levelgrid/*pages. A newgrid/<name>.mdxpage that isn't listed inCORE_ORDERsorts to the alphabetical tail of "Core Documentation" — add it toCORE_ORDERif it needs a specific position.If you moved or renamed a page, add a redirect in
astro.config.mjs(redirects: { '/grid/old-path/': '/grid/new-path/' }) so inbound links and bookmarks keep working, and update any internal links per the Internal-link rules.
Then build and run the link check (see Verification).
Quick Decision Matrix
The matrix is grouped by change category. If your change spans multiple categories, apply each matching row — there is no precedence ordering between groups (you update all matching surfaces). The LLM files are intentionally absent: updating the MDX/source docs below regenerates them automatically.
Code / API changes
| What Changed | Update These Docs |
|---|---|
| New public API | api-reference.mdx, regenerate TypeDoc |
| New event | api-reference.mdx, plugin .mdx if plugin-specific, JSDoc on DataGridEventMap |
| Breaking change | CHANGELOG (migration guide), affected MDX |
Plugin changes
| What Changed | Update These Docs |
|---|---|
| New plugin | Plugin README, plugin .mdx, plugins/index.mdx |
| Plugin config change | Plugin .mdx, plugin README, plugin JSDoc |
Adapter changes
| What Changed | Update These Docs |
|---|---|
| New adapter feature | Adapter README, adapter MDX docs |
Theming / styling changes
| What Changed | Update These Docs |
|---|---|
| New CSS variable | theming.mdx, grid.css registry comment |
Workspace / tooling changes
| What Changed | Update These Docs |
|---|---|
| Workflow/convention change | .github/copilot-instructions.md, AGENTS.md |
| New skill or tooling | .github/skills/, AGENTS.md if Nx-related |
Documentation Inventory
Hand-Written (update manually)
| Category | Location | When to Update |
|---|---|---|
| Grid README | libs/grid/README.md |
New features, API changes, install instructions |
| Adapter READMEs | libs/grid-{angular,react,vue}/README.md |
Adapter feature changes |
| Plugin READMEs | libs/grid/src/lib/plugins/*/README.md |
Plugin feature changes |
| Core MDX docs | apps/docs/src/content/docs/grid/*.mdx |
Core features, theming, architecture, getting started |
| Plugin MDX docs | apps/docs/src/content/docs/grid/plugins/*.mdx |
Plugin features, config options, examples |
| Adapter MDX docs | apps/docs/src/content/docs/grid/adapters/*.mdx |
Framework-specific usage, examples |
| Demo components | apps/docs/src/components/demos/**/*.astro |
New demos, interactive feature showcases |
| Copilot instructions | .github/copilot-instructions.md |
Workflow, conventions, architecture changes |
| Agent instructions | AGENTS.md |
Nx or workspace convention changes |
| Contributing guide | CONTRIBUTING.md |
Development workflow changes |
| Architecture doc | libs/grid/ARCHITECTURE.md |
Internal design changes |
Auto-Generated (regenerate, don't hand-edit)
| Category | Generated By | Command / Trigger |
|---|---|---|
LLM index (llms.txt) |
apps/docs/src/pages/llms.txt.ts |
bun nx build docs |
LLM full guide (llms-full.txt) |
apps/docs/src/pages/llms-full.txt.ts |
bun nx build docs |
| TypeDoc API pages | typedoc-to-mdx.ts scripts |
bun nx typedoc <project> |
| CHANGELOGs | release-please |
Automatic on release PR |
| Custom elements manifest | @custom-elements-manifest/analyzer |
bun run cem |
The LLM files are derived from the MDX/MD doc pages, the demo .astro sources, and the JSDoc-generated TypeDoc MDX — so they stay in sync automatically when you update those sources. Never hand-edit the root llms.txt / llms-full.txt.
How to Write Each Doc Type
Multi-Path Features
When a feature has multiple configuration paths (e.g., CSS vs JS, declarative vs programmatic), apply this checklist to every documentation file that has a dedicated section explaining that feature, a configuration table for it, or a code example using it (not merely a passing hyperlink or index entry) (i.e. the feature's plugin/core MDX page, its README, and any adapter MDX page that documents it in detail). You do not need to touch unrelated documentation files. (The LLM files pick up the change automatically once the MDX is updated.)
For each such file, complete all four steps in order:
- State that both paths exist — even if the file only covers one in detail.
- Explain when to use which — include a comparison table with trade-offs.
- Cross-reference — link to the other doc surface that covers the other path in detail.
- Identify the recommended default — guide developers toward the simpler path unless they have specific needs.
Apply this to: icons (CSS vars vs gridConfig.icons), column config (declarative <tbw-grid-column> vs JS columns), features (declarative features vs manual plugins), styling (CSS custom properties vs registerStyles() vs cellClass/rowClass), etc.
Plugin MDX (.mdx)
Plugin MDX pages live in apps/docs/src/content/docs/grid/plugins/. Structure:
---
title: Plugin Name
description: One-line description.
---
import PluginDefaultDemo from '@components/demos/plugin-name/PluginNameDefaultDemo.astro';
import ShowSource from '@components/ShowSource.astro';
## Installation
\`\`\`typescript
import { MyPlugin } from '@toolbox-web/grid/plugins/my-plugin';
\`\`\`
## Basic Usage
<ShowSource component="plugin-name/PluginNameDefaultDemo">
<PluginDefaultDemo />
</ShowSource>
## Configuration
Auto-generated from the plugin's config interface by \`genPluginConfigTable()\`.
## Events
| Event | Detail | Description |
| ----- | ------ | ----------- |
## API
Public methods and properties.
Plugin JSDoc conventions for auto-generated API docs:
## Configuration Optionstables are auto-generated from the constructor's config interface — do not hand-write them in JSDoc## Programmatic APItables are redundant with auto-generated Methods — do not hand-write them in JSDoc- Use
{@link TypeName}in hand-written sections (Column Configuration, CSS Custom Properties, Events) to cross-link types- Run
bun nx typedoc gridafter changing JSDoc to regenerate MDX
Plugin README (README.md)
Keep concise — links to docs site for live examples:
# @toolbox-web/grid — Plugin Name
Brief description.
## Usage
\`\`\`typescript
import { MyPlugin } from '@toolbox-web/grid/plugins/my-plugin';
\`\`\`
## Options
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
## Documentation
See the [docs site](https://toolboxjs.com/grid/plugins/my-plugin/) for live examples.
Where LLM-file content comes from
llms.txt (concise index) and llms-full.txt (full guide) are autogenerated at docs-build time by apps/docs/src/pages/llms.txt.ts and llms-full.txt.ts. Do not hand-edit the root files — they are derived from:
| Source | Drives |
|---|---|
MDX/MD pages under apps/docs/src/content/docs/grid/** |
Prose body of llms-full.txt; link entries in llms.txt |
Demo .astro under apps/docs/src/components/demos/** |
Inlined <script> code blocks in llms-full.txt |
| JSDoc → TypeDoc MDX | The linked ## API Reference index (per-symbol .md pages) |
libs/grid/src/lib/data/css-variable-registry.* |
The CSS-variables reference block |
So updating the MDX/curated/source docs is how you update the LLM files — keep them accurate and the generated files follow automatically. The only manual curation knobs (touch these only when adding/removing pages or changing ingestion behaviour, not for routine content):
<Audience only="human|agent">regions inside a page — split a single page's content by consumer (see Audience-targeted content below). This is the content-level knob; the three below are page-level / global.- Page frontmatter
llmsFull: false— omits a page from thellms-full.txtcorpus while keeping it linked inllms.txt(used for pages that teach an agent nothing about building with the grid — currently thechangelog.mdxpages). See_llm-markdown.ts. - Ordering & section maps in
apps/docs/src/pages/_llm-sources.ts—CORE_ORDER,SECTION_ORDER,API_AREA_ORDER,sectionOf(),apiAreaOf(). Adjust when a new doc section/area needs explicit placement. - Agent preamble in
apps/docs/src/pages/_llm-agent-preamble.ts— the hand-authoredRULE 0/ critical-rules / anti-patterns block prepended to both files. Keep it in sync with the human docs it summarises (Getting Started, Plugins Overview) when those rules change.
To verify the generated output after a docs change, run bun nx build docs and inspect the emitted /llms.txt and /llms-full.txt.
Audience-targeted content
<Audience> (apps/docs/src/components/Audience.astro) lets one MDX page carry content meant for only one consumer. Import it in the page's frontmatter (import Audience from '@components/Audience.astro';) and wrap a block:
<Audience only="agent">
_## Steering heading (note the `_` prefix — see authoring rules)
Steering directive / extra gotcha / copy-paste recipe that helps a code-generating agent.
</Audience>
<Audience only="human">
## Steering heading (plain `##` — renders on the site)
Motivational aside, marketing prose, or screenshot-driven walkthrough that would only dilute agent guidance.
</Audience>
only value |
HTML docs site | Agent corpus (llms-full.txt + per-page .md) |
|---|---|---|
"human" |
Rendered | Stripped (removed entirely) |
"agent" |
Omitted (renders nothing) | Kept (wrapper tags unwrapped, inner Markdown flows through) |
The two paths are independent: the HTML behaviour is in Audience.astro, the corpus behaviour is in resolveAudienceRegions() (apps/docs/src/pages/_llm-markdown.ts).
When to reach for it — use <Audience> for a portion of an otherwise-shared page. For a whole page that should stay out of the agent corpus, use frontmatter llmsFull: false instead.
Use only="agent" for… |
Use only="human" for… |
|---|---|
| Front-loaded "do X, never Y" steering directives the rendered page shouldn't repeat | Marketing/motivational prose, "why you'll love this" framing |
| Extra failure-mode gotchas and edge cases that would clutter the human page | Screenshot/GIF-driven walkthroughs that don't translate to text |
| Copy-paste, full-context code recipes (imports + config) tuned for one-shot generation | Onboarding narrative, tone-setting intros, anecdotes |
| Explicit anti-patterns ("don't add SelectionPlugin just to make a row clickable") | Calls-to-action, links to live demos/playgrounds |
Authoring rules (enforced by the regex transform):
- Leave a blank line after the opening tag and before the closing tag so MDX parses the children as Markdown blocks.
- Headings inside
only="agent"blocks must be prefixed with_— write_## Heading, not## Heading. Starlight harvests real##headings into the human page's table-of-contents at compile time (before the runtime conditional render), so a bare##in an agent-only block leaks a phantom TOC link on the human page. The_sentinel makes it a non-heading for the human build; the agent-markdown transform (out.replace(/^_(#{1,6} )/, …)in_llm-markdown.ts) strips the_to restore a real, navigable heading in the corpus. The strip runs in prose only, so_#inside a code fence is left intact. (Human-only blocks render normally, so they use plain##.) - Do not nest
<Audience>blocks — the non-greedy match stops at the first</Audience>. - Keep agent-only directives consistent with the human docs they shadow; don't let the two audiences drift into contradicting each other.
- After adding/editing an
<Audience>block, runbun nx build docsand confirm the agent content lands in/llms-full.txt(and the human content does not, and vice-versa).
api-reference.mdx (apps/docs/src/content/docs/grid/api-reference.mdx) Events section — Update when:
- Event cancelability changes (the cancelable events table is curated)
- Framework-specific listening patterns change
:::note
Individual event descriptions, payload types, and code examples are auto-generated from JSDoc in DataGridEventMap — update the JSDoc, not the curated docs. Run bun nx typedoc grid to regenerate.
:::
Core MDX Docs (apps/docs/src/content/docs/grid/)
| File | Contents |
|---|---|
index.mdx |
Landing page, key features |
getting-started.mdx |
Installation, first grid setup |
api-reference.mdx |
Public API reference (properties, methods, events) |
theming.mdx |
CSS custom properties, theme files |
core.mdx |
Core features guide (columns, rows, etc.) |
custom-plugins.mdx |
How to write a plugin |
performance.mdx |
Performance tips |
accessibility.mdx |
A11y features |
troubleshooting.mdx |
Common issues and fixes |
demos.mdx |
Full-featured demo applications |
comparison.mdx |
Feature comparison table |
architecture.mdx |
Internal architecture documentation |
Regenerating API Docs
When public API changes (new exports, changed signatures, updated JSDoc):
# Generate TypeDoc JSON and convert to MDX
bun nx typedoc grid
bun nx typedoc grid-angular
bun nx typedoc grid-react
bun nx typedoc grid-vue
Output goes to libs/*/docs/api/ (MDX pages) and libs/*/docs/api-generated/api.json.
Do NOT hand-edit files in docs/api/ or docs/api-generated/ — they are regenerated.
Writing Good JSDoc
JSDoc comments feed into TypeDoc API docs:
/**
* Configuration for the grid component.
*
* @example
* ```typescript
* const config: GridConfig = {
* columns: [{ field: 'name', header: 'Name' }],
* };
* ```
*
* @remarks
* The config is merged with defaults via `mergeEffectiveConfig()`.
*
* @see {@link ColumnConfig} for column-level options
*/
export interface GridConfig { ... }
Key tags:
@example— Code examples (rendered in docs)@remarks— Extended description@see— Cross-references@deprecated— Mark deprecated APIs with migration info@internal— Exclude from public API docs@since— Version when feature was added@category— Routes exports to different TypeDoc sidebar sections (grid core only, see below)@group— Organizes class members into subsection headings within a page (see below)@fires— Documents events emitted by a method; collected into Events tables
@category and @group — Controlling API Doc Output
The typedoc-to-mdx.ts scripts convert TypeDoc JSON into MDX pages. Two JSDoc tags control where and how exports appear.
@categoryand@groupare only processed by the grid core script (libs/grid/scripts/typedoc-to-mdx.ts). Adapter scripts use hard-coded name lists instead (see below).
@category — API Doc Routing (Grid Core)
Add @category to top-level exports in types.ts, constants.ts, or grid.ts to control which API docs section they land in. The routing logic lives in processCoreModule():
@category Plugin Development→Grid/API/Plugin Development/{Kind}/{Name}@category Framework Adapters→Grid/API/Framework Adapters/{Kind}/{Name}- Any other value or no
@category→Grid/API/Core/{Kind}/{Name}
Only Plugin Development and Framework Adapters cause routing changes. Other values (e.g., Data Management, Events) are informational — they still route to Core. Check processCoreModule() in libs/grid/scripts/typedoc-to-mdx.ts for the current routing logic.
DataGridElement split: The DataGridElement class is special-cased by genDataGridSplit() — it produces 3 separate MDX documents by filtering members:
- Public API (
DataGridElement.mdx) — members without_prefix, without@internal, without@category Framework Adapters - Plugin API (
DataGridElement-PluginAPI.mdx) —_-prefixed members (not__) or@internal Plugin API - Framework Adapters (
DataGridElement-Adapters.mdx) — members tagged@category Framework Adapters
When adding a new member to DataGridElement, choose the right tag/prefix to place it in the correct document.
@group — Section Headings Within a Page
Add @group GroupName to class members (accessors, methods) to organize them under headings within a generated MDX page. Currently used on DataGridElement members. The genMembersSectionByGroup() function renders groups in a defined order — check the groupOrder array in that function for the current list. Members without @group fall into a generic section at the end.
When adding a new member, use an existing @group value if it fits. If no group fits, create a new one — it will be appended after the ordered groups. If the new group should appear in a specific position, add it to the groupOrder array in genMembersSectionByGroup().
@internal Modifier — Visibility Control
@internalalone → excluded from all generated docs@internal Plugin API→ included only in the Plugin API document_prefix (without@internal) → same as@internal Plugin APIby convention__prefix → excluded from all docs (deeply internal)
Adapter Scripts — Hard-Coded Categorization
Adapter scripts (libs/grid-{angular,react,vue}/scripts/typedoc-to-mdx.ts) do NOT use @category. They categorize by checking export names against hard-coded lists (e.g., isDirective() checks a directiveNames array in Angular). Each adapter sorts into folders like Directives/, Components/, Hooks/, Types/, Utilities/, etc.
When adding a new export to an adapter: check the script's categorization functions. If your export doesn't match an existing check (e.g., a new Angular directive), add its name to the corresponding list so it lands in the right folder instead of the catch-all Utilities/. After updating the categorization list, run bun nx typedoc grid-<adapter> (e.g., bun nx typedoc grid-angular) to regenerate the adapter API MDX pages.
Verification
After updating docs:
# Verify docs site builds cleanly
bun nx build docs
# Authoritative broken-link gate (NOT an nx target — Astro does NOT fail on broken links)
node .github/skills/docs-update/check-doc-links.mjs
bun nx build docspassing (EXIT=0) does not mean links are valid — Astro silently renders dead links.node .github/skills/docs-update/check-doc-links.mjsis the real gate; it must report "No broken internal links found." If the script reports broken links, fix every broken link in files you modified as part of this change before committing. For a broken link in a file you did not modify, treat it as pre-existing — do not fix it in this change; note the file and line in your PR description so it can be tracked separately.
Internal-link rules (avoid the common 404s)
trailingSlash: 'always'— every internal link MUST end with/(/grid/plugins/shell/, not/grid/plugins/shell).- Anchors must match a real heading slug — grep the target page's
^#{1,4}headings before linking to#some-anchor; renamed/removed headings are a frequent source of dead anchors. - TypeDoc generates NO page for area roots or
export constobjects.tools/typedoc-mdx-shared.tsKIND_FOLDER_MAPonly emitsClasses/,Interfaces/,Functions/,Types/— there is no per-area index page and noVariables/folder.- ❌
/grid/api/framework-adapters/(area root) · ❌/grid/api/plugin-development/· ❌/grid/api/core/variables/gridclasses/ - ✅ link to a concrete generated child page (
/grid/api/<area>/<kind>/<symbol>/, slug lowercased), e.g./grid/api/framework-adapters/interfaces/frameworkadapter/or/grid/api/plugin-development/classes/basegridplugin/. - Exported const objects (
GridClasses,GridDataAttrs,GridSelectors,GridCSSVarsincore/constants.ts) are public API but have NO generated page — keep them as inline code, don't link. Only their TYPE aliases (GridClassName,GridDataAttr) getTypes/pages.
- ❌
Pre-Commit Documentation Review
Before each commit, quickly scan:
- Did I change any public API? → Update
api-reference.mdx+ JSDoc; regenerate TypeDoc - Did I change any plugin? → Update plugin
.mdxand README - Did I add/remove CSS variables? → Update
theming.mdx,grid.cssregistry comment - Did I change events? → Update
api-reference.mdx, plugin.mdx, JSDoc onDataGridEventMap - Did I change conventions? → Update
copilot-instructions.md
The llms.txt / llms-full.txt files are regenerated from the above sources at docs-build time — there is nothing to hand-edit for them.