PettyUI/docs/superpowers/specs/2026-03-29-pettyui-ai-first-architecture.md
Mats Bosson 8dc5ab32ce 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.
2026-03-29 20:28:59 +07:00

11 KiB

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

// 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:

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

: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

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 exportspettyui/dialog not pettyui barrel. Prevents hallucination
  4. Compound componentsDialog.Root + Dialog.Content over prop explosion
  5. Sensible defaults — components work with zero props, accept controlled props when needed
  6. Union types over booleansvariant: '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