@agent-scope/site
v1.20.0
Published
Static HTML gallery generator for Scope — reads .reactscope/ output and produces a component gallery website
Readme
@agent-scope/site
Static HTML gallery generator for Scope — reads .reactscope/ output and produces a self-contained component documentation website with a gallery, analytics dashboard, per-component detail pages, and design token compliance visualization.
Table of Contents
Installation
npm install @agent-scope/siteThe package is also bundled with @agent-scope/cli. If you have the CLI installed, scope site build is the recommended way to invoke it.
Usage
Via the CLI (recommended)
# Build the site from .reactscope/ output
scope site build
# Build with custom options
scope site build \
--output ./docs/components \
--base-path /docs/ \
--title "Acme Component Library" \
--compliance .reactscope/compliance.json
# Serve locally for preview
scope site serve
scope site serve --port 8080See @agent-scope/cli for the full scope site command reference.
Programmatic API
import { buildSite } from '@agent-scope/site';
await buildSite({
inputDir: '.reactscope',
outputDir: '.reactscope/site',
basePath: '/docs/',
title: 'My Component Library',
compliancePath: '.reactscope/compliance.json',
});Output Structure
buildSite() generates a directory of static HTML files with all CSS and JavaScript embedded inline — no web server or build step needed after generation.
outputDir/ # default: .reactscope/site/
├── index.html # Gallery homepage — component grid with search
├── dashboard.html # Analytics dashboard
├── button.html # Detail page for "Button" component
├── search-page.html # Detail page for "SearchPage" component
├── complex-form.html # Detail page for "ComplexForm" component
└── ... # One file per component (PascalCase → kebab-case)File naming: Component names are slugified from PascalCase to kebab-case:
Button→button.htmlSearchPage→search-page.htmlComplexForm→complex-form.html
Total files generated: N (components) + 2 (index + dashboard)
All HTML files are fully self-contained: CSS variables, layout styles, syntax highlighting, and client-side search JavaScript are all inlined. No external assets or CDN requests are required.
Pages
Gallery (index.html)
The gallery homepage provides an overview of all components with real-time search.
Header:
- Site title
- Total component count
- Search box (filters component cards in real time, client-side)
Statistics grid:
| Stat | Description |
|------|-------------|
| Total Components | Count of all components in the manifest |
| Simple | Components classified as simple complexity |
| Complex | Components classified as complex complexity |
| Memoized | Components wrapped in React.memo |
Component card grid:
Each card shows:
- Rendered screenshot (base64 PNG from
.reactscope/renders/) if available - Component name
- Prop count
- Complexity badge (
simple/complex) - Hook count
Cards link to the corresponding detail page. The grid uses CSS auto-fill / minmax(280px) for responsive layout.
Dashboard (dashboard.html)
The analytics dashboard provides a library-wide view of component health and design token compliance.
Key metrics grid:
- Total components
- Average props per component
- Components with screenshots
- Overall design token compliance percentage
Complexity breakdown:
- Simple / complex counts and percentage
Top components by prop count: A ranked table (top 10) with: component name (linked to detail page), prop count, complexity badge.
Design token compliance section (requires --compliance / compliancePath):
- Overall compliance percentage with visual progress bar
- Per-component compliance scores
- On-system and off-system property counts per component
Component Detail Pages
Each component gets its own page (<slug>.html) with ten expandable sections.
Playground
- Props reference table: prop name, type (syntax-highlighted), required/optional, default value, possible enum values
- Rendered preview: embedded base64 PNG screenshot, or "not generated" if no render exists
Matrix
Grid of renders across prop axis combinations (from scope render matrix):
- Each cell shows the rendered output or an error message
- Cell labels show the axis values for that combination
- Grid column count adapts to the number of cells
Docs
Placeholder for component documentation. Currently displays "No documentation file found."
Analysis
Statistics grid:
- Complexity class badge
- Prop count
- Hook count
- Side effect count
- Export type (named / default)
- Memoized (Yes / No)
- Forwarded ref (Yes / No)
Analysis grid (2 columns):
- Detected hooks (tagged list:
useState,useEffect, etc.) - Required contexts (tagged list)
- HOC wrappers (tagged list)
- Side effects (tagged list: fetch URLs, timers, subscriptions, global listeners)
X-Ray
Collapsible DOM tree from the rendered component:
- First 2 levels open by default, deeper levels collapsed
- Syntax-highlighted element attributes and values
- Total element count shown in header
Optional computed styles table:
- CSS selector (element path)
- CSS property name
- Computed value
Tokens
Design token compliance audit (requires --compliance / compliancePath):
- Compliance percentage with visual bar
- Per-property audit table:
| Column | Description |
|--------|-------------|
| Property | CSS property name (monospace) |
| Value | Computed value (with color swatch for hex values) |
| Status | ✓ on-system (token.path) or ✗ off-system |
| Nearest | Closest on-system token (when off-system) |
Accessibility
WCAG accessibility audit (from scope render data):
- Component ARIA role badge
- Accessible name
- Violation list (red-highlighted) or success confirmation
Composition
Component dependency graph:
- Left column: components this component composes (children)
- Right column: components that compose this component (parents)
- All entries link to their respective detail pages
Responsive
Multi-viewport renders. Placeholder: "Not generated."
Stress Tests
Edge case and stress test renders. Placeholder: "Not generated."
Configuration Options
SiteOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| inputDir | string | ".reactscope" | Directory containing manifest and renders |
| outputDir | string | ".reactscope/site" | Output directory for generated HTML files |
| basePath | string | "/" | Base URL path prefix for subdirectory deployment |
| compliancePath | string | — | Path to compliance batch report JSON (optional) |
| title | string | "Scope — Component Gallery" | Site title shown in header and <title> |
basePath for subdirectory deployment
When deploying to a path other than the domain root, set basePath so that inter-page links resolve correctly:
await buildSite({
basePath: '/docs/components/',
// ...
});This prefixes all href attributes in the generated HTML with the given path.
Architecture
The package is a zero-dependency static site generator organized into focused modules:
src/
├── index.ts # Public API: exports buildSite()
├── builder.ts # Main orchestrator: read → render → write
├── reader.ts # Input reading: manifest.json, renders/, compliance
├── types.ts # TypeScript interfaces (ManifestData, RenderFileData, etc.)
├── css.ts # Inline CSS string (design tokens, layout, components)
├── utils.ts # escapeHtml(), slugify(), syntaxHighlightJSX(), renderDOMTree()
└── templates/
├── layout.ts # HTML shell, top nav, sidebar, "On This Page" nav
├── component-index.ts # Gallery homepage template
├── component-detail.ts # Per-component detail page template
└── dashboard.ts # Analytics dashboard templateData flow
.reactscope/manifest.json ─┐
.reactscope/renders/*.json ├─→ reader.ts → builder.ts → templates/ → *.html
.reactscope/compliance.json (opt.) ─┘reader.tsreads the manifest JSON, optionally scans therenders/directory for per-component JSON files, and optionally reads the compliance report.builder.tsiterates over all components, calls templates for each page, and writes HTML files tooutputDir.templates/functions receive typed data objects and return HTML strings. All templates calllayout.tsto wrap content in the shared shell (nav, sidebar, "On This Page").css.tsexports a single inline CSS string embedded in every page's<style>tag.utils.tsprovides XSS-safe HTML escaping, PascalCase-to-kebab-case slugification, JSX syntax highlighting, and recursive DOM tree rendering.
Design principles
- Zero dependencies — the package has no runtime NPM dependencies. All output is pure HTML/CSS/JS.
- Embedded assets — CSS and JavaScript are inlined into every HTML file. No separate asset directory.
- XSS safety — all user-controlled strings (component names, prop values, file paths) pass through
escapeHtml()before insertion into HTML. - Idempotent builds — running
buildSite()multiple times on the same input produces identical output. - Progressive enhancement — pages are readable without JavaScript; search filtering and collapsible sections are added via inline
<script>tags.
CSS design system
The generated HTML uses CSS custom properties for consistent styling:
--color-text: #0f0f0f;
--color-muted: #6b7280;
--color-border: #e5e7eb;
--color-bg: #ffffff;
--color-bg-subtle: #f9fafb;
--color-bg-code: #1a1a2e;
--color-accent: #2563eb;
--color-success: #16a34a;
--color-warn: #d97706;
--color-error: #dc2626;
--font-body: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;Layout: Three-column design — left sidebar (220px, component list with search), main content (flex, max 900px), right sidebar (200px, "On This Page" nav). The left sidebar collapses at 768px, the right sidebar at 1024px.
Data Formats
Input: manifest.json
interface ManifestData {
version: string;
generatedAt: string;
components: Record<string, ComponentData>;
tree: Record<string, { children: string[]; parents: string[] }>;
}
interface ComponentData {
filePath: string;
exportType: "named" | "default" | "none";
displayName: string;
props: Record<string, PropData>;
composes: string[];
composedBy: string[];
forwardedRef: boolean;
hocWrappers: string[];
memoized: boolean;
loc: { start: number; end: number };
complexityClass: "simple" | "complex";
requiredContexts: string[];
detectedHooks: string[];
sideEffects: {
fetches: string[];
timers: boolean;
subscriptions: string[];
globalListeners: boolean;
};
}
interface PropData {
type: string;
values?: string[]; // enum values
default?: string;
required: boolean;
rawType: string;
}Input: renders/<Component>.json
interface RenderFileData {
screenshot?: string; // Base64-encoded PNG
width?: number;
height?: number;
renderTimeMs?: number;
computedStyles?: Record<string, Record<string, string>>;
dom?: {
tree: DOMNodeData;
elementCount: number;
boundingBox: { x: number; y: number; width: number; height: number };
};
console?: {
errors: string[];
warnings: string[];
logs: string[];
};
accessibility?: {
role: string;
name: string;
violations: string[];
};
// Matrix render fields
cells?: MatrixCellData[];
axisLabels?: string[][];
stats?: {
total: number;
success: number;
failed: number;
avgRenderTimeMs: number;
};
}Input: compliance.json
interface ComplianceBatchData {
components: Record<string, ComplianceReportData>;
}
interface ComplianceReportData {
properties: Record<string, PropertyResultData>;
total: number;
onSystem: number;
offSystem: number;
compliance: number; // 0–1
auditedAt: string;
}
interface PropertyResultData {
property: string;
value: string;
status: "on_system" | "OFF_SYSTEM";
token?: string;
nearest?: { token: string; value: string; distance: number };
}