}
diff --git a/apps/registry/content/pages/docs.mdx b/apps/registry/content/pages/docs.mdx
index 9a22024c..0c6c2054 100644
--- a/apps/registry/content/pages/docs.mdx
+++ b/apps/registry/content/pages/docs.mdx
@@ -10,17 +10,17 @@ og:
# Getting Started
-Welcome to VLLNT UI. This is a component registry built with shadcn/ui.
+Welcome to VLLNT UI. This is an accessible React component registry built with Radix primitives, Tailwind CSS, CVA, and the shadcn CLI registry format.
-## Installation
+## Quick install
-Install components using the shadcn CLI:
+Install any component with the shadcn CLI:
```bash
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/[component-name].json
```
-## Usage
+## Package usage
Import components from `@vllnt/ui`:
@@ -31,3 +31,10 @@ export function MyComponent() {
return
}
```
+
+## Machine-readable surfaces
+
+- `/r/registry.json` lists every registry item.
+- `/r/.json` returns the shadcn-compatible descriptor for one component.
+- `/sitemap.xml` exposes all public pages and registry endpoints.
+- `/llms.txt` and `/llms-full.txt` provide compact and full agent context.
diff --git a/apps/registry/content/pages/docs/agents.mdx b/apps/registry/content/pages/docs/agents.mdx
new file mode 100644
index 00000000..b9570f8e
--- /dev/null
+++ b/apps/registry/content/pages/docs/agents.mdx
@@ -0,0 +1,45 @@
+---
+title: Agents
+description: Use llms.txt, llms-full.txt, MCP, and registry JSON from AI coding agents.
+type: docs
+og:
+ title: Agents
+ description: AI-agent consumption guide for VLLNT UI
+ type: docs
+---
+
+# Agents
+
+VLLNT UI exposes agent-readable surfaces so coding agents can discover components, inspect install contracts, and cite current documentation without scraping the rendered site.
+
+## llms.txt
+
+Use `/llms.txt` for a compact overview of the registry. It includes install guidance, core docs links, and component links grouped by category.
+
+```text
+https://ui.vllnt.ai/llms.txt
+```
+
+## llms-full.txt
+
+Use `/llms-full.txt` when the agent needs one larger text context. It includes the main docs pages and component metadata summaries.
+
+```text
+https://ui.vllnt.ai/llms-full.txt
+```
+
+## Registry JSON
+
+Use `/r/registry.json` for discovery and `/r/.json` for exact install descriptors. JSON is the most reliable source for file paths, dependencies, and registry dependencies.
+
+## MCP
+
+The `/mcp` route is reserved for agent workflows that need a structured protocol endpoint. Treat registry JSON as the canonical install contract and MCP as an integration surface.
+
+## Safe agent workflow
+
+- Fetch `/llms.txt` for orientation.
+- Fetch `/r/registry.json` to choose components.
+- Fetch individual `/r/.json` descriptors before editing code.
+- Prefer package source over generated registry files when contributing upstream.
+- Run the project gates before opening a PR.
diff --git a/apps/registry/content/pages/docs/changelog.mdx b/apps/registry/content/pages/docs/changelog.mdx
new file mode 100644
index 00000000..457d5f9a
--- /dev/null
+++ b/apps/registry/content/pages/docs/changelog.mdx
@@ -0,0 +1,21 @@
+---
+title: Changelog
+description: Track release notes from CHANGELOG.md and the package changelog for each published version.
+type: docs
+og:
+ title: Changelog
+ description: VLLNT UI release notes and changelog sources
+ type: docs
+---
+
+# Changelog
+
+This page includes the repository and package changelogs at build time. The published package changelog remains the source for package-level release notes, while the root changelog tracks site, registry, CI, and agent-surface changes.
+
+## Sources
+
+- Root `CHANGELOG.md`: repo-wide changes and current release target.
+- `packages/ui/CHANGELOG.md`: published `@vllnt/ui` package changes.
+- GitHub Releases: release artifacts and announcement notes.
+
+## Current generated changelog
diff --git a/apps/registry/content/pages/docs/cli.mdx b/apps/registry/content/pages/docs/cli.mdx
new file mode 100644
index 00000000..37cb484e
--- /dev/null
+++ b/apps/registry/content/pages/docs/cli.mdx
@@ -0,0 +1,60 @@
+---
+title: CLI
+description: Use the shadcn CLI with VLLNT UI registry URLs, aliases, and repeatable team workflows.
+type: docs
+og:
+ title: CLI
+ description: shadcn CLI cookbook for VLLNT UI
+ type: docs
+---
+
+# CLI
+
+The VLLNT UI registry uses the same JSON descriptor shape expected by the shadcn CLI. You can install components directly from public URLs without adding a custom package dependency.
+
+## Add one component
+
+```bash
+pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/button.json
+```
+
+The CLI reads the descriptor, installs dependencies, and writes source files into your configured component directory.
+
+## Add multiple components
+
+Install components one at a time so diffs are easy to review:
+
+```bash
+pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/dialog.json
+pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/toast.json
+pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/form.json
+```
+
+## Inspect the registry first
+
+Use the registry index when scripting:
+
+```bash
+curl https://ui.vllnt.ai/r/registry.json
+```
+
+Each item includes its slug, title, description, category, dependencies, and file list. Per-component descriptors live at:
+
+```text
+https://ui.vllnt.ai/r/.json
+```
+
+## Team workflow
+
+- Pin component additions in pull requests instead of installing during deployment.
+- Review generated files like normal application code.
+- Keep local edits close to the copied component so future updates are easy to reconcile.
+- Re-run the CLI only when you intentionally accept upstream changes.
+
+## Custom registries
+
+If you build a private registry on top of VLLNT UI, keep the same descriptor fields and stable URL shape. Consumers should be able to swap only the host:
+
+```bash
+pnpm dlx shadcn@latest add https://your-registry.example.com/r/button.json
+```
diff --git a/apps/registry/content/pages/docs/components.mdx b/apps/registry/content/pages/docs/components.mdx
new file mode 100644
index 00000000..2b0b8b4c
--- /dev/null
+++ b/apps/registry/content/pages/docs/components.mdx
@@ -0,0 +1,45 @@
+---
+title: Components
+description: Learn component anatomy, accessibility expectations, composition patterns, and test coverage.
+type: docs
+og:
+ title: Components
+ description: Component anatomy and quality expectations for VLLNT UI
+ type: docs
+---
+
+# Components
+
+VLLNT UI components are designed as accessible primitives for real application surfaces. Each component should expose predictable DOM, semantic state, and enough composition hooks to fit product workflows.
+
+## Anatomy
+
+Most components live under:
+
+```text
+packages/ui/src/components//
+```
+
+Common files include:
+
+- `.tsx` for source.
+- `.test.tsx` for Vitest coverage.
+- `.stories.tsx` for Storybook examples.
+- `.visual.tsx` for component-test screenshots when applicable.
+- `index.ts` for exports.
+
+## Composition
+
+Prefer explicit subcomponents when a component has multiple regions. Keep root elements semantic: dialogs are dialogs, lists are lists, navigation is navigation, and controls expose native button or input behavior whenever possible.
+
+## Accessibility
+
+Every interactive component should support keyboard operation, visible focus, ARIA only where native semantics are not enough, and stable labels for assistive technology. Components built on Radix primitives should preserve the primitive's accessibility contract.
+
+## Testing
+
+Tests should cover rendering, prop forwarding, key interactions, and accessibility-critical state. When a component persists state or listens to browser events, include regression tests for storage and event behavior.
+
+## Registry copies
+
+Registry files are generated from package source. Fix source first, then regenerate the registry output instead of editing `apps/registry/registry/default` by hand.
diff --git a/apps/registry/content/pages/docs/contributing.mdx b/apps/registry/content/pages/docs/contributing.mdx
new file mode 100644
index 00000000..29c26133
--- /dev/null
+++ b/apps/registry/content/pages/docs/contributing.mdx
@@ -0,0 +1,47 @@
+---
+title: Contributing
+description: Set up the repository, run the dev loop, follow story conventions, and prepare PRs.
+type: docs
+og:
+ title: Contributing
+ description: Contributor workflow for VLLNT UI
+ type: docs
+---
+
+# Contributing
+
+The repository is a pnpm and Turborepo workspace. The package source lives in `packages/ui`, and the registry/docs site lives in `apps/registry`.
+
+## Setup
+
+```bash
+pnpm install --frozen-lockfile
+```
+
+Use pnpm only. Do not use npm, yarn, or bun for workspace commands.
+
+## Local dev loop
+
+Useful commands from the repo root:
+
+```bash
+pnpm -F @vllnt/ui lint
+pnpm -F @vllnt/ui exec tsc --noEmit --project tsconfig.build.json
+pnpm build
+pnpm test:once
+pnpm check:circular
+```
+
+Run narrower tests while developing, then run the full gates before opening or updating a PR.
+
+## Stories
+
+Stories should demonstrate realistic states, not only default rendering. Include disabled, loading, empty, error, and long-content states when those states exist.
+
+## Registry output
+
+Do not hand-edit generated registry files. Update source files under `packages/ui/src/components`, then regenerate registry output through the documented scripts.
+
+## Pull requests
+
+Every PR should link an issue, keep the body aligned with the current head, and include validation evidence. Keep unrelated refactors out of feature PRs so review can focus on the issue being closed.
diff --git a/apps/registry/content/pages/docs/faq.mdx b/apps/registry/content/pages/docs/faq.mdx
new file mode 100644
index 00000000..81dbad84
--- /dev/null
+++ b/apps/registry/content/pages/docs/faq.mdx
@@ -0,0 +1,51 @@
+---
+title: FAQ
+description: Answers to common questions about licensing, peer dependencies, RSC, Tailwind, and support.
+type: docs
+og:
+ title: FAQ
+ description: Common VLLNT UI questions
+ type: docs
+---
+
+# FAQ
+
+## What license does VLLNT UI use?
+
+The project is released under the repository license. Check `LICENSE` in the repo for the canonical license text.
+
+## Is this a package or a shadcn registry?
+
+Both. You can import from `@vllnt/ui`, or you can copy component source into your app with the shadcn CLI and `/r/.json` descriptors.
+
+## What are the peer dependencies?
+
+React, React DOM, Tailwind CSS, and Radix primitives are the important runtime expectations. Individual registry descriptors list their npm dependencies.
+
+## Does it support React Server Components?
+
+Components that use hooks or browser APIs are client components. Server-safe exports can be rendered from server components, but interactive components should keep their `"use client"` directive.
+
+## Does it support Tailwind v4?
+
+The registry site uses Tailwind-compatible utility classes and semantic variables. Check component source and your app's Tailwind setup when copying components into a Tailwind v4 project.
+
+## Can I customize component source?
+
+Yes. Registry installation copies source into your app. Keep changes local, and re-run the CLI only when you intentionally reconcile upstream updates.
+
+## Are components accessible?
+
+Accessibility is a core requirement. Components use native semantics and Radix primitives where appropriate, and tests cover important roles and interactions.
+
+## How do I report a bug?
+
+Use the report link on the component page or open a GitHub issue with reproduction details, expected behavior, and environment information.
+
+## Can AI agents use the registry?
+
+Yes. Use `/llms.txt`, `/llms-full.txt`, `/r/registry.json`, and `/r/.json` for agent-friendly context.
+
+## How often is the registry updated?
+
+The registry is rebuilt during site deployment. Registry JSON includes version and generated metadata so consumers can compare changes.
diff --git a/apps/registry/content/pages/docs/installation.mdx b/apps/registry/content/pages/docs/installation.mdx
new file mode 100644
index 00000000..dfa85b1d
--- /dev/null
+++ b/apps/registry/content/pages/docs/installation.mdx
@@ -0,0 +1,65 @@
+---
+title: Installation
+description: Install VLLNT UI with pnpm, Tailwind CSS, the shadcn CLI, and your first registry component.
+type: docs
+og:
+ title: Installation
+ description: Install VLLNT UI components with the shadcn CLI
+ type: docs
+---
+
+# Installation
+
+VLLNT UI ships as both an npm package and a shadcn-compatible registry. Most teams should install individual components from the registry so the source lands in their app and can be adapted locally.
+
+## Prerequisites
+
+- Node.js 20 or newer.
+- pnpm for this repository and for examples in this documentation.
+- A React application with TypeScript.
+- Tailwind CSS configured in the application.
+
+## Install the package
+
+Use the package when you want to import prebuilt exports from `@vllnt/ui`:
+
+```bash
+pnpm add @vllnt/ui
+```
+
+Then import a component:
+
+```tsx
+import { Button } from "@vllnt/ui";
+
+export function SaveButton() {
+ return ;
+}
+```
+
+## Add a registry component
+
+Use the shadcn CLI when you want the component source copied into your project:
+
+```bash
+pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/button.json
+```
+
+Replace `button` with any component slug from the [component index](/components).
+
+## Tailwind setup
+
+Make sure your app imports the shared CSS variables and scans the files where copied components live. The exact Tailwind setup depends on your app, but the minimum shape is:
+
+```css
+@import "tailwindcss";
+```
+
+Keep your component directory inside Tailwind content scanning, and keep the registry-generated utility classes intact unless you know the rendered states no longer need them.
+
+## First component checklist
+
+- Install one small component, such as `button` or `badge`.
+- Import it from the copied local path or from `@vllnt/ui`, depending on your chosen install mode.
+- Render it in a page with light and dark mode enabled.
+- Run your app lint and typecheck before adding more components.
diff --git a/apps/registry/content/pages/docs/registry.mdx b/apps/registry/content/pages/docs/registry.mdx
new file mode 100644
index 00000000..6b2aa5a2
--- /dev/null
+++ b/apps/registry/content/pages/docs/registry.mdx
@@ -0,0 +1,45 @@
+---
+title: Registry
+description: Understand the /r/registry.json schema and how to build a compatible registry surface.
+type: docs
+og:
+ title: Registry
+ description: VLLNT UI registry schema and extension guide
+ type: docs
+---
+
+# Registry
+
+The public registry is the machine-readable contract behind the component site. It exposes a top-level index and one descriptor per component.
+
+## Endpoints
+
+- `/r/registry.json` lists every component and registry-level metadata.
+- `/r/.json` returns the install descriptor for a single component.
+- `/components/` is the human documentation page for the same component.
+
+## Item shape
+
+Registry items include:
+
+- `name`: stable slug used in URLs.
+- `title`: human-readable component name.
+- `description`: short summary for search, agents, and docs.
+- `type`: registry item kind.
+- `category`: component category.
+- `files`: source files installed by the CLI.
+- `dependencies`: npm dependencies required by the component.
+- `registryDependencies`: other registry components needed at runtime.
+- `version` and `stability`: release metadata stamped during registry build.
+
+## Building on top
+
+A compatible registry should keep stable component slugs, serve JSON with cache-friendly URLs, and preserve the `files` contract expected by the shadcn CLI. If you wrap VLLNT UI in a private registry, keep your custom metadata additive so standard tooling can still read the descriptor.
+
+## Generated files
+
+The registry app generates shims and metadata from the package source. Do not hand-edit generated output. Change the source component, then run the documented registry build or shim sync script.
+
+## Agent usage
+
+Agents should prefer `/r/registry.json` for discovery and `/r/.json` for implementation details. Use `/llms-full.txt` when a single text context is easier than fetching many JSON endpoints.
diff --git a/apps/registry/content/pages/docs/theming.mdx b/apps/registry/content/pages/docs/theming.mdx
new file mode 100644
index 00000000..d16bd103
--- /dev/null
+++ b/apps/registry/content/pages/docs/theming.mdx
@@ -0,0 +1,61 @@
+---
+title: Theming
+description: Customize design tokens, CSS variables, dark mode, and local themes for VLLNT UI.
+type: docs
+og:
+ title: Theming
+ description: Design tokens, CSS variables, and dark mode for VLLNT UI
+ type: docs
+---
+
+# Theming
+
+VLLNT UI components are styled with Tailwind utilities that read semantic CSS variables. The goal is to keep component source portable while allowing each application to define its own brand surface.
+
+## Token model
+
+Use semantic tokens for component intent:
+
+- `background` and `foreground` for page surfaces.
+- `card` and `card-foreground` for contained panels.
+- `primary`, `secondary`, `muted`, and `accent` for interaction states.
+- `border`, `input`, and `ring` for control chrome.
+- `destructive` for destructive actions.
+
+Avoid hard-coding product colors inside component internals. Prefer mapping brand color decisions into variables once.
+
+## Dark mode
+
+Use a class-based dark mode strategy so server-rendered markup and client hydration agree:
+
+```tsx
+
+ {children}
+
+```
+
+The site uses `ThemeProvider` for the registry app, but copied components only need the variables and Tailwind utilities available in their host application.
+
+## Custom themes
+
+Define theme variables at the application boundary:
+
+```css
+:root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+}
+
+.dark {
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+}
+```
+
+Keep custom themes semantic. If a component needs a new recurring intent, add a token for the intent instead of scattering one-off utility colors.
+
+## Motion and density
+
+Components should remain usable in dense product surfaces. Use motion to communicate state changes, not as decoration. Respect `prefers-reduced-motion` when adding new animation.
diff --git a/apps/registry/lib/docs-pages.ts b/apps/registry/lib/docs-pages.ts
new file mode 100644
index 00000000..57b9e570
--- /dev/null
+++ b/apps/registry/lib/docs-pages.ts
@@ -0,0 +1,70 @@
+export type DocsPage = {
+ readonly description: string;
+ readonly slug: string;
+ readonly title: string;
+};
+
+export const DOCS_PAGES: readonly DocsPage[] = [
+ {
+ description:
+ "Install VLLNT UI with pnpm, Tailwind CSS, the shadcn CLI, and your first registry component.",
+ slug: "installation",
+ title: "Installation",
+ },
+ {
+ description:
+ "Use the shadcn CLI with VLLNT UI registry URLs, aliases, and repeatable team workflows.",
+ slug: "cli",
+ title: "CLI",
+ },
+ {
+ description:
+ "Customize design tokens, CSS variables, dark mode, and local themes for VLLNT UI.",
+ slug: "theming",
+ title: "Theming",
+ },
+ {
+ description:
+ "Understand the /r/registry.json schema and how to build a compatible registry surface.",
+ slug: "registry",
+ title: "Registry",
+ },
+ {
+ description:
+ "Learn component anatomy, accessibility expectations, composition patterns, and test coverage.",
+ slug: "components",
+ title: "Components",
+ },
+ {
+ description:
+ "Use llms.txt, llms-full.txt, MCP, and registry JSON from AI coding agents.",
+ slug: "agents",
+ title: "Agents",
+ },
+ {
+ description:
+ "Set up the repository, run the dev loop, follow story conventions, and prepare PRs.",
+ slug: "contributing",
+ title: "Contributing",
+ },
+ {
+ description:
+ "Track release notes from CHANGELOG.md and the package changelog for each published version.",
+ slug: "changelog",
+ title: "Changelog",
+ },
+ {
+ description:
+ "Answers to common questions about licensing, peer dependencies, RSC, Tailwind, and support.",
+ slug: "faq",
+ title: "FAQ",
+ },
+];
+
+export function getDocsPage(slug: string): DocsPage | undefined {
+ return DOCS_PAGES.find((page) => page.slug === slug);
+}
+
+export function getDocsPath(page: DocsPage): string {
+ return `/docs/${page.slug}`;
+}
diff --git a/apps/registry/lib/jsonld.ts b/apps/registry/lib/jsonld.ts
index 2459df26..6b076358 100644
--- a/apps/registry/lib/jsonld.ts
+++ b/apps/registry/lib/jsonld.ts
@@ -10,6 +10,13 @@ type JsonLdValue =
type JsonLdNode = { readonly [key: string]: JsonLdValue };
+export type JsonLdScriptAttributes = {
+ readonly dangerouslySetInnerHTML: {
+ readonly __html: string;
+ };
+ readonly type: "application/ld+json";
+};
+
export function organizationLd(): JsonLdNode {
return {
"@context": "https://schema.org",
@@ -85,6 +92,41 @@ export function breadcrumbLd(trail: ReadonlyArray<{
};
}
+export function techArticleLd(article: {
+ readonly description: string;
+ readonly title: string;
+ readonly url: string;
+}): JsonLdNode {
+ return {
+ "@context": "https://schema.org",
+ "@type": "TechArticle",
+ headline: article.title,
+ description: article.description,
+ url: article.url,
+ author: {
+ "@type": "Organization",
+ name: "VLLNT",
+ url: SITE_URL,
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "VLLNT",
+ url: SITE_URL,
+ },
+ programmingLanguage: "TypeScript",
+ about: "React component library documentation",
+ };
+}
+
export function jsonLdScript(node: JsonLdNode | readonly JsonLdNode[]): string {
- return JSON.stringify(node);
+ return JSON.stringify(node).replaceAll("<", "\\u003c");
+}
+
+export function jsonLdScriptAttributes(
+ node: JsonLdNode | readonly JsonLdNode[],
+): JsonLdScriptAttributes {
+ return {
+ dangerouslySetInnerHTML: { __html: jsonLdScript(node) },
+ type: "application/ld+json",
+ };
}
diff --git a/apps/registry/lib/sidebar-sections.ts b/apps/registry/lib/sidebar-sections.ts
index 3113e4a8..5c0e7cfd 100644
--- a/apps/registry/lib/sidebar-sections.ts
+++ b/apps/registry/lib/sidebar-sections.ts
@@ -1,4 +1,5 @@
import registryData from "@/registry.json";
+import { DOCS_PAGES, getDocsPath } from "@/lib/docs-pages";
import type {
ComponentCategory,
Registry,
@@ -73,6 +74,18 @@ export function getSidebarSections(_activeCategory?: ComponentCategory) {
{ href: "/components", title: "Components" },
],
},
+ {
+ collapsible: true,
+ defaultOpen: true,
+ items: [
+ { href: "/docs", title: "Overview" },
+ ...DOCS_PAGES.map((page) => ({
+ href: getDocsPath(page),
+ title: page.title,
+ })),
+ ],
+ title: "Documentation",
+ },
...groupedComponents.map((group) => ({
collapsible: true,
defaultOpen: true,