AI-first architecture spec

New spec supersedes all prior design docs. Phase 1 covers Zod-first
props migration for 31 components, Meta objects, Card/Avatar/NavigationMenu,
cut ContextMenu/Image/Meter, and registry scaffolding.
This commit is contained in:
Mats Bosson 2026-03-29 20:28:59 +07:00
parent 8f075f1792
commit 8dc5ab32ce
2 changed files with 3450 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,323 @@
# PettyUI AI-First Architecture Design
**Date:** 2026-03-29
**Status:** Approved
**Supersedes:** All prior specs and plans in this directory
## Vision
PettyUI is the first component library where **the AI is the intended user, not an afterthought**. The primary consumer is LLMs generating code. Human developers benefit as a side effect of good AI ergonomics. Distribution follows the proven Radix → shadcn two-layer model: headless core + copy-paste styled registry.
## Package Structure
```
packages/
core/ → Headless primitives with Zod-first props + meta
mcp/ → MCP server: discovery, validation, code generation
registry/ → Styled copy-paste components (shadcn layer for Solid)
```
No separate `schemas/` package. The Zod schema lives WITH the component because it IS the component's type system.
## Component Inventory
Organized by **AI task** — what the agent is trying to build — not UI taxonomy.
### BUILD A FORM (AI's #1 task)
| Component | Status | Notes |
|-----------|--------|-------|
| TextField | Done | |
| Checkbox | Done | |
| Switch | Done | |
| RadioGroup | Done | |
| Select | Done | |
| Combobox | Done | |
| Slider | Done | |
| NumberField | Done | |
| ToggleGroup | Done | |
| **Form** | New | Validation integration (Zod v4), field grouping, error display, aria-describedby linking |
| **DatePicker** | New | Calendar + input, range selection, locale-aware |
| **Calendar** | New | Standalone month/year grid, keyboard nav |
### BUILD NAVIGATION
| Component | Status | Notes |
|-----------|--------|-------|
| Tabs | Done | |
| Breadcrumbs | Done | |
| Link | Done | |
| DropdownMenu | Done | |
| **CommandPalette** | New | cmdk pattern — search, keyboard nav, grouped actions |
| **NavigationMenu** | New | Horizontal nav with dropdown submenus, hover intent |
### BUILD AN OVERLAY
| Component | Status | Notes |
|-----------|--------|-------|
| Dialog | Done | |
| AlertDialog | Done | |
| Drawer | Done | |
| Popover | Done | |
| Tooltip | Done | |
| HoverCard | Done | |
| Toast | Done | |
### BUILD A DASHBOARD / DATA VIEW
| Component | Status | Notes |
|-----------|--------|-------|
| Progress | Done | |
| Badge | Done | |
| Skeleton | Done | |
| Alert | Done | |
| **DataTable** | New | Sorting, filtering, pagination, column resize, row selection |
| **VirtualList** | New | Virtualized rendering for large datasets, variable row heights |
| **Avatar** | New | Image + fallback initials, status indicator |
### BUILD LAYOUT & STRUCTURE
| Component | Status | Notes |
|-----------|--------|-------|
| Accordion | Done | |
| Collapsible | Done | |
| **Card** | New | Header/Content/Footer compound, token contract |
| **Wizard/Stepper** | New | Multi-step flows, step validation, linear/non-linear |
### BUILD INTERACTIONS
| Component | Status | Notes |
|-----------|--------|-------|
| Button | Done | |
| Toggle | Done | |
### CUT (remove from exports, keep internally if needed)
| Component | Reason |
|-----------|--------|
| ContextMenu | Niche desktop pattern |
| Image | `<img loading="lazy">` covers it |
| Meter | Nobody uses it vs Progress |
| Separator | Keep as menu sub-component only, drop standalone export |
| Pagination | Keep as DataTable internal, drop standalone export |
| ColorPicker suite | Design-tool-only, <5% of projects |
| Menubar | Desktop OS pattern |
| TimeField | Extremely specialized |
| Rating | Trivial, single-purpose |
| FileField | Browser native + dropzone libs |
| SegmentedControl | ToggleGroup covers this |
### Totals
| Category | Done | Cut | New | Final Total |
|----------|------|-----|-----|-------------|
| Components | 28 | -3 | +10 | 35 |
| Standalone exports | 28 | -5 | +10 | 33 |
| Primitives | 5 | 0 | +1 (createVirtualizer) | 6 |
| Utilities | 6 | 0 | 0 | 6 |
## Schema Architecture: Zod-First, Single Source of Truth
Every component's props are defined AS Zod schemas. TypeScript types are derived FROM the schemas. No duplication, no drift.
### Per-Component Pattern
```typescript
// components/dialog/dialog.props.ts
import { z } from "zod/v4";
export const DialogRootProps = z.object({
open: z.boolean().optional()
.describe("Controlled open state"),
defaultOpen: z.boolean().optional()
.describe("Initial open state (uncontrolled)"),
onOpenChange: z.function().args(z.boolean()).returns(z.void()).optional()
.describe("Called when open state changes"),
modal: z.boolean().default(true)
.describe("Whether to trap focus and add backdrop"),
});
// TypeScript types DERIVED from schema
export type DialogRootProps = z.infer<typeof DialogRootProps>;
// Metadata — just enough for MCP discovery
export const DialogMeta = {
name: "Dialog",
description: "Modal overlay requiring user acknowledgment",
parts: ["Root", "Trigger", "Portal", "Overlay", "Content", "Title", "Description", "Close"] as const,
requiredParts: ["Root", "Content", "Title"] as const,
} as const;
```
### What This Enables
| AI asks... | Schema provides... |
|---|---|
| "What components can build a form?" | `.describe()` strings + MCP semantic search |
| "What props does Select accept?" | Full typed contract with descriptions |
| "Is this Dialog usage valid?" | Runtime validation against schema |
| "Which parts are required for a11y?" | `requiredParts` in meta |
## MCP Server Architecture
The MCP server is the AI's interface to PettyUI. It reads Zod schemas and meta directly from `core/`.
### Tools
```
pettyui.discover → "I need to collect user input" → returns matching components with schemas
pettyui.inspect → "Tell me about Dialog" → returns full props schema + parts + required
pettyui.validate → "Is this Dialog usage correct?" → validates JSX/props against schema
pettyui.add → "Add Dialog to my project" → copies styled component from registry
pettyui.compose → "Build a settings form" → returns composed multi-component JSX
```
### pettyui.discover
```
Input: { intent: "I need a searchable dropdown" }
Output: [
{ name: "Combobox", match: 0.95, description: "..." },
{ name: "CommandPalette", match: 0.72, description: "..." },
{ name: "Select", match: 0.6, description: "..." }
]
```
Uses `.describe()` strings for semantic matching. No rigid taxonomy — descriptions ARE the search index.
### pettyui.inspect
```
Input: { component: "Dialog" }
Output: {
props: { /* Zod schema as JSON Schema */ },
parts: ["Root", "Trigger", "Portal", "Overlay", "Content", "Title", "Description", "Close"],
requiredParts: ["Root", "Content", "Title"],
example: "/* minimal valid usage */",
source: "/* full component source if requested */"
}
```
### pettyui.validate
```
Input: { component: "Dialog", jsx: "<Dialog.Root><Dialog.Content>hello</Dialog.Content></Dialog.Root>" }
Output: {
valid: false,
errors: ["Missing required part: Title. Dialog.Content must contain Dialog.Title for accessibility"],
fix: "<Dialog.Root><Dialog.Content><Dialog.Title>...</Dialog.Title>hello</Dialog.Content></Dialog.Root>"
}
```
### pettyui.add
```
Input: { component: "Dialog", style: "default" }
Output: {
files: [{ path: "src/components/ui/dialog.tsx", content: "/* styled component */" }],
dependencies: ["pettyui/dialog"]
}
```
### pettyui.compose
```
Input: { intent: "login form with email and password", components: ["Form", "TextField", "Button"] }
Output: { jsx: "/* complete composed component */", imports: ["pettyui/form", "pettyui/text-field", "pettyui/button"] }
```
### MCP Server Does NOT Do
- No styling opinions — registry's job
- No state management — components handle their own
- No routing — out of scope
- No build tooling — tsdown/vite
## Registry Layer (shadcn Model for Solid)
### What Gets Copied
`pettyui add dialog` creates `src/components/ui/dialog.tsx`:
```typescript
import { Dialog as DialogPrimitive } from "pettyui/dialog";
import { cn } from "@/lib/utils";
const DialogContent = (props) => (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay class={cn("fixed inset-0 bg-black/50 ...")} />
<DialogPrimitive.Content
class={cn("fixed left-1/2 top-1/2 ... bg-background rounded-lg shadow-lg", props.class)}
>
{props.children}
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
);
export { Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, DialogClose };
```
### Registry Principles
1. **Tailwind + CSS variables** — styles co-located, tokens via `--` variables
2. **Imports headless from core** — registry depends on `pettyui/*`, not copy-pasted behavior
3. **Pre-composed for 90% case** — DialogContent includes Portal + Overlay automatically
4. **Fully editable** — your file after copy
5. **One file per component** — AI reads one file, gets everything
### Theme Contract
```css
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--muted: 0 0% 96.1%;
--border: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
```
### CLI
```bash
pettyui init # Sets up tokens, utils, tsconfig paths
pettyui add dialog # Copies styled dialog component
pettyui add form # Copies styled form with Zod integration
pettyui diff dialog # Shows what you've changed vs upstream
```
## Build Order
```
Phase 1: Foundation Phase 2: Advanced Phase 3: AI Layer
───────────────── ────────────────── ─────────────────
Zod-first props refactor DataTable MCP server
(migrate 28 components) CommandPalette pettyui.discover
Meta objects on all DatePicker + Calendar pettyui.inspect
components Wizard/Stepper pettyui.validate
Card + Avatar (simple) Form system pettyui.add
NavigationMenu VirtualList + primitive pettyui.compose
Cut ContextMenu/Image/
Meter/Separator/Pagination
Registry scaffolding Registry: styled versions CLI wrapper
(init, tokens, utils) of all components pettyui init/add/diff
```
Phase 1 is mostly refactoring what exists. Phase 2 is the hard new work. Phase 3 is where PettyUI becomes unique.
## Key Design Principles
1. **AI is the primary user** — every API decision optimizes for LLM code generation
2. **Zod-first** — schemas ARE the type system, not a parallel description
3. **Sub-path exports**`pettyui/dialog` not `pettyui` barrel. Prevents hallucination
4. **Compound components**`Dialog.Root` + `Dialog.Content` over prop explosion
5. **Sensible defaults** — components work with zero props, accept controlled props when needed
6. **Union types over booleans**`variant: 'primary' | 'secondary'` not `isPrimary?: boolean`
7. **Consistent naming** — same patterns everywhere, reduces AI search space
8. **CSS variables for theming** — AI handles CSS vars naturally vs JS theme providers
9. **`.describe()` on every prop** — this IS the documentation
10. **Runtime validation feedback** — AI generates, validates, fixes. Feedback loop