Skip to content

Docsite Architecture

Cindy Zhang edited this page Jun 23, 2026 · 1 revision

Docsite Architecture

The Astryx docsite lives at xds-sandbox.vercel.app and is a Next.js 15 app that renders documentation from generated registries. All content originates from .doc.mjs files colocated with source code — there is no separate CMS or markdown content directory.


Quick Reference

What Where
Docsite app apps/docsite/
Data generation script apps/docsite/scripts/generate-data.mjs
Generated registries apps/docsite/src/generated/ (gitignored, built at dev/build time)
Component docs source packages/core/src/<Component>/<Name>.doc.mjs
Reference docs source packages/cli/docs/<topic>.doc.mjs
Example blocks source packages/cli/templates/blocks/components/<Name>/
Page templates source packages/cli/templates/pages/<slug>/
Doc type definitions packages/core/src/docs-types.ts
Shell / sidebar nav apps/docsite/src/components/DocsShell.tsx
Deployment Vercel (auto-deploy on push to main)

Page Types

1. Home / Overview

URL: /

What it shows: Hero section, package grid (libraries + themes), and foundations card grid.

Data source: packageRegistry, componentRegistry (count), docsRegistry (foundations topics), themeRegistry (live theme previews).

Code: apps/docsite/src/app/(docs)/page.tsx


2. Component Detail

URL pattern: /components/[name]

What it shows: Two tabs — Overview (showcase preview, usage description, import path, best practices, examples) and Playground (interactive preview with live prop controls).

Data source:

  • Component metadata → componentRegistry (from packages/core/src/<Dir>/<Name>.doc.mjs)
  • Showcase preview → showcaseRegistry (from packages/cli/templates/blocks/components/<Name>/<Name>Showcase.tsx)
  • Example blocks → exampleRegistry (from non-showcase .doc.mjs + .tsx pairs in the blocks directory)
  • Package info → packageRegistry

Code:

  • Route: apps/docsite/src/app/(docs)/components/[name]/page.tsx
  • Client component: apps/docsite/src/components/component-detail/ComponentDetailClient.tsx
  • Sub-components: apps/docsite/src/components/component-detail/ (PropsTable, BestPractices, ExampleBlock, ShowcasePreview, InteractivePreview, HookSignature, PlaygroundPropsTable)

3. Package Page

URL pattern: /packages/[name]

What it shows: Adapts based on package type:

  • Component package (@xds/core): heading + grouped component grid with preview cards
  • Theme package (@xds/theme-*): live theme preview with install steps
  • Other (@xds/cli): README rendered as markdown

Data source: packageRegistry, groupedComponentRegistry, themeRegistry

Code: apps/docsite/src/app/(docs)/packages/[name]/page.tsx


4. Doc Topic (Guides & Foundations)

URL pattern: /docs/[topic]

What it shows: Long-form reference documentation. Two rendering modes:

  • Token topics (color, elevation, motion, shape, spacing, typography, tokens): rendered via TokensDocView with live token tables
  • Other docs (principles, styling, theme, getting-started): rendered via ReferenceDocView with prose/code/table/list blocks

Data source: docsRegistry (from packages/cli/docs/<topic>.doc.mjs)

Code:

  • Route: apps/docsite/src/app/(docs)/docs/[topic]/page.tsx
  • Token view: apps/docsite/src/components/docs/TokensDocView.tsx
  • Reference view: apps/docsite/src/components/docs/ReferenceDocView.tsx
  • Block renderer: apps/docsite/src/components/docs/ContentBlockRenderer.tsx

5. Changelog

URL: /changelog

What it shows: Aggregated changelogs from all non-private packages, with component name cross-referencing for linking.

Data source: packageRegistry (the changelog field, read from packages/<name>/CHANGELOG.md), componentRegistry (component names for linking)

Code: apps/docsite/src/app/(docs)/changelog/page.tsx, apps/docsite/src/components/ChangelogView.tsx


6. Craft (Template Gallery)

URL: /craft

What it shows: Tabbed gallery of templates, themes, and component showcases with hover-overlay cards.

Data source: templateRegistry, blockRegistry (showcases), themeRegistry, packageRegistry

Code: apps/docsite/src/app/craft/page.tsx


7. Template Viewer

URL pattern: /craft/templates/[slug]

What it shows: Individual template preview (WIP — currently shows title + description).

Data source: templateRegistry

Code: apps/docsite/src/app/craft/templates/[slug]/page.tsx


8. Theme Editor

URL: /craft/theme

What it shows: Live theme customization (WIP — placeholder page).

Code: apps/docsite/src/app/craft/theme/page.tsx


9. Playground

URL: /playground

What it shows: Full-page Monaco code editor with live preview iframe. Users write TSX and see it rendered.

Data source: Component scope generated by scripts/generate-playground-types.mjs

Code: apps/docsite/src/app/playground/page.tsx, apps/docsite/src/app/playground/PlaygroundClient.tsx


Data Pipeline

How data gets generated

Source files (.doc.mjs)  →  generate-data.mjs  →  src/generated/*.ts  →  Next.js pages

The script runs automatically before dev and build (via predev / prebuild in package.json). You can also run it manually:

cd apps/docsite
node scripts/generate-data.mjs

Generated Registries

Registry File Sources Contains
packageRegistry packageRegistry.ts packages/*/package.json + README + CHANGELOG Package metadata, versions, descriptions
componentRegistry componentRegistry.ts packages/core/src/*/*.doc.mjs Full component docs: props, usage, theming, keywords
groupedComponentRegistry groupedComponentRegistry.ts Derived from componentRegistry Nav-ready grouped/sorted component lists
blockRegistry blockRegistry.ts packages/cli/templates/blocks/**/*.doc.mjs Example blocks with source code
showcaseRegistry showcaseRegistry.ts Subset of blocks where isShowcase: true Dynamic imports for showcase previews
exampleRegistry exampleRegistry.ts Subset of blocks where isShowcase: false Dynamic imports + source for example blocks
templateRegistry templateRegistry.ts packages/cli/templates/pages/*/template.doc.mjs Page template metadata
docsRegistry docsRegistry.ts packages/cli/docs/*.doc.mjs Full doc topics with sections and content blocks
themeRegistry themeRegistry.ts Theme packages installed in docsite Live theme objects for preview rendering

Package discovery

Packages are auto-discovered from the root package.json workspaces field. Only packages/* directories are included (not apps/* or internal/*). Private packages and packages not installed in the docsite's dependencies are filtered out.


Data Structures

ComponentEntry (componentRegistry)

interface ComponentEntry {
  name: string;            // "Button", "useToast"
  moduleName: string;      // "Button", "useToast"
  directory: string;       // "Button", "hooks"
  group: string | null;    // Grouping label for nav (e.g. "Button")
  description: string;     // From usage.description
  keywords: string[];      // Search terms
  hidden: boolean;         // Excluded from nav if true
  parentDoc: string | null; // Parent component for sub-components/hooks
  props: PropDoc[];        // Prop documentation
  usage: UsageDoc | null;  // Best practices, anatomy, features
  theming: ThemingDoc | null; // CSS class targets, vars
  params: HookParamDoc[] | null;  // Hook parameters (null for components)
  returns: HookReturnDoc[] | null; // Hook return (null for components)
  playground: { defaults: Record<string, unknown> } | null;
}

DocTopic (docsRegistry)

interface DocTopic {
  topic: string;           // URL slug: "tokens", "getting-started"
  title: string;           // Display title
  description: string;     // Short description
  category: string | null; // "guide" | "foundations" | null
  sections: DocSection[];  // Array of {title, content: ContentBlock[]}
}

ContentBlock types

type ContentBlock =
  | { type: 'prose'; text: string }
  | { type: 'code'; lang: string; code: string; label?: string }
  | { type: 'table'; headers: string[]; rows: string[][] }
  | { type: 'list'; style: 'ordered' | 'unordered' | 'do' | 'dont'; items: string[] }

BlockEntry (blockRegistry)

interface BlockEntry {
  dirName: string;         // File basename
  name: string;            // Display name
  description: string;
  exampleFor: string;      // Component name this example belongs to
  isShowcase: boolean;     // Hero preview vs. inline example
  aspectRatio: number;     // For card thumbnails
  componentsUsed: string[];
  category: string;        // Path like "components/Button"
  source: string;          // Raw TSX source
}

Navigation Structure

The sidebar navigation is built in DocsShell.tsx from the generated registries. Here's the mapping:

Nav Section Data Source How Items Are Derived
Home Hardcoded Single link to /
What's New Hardcoded Single link to /changelog
Guide docsRegistry Topics with category === 'guide', sorted alphabetically
Guide → Foundations docsRegistry Topics with category === 'foundations', "tokens" pinned first, rest alphabetical
Libraries packageRegistry Non-theme packages, filtered to non-private + installed in docsite
Themes packageRegistry Packages where name includes theme-, uses displayName
Components groupedComponentRegistry Grouped items from @xds/core (see grouping logic below)
Utilities groupedComponentRegistry Hooks/utilities separated from main component list

Component Grouping Logic

The generateGroupedComponentRegistry function in generate-data.mjs groups components for the nav sidebar:

  1. Utilities bucket — components with group === 'Utilities' or standalone hooks in the hooks/ directory
  2. Explicit groups — components with a group field in their .doc.mjs
  3. Directory-based groups — hooks without parentDoc that live in a non-hooks directory are grouped by directory name
  4. Parent-child groups — hooks with a parentDoc field are nested under their parent component
  5. Ungrouped — everything else appears as standalone nav items

Groups with a single item render as a plain entry. Groups with multiple items render as a collapsible section.

Adding a new nav category

To add a new doc topic category (beyond guide and foundations):

  1. Add category: 'your-category' to the .doc.mjs file in packages/cli/docs/
  2. In DocsShell.tsx, add filtering logic: const yourTopics = docTopics.filter(d => d.category === 'your-category')
  3. Add a new <SideNavSection> or nested <SideNavItem> for the section
  4. Add active-state detection for the new category

Authoring Documentation

Adding a new component doc

  1. Create packages/core/src/<ComponentDir>/<Name>.doc.mjs
  2. Export a docs object typed as ComponentDoc (see packages/core/src/docs-types.ts)
  3. Run node apps/docsite/scripts/generate-data.mjs to regenerate registries
  4. The component automatically appears in nav and at /components/<Name>

Adding a showcase preview

  1. Create packages/cli/templates/blocks/components/<Name>/<Name>Showcase.doc.mjs with isShowcase: true and exampleFor: '<Name>'
  2. Create the matching <Name>Showcase.tsx with a default export
  3. Regenerate data — the showcase appears on the component's Overview tab

Adding an example block

  1. Create packages/cli/templates/blocks/components/<Name>/<ExampleName>.doc.mjs with isShowcase: false and exampleFor: '<Name>'
  2. Create the matching .tsx file with a default export
  3. Regenerate — the example appears in the component's "Examples" section

Adding a reference doc

  1. Create packages/cli/docs/<topic>.doc.mjs
  2. Export a docs object typed as ReferenceDoc
  3. Set category: 'guide' or category: 'foundations' to control nav placement
  4. Regenerate — the topic appears in the sidebar under its category

Hiding a component from nav

Set hidden: true in the component's .doc.mjs. It won't appear in the sidebar but the page still exists at /components/<Name>.


Development

cd apps/docsite
pnpm install
pnpm dev          # Runs generate + next dev

The predev script auto-regenerates data. After changing any .doc.mjs file, restart the dev server or manually run:

node scripts/generate-data.mjs

Testing

pnpm test         # Runs data extraction tests

Tests live in apps/docsite/src/__tests__/data-extraction.test.ts and verify the generation pipeline produces valid registries.


Deployment

The docsite deploys to Vercel at xds-sandbox.vercel.app. It uses Next.js static export (generateStaticParams) for component and doc pages. The build step runs generate-data.mjs automatically via prebuild.

Clone this wiki locally