diff --git a/.agents/context/README.md b/.agents/context/README.md new file mode 100644 index 0000000..c226b1d --- /dev/null +++ b/.agents/context/README.md @@ -0,0 +1,50 @@ +# AI Context Directory + +> Passive knowledge that AI agents can retrieve on-demand. This content is NOT automatically loaded. + +## Structure + +``` +context/ +├── research/ # Raw research materials, deep-dive documentation +│ └── *.md # Topic-specific research +├── decisions/ # Architecture Decision Records (ADRs) +│ └── NNN-*.md # Numbered decision records +└── glossary.md # Project-specific terminology +``` + +## Purpose + +This directory contains **reference materials** (passive knowledge) that agents can fetch when needed, but that should NOT be included in every context window. + +### vs Skills + +| Context (`context/`) | Skills (`skills/`) | +|---------------------|-------------------| +| **WHAT** things are | **HOW** to do things | +| Reference materials | Action-oriented instructions | +| Loaded on-demand | Triggered by user request | +| Background knowledge | Step-by-step procedures | + +### vs Workflows + +| Context (`context/`) | Workflows (`workflows/`) | +|---------------------|-------------------------| +| Understanding | Execution | +| Research & decisions | Step-by-step procedures | +| Read to learn | Follow to accomplish | + +## When to Add Here + +Add content to `context/` when: +- It's background research that informs decisions +- It's an Architecture Decision Record (ADR) +- It defines terminology or concepts +- It's too detailed for AGENTS.md but useful for deep dives + +## When NOT to Add Here + +Don't add content to `context/` when: +- It's a step-by-step procedure → use `workflows/` +- It's a reusable capability → use `skills/` +- It needs to be in every session → use `AGENTS.md` or `.cursor/rules/` diff --git a/.agents/context/decisions/001-use-oklch-colors.md b/.agents/context/decisions/001-use-oklch-colors.md new file mode 100644 index 0000000..2572b70 --- /dev/null +++ b/.agents/context/decisions/001-use-oklch-colors.md @@ -0,0 +1,52 @@ +# ADR-001: Use OKLCH Color Space for CSS Variables + +## Status + +**Accepted** - January 2025 + +## Context + +When setting up the theming system for Usage UI, we needed to choose a color space for CSS variables. Options considered: +- HSL (Hue, Saturation, Lightness) +- RGB/Hex +- OKLCH (OK Lightness, Chroma, Hue) + +## Decision + +Use **OKLCH** color space for all CSS variables. + +```css +:root { + --meter-success: oklch(0.723 0.191 142.5); + --meter-warning: oklch(0.795 0.184 86.047); + --meter-danger: oklch(0.637 0.237 25.331); +} +``` + +## Rationale + +1. **Perceptual uniformity**: OKLCH is perceptually uniform, meaning equal changes in values produce equal perceived changes in color. + +2. **shadcn/ui alignment**: shadcn/ui v4+ uses OKLCH as the default color space. Aligning with upstream reduces friction. + +3. **Better for programmatic manipulation**: Creating tints/shades and color palettes is more predictable in OKLCH. + +4. **Modern browser support**: All modern browsers support OKLCH as of 2024. + +## Consequences + +### Positive +- Consistent color perception across the theme +- Easy to create harmonious color variations +- Future-proof alignment with CSS standards + +### Negative +- Less familiar to developers used to HSL/RGB +- Requires conversion when working with design tools that don't support OKLCH +- Slightly more complex syntax + +## Notes + +When converting from other color spaces, use tools like: +- [oklch.com](https://oklch.com) +- CSS `color()` function for browser conversion diff --git a/.agents/context/decisions/002-monorepo-structure.md b/.agents/context/decisions/002-monorepo-structure.md new file mode 100644 index 0000000..c118829 --- /dev/null +++ b/.agents/context/decisions/002-monorepo-structure.md @@ -0,0 +1,70 @@ +# ADR-002: Adopt Lightweight Monorepo Structure + +## Status + +**Accepted** - January 2025 + +## Context + +Usage UI started as a single Next.js app using the Vercel Registry Starter template. As the project grew to support 20+ components, we needed to decide on the architecture: + +1. **Single App**: Keep everything in one Next.js application +2. **Monorepo**: Separate docs site and component registry into packages +3. **Multi-repo**: Separate repositories for docs and components + +## Decision + +Adopt a **lightweight monorepo** structure using pnpm workspaces and Turborepo. + +``` +usage-ui/ +├── apps/ +│ └── www/ # Documentation site +├── packages/ +│ └── ui/ # Component registry +├── tooling/ # Shared configs +├── turbo.json +└── pnpm-workspace.yaml +``` + +## Rationale + +### Why Monorepo Over Single App + +1. **Industry standard**: Magic UI, Origin UI, shadcn/ui all use monorepo structure +2. **Separation of concerns**: Docs site vs distributable components are distinct +3. **Scalability**: Easier to manage 20+ components with clear boundaries +4. **Independent deployment**: Can deploy docs without rebuilding components + +### Why Monorepo Over Multi-repo + +1. **Atomic changes**: Can update component + docs in single PR +2. **Shared tooling**: One Biome config, one TypeScript config +3. **Simpler CI/CD**: Single pipeline with Turbo caching +4. **Cross-package testing**: Can test integration between packages + +### Why pnpm + Turborepo + +1. **pnpm**: Fastest package manager, best monorepo support, disk efficient +2. **Turborepo**: Simple config, excellent caching, Vercel-native integration + +## Consequences + +### Positive +- Clear separation between distributable code and docs +- Turbo caching speeds up CI/CD significantly +- Changesets enables proper versioning +- Matches industry best practices + +### Negative +- More complex initial setup +- Steeper learning curve for contributors +- Requires understanding of workspace protocols + +## Implementation Notes + +Key files created: +- `pnpm-workspace.yaml` - Workspace definition +- `turbo.json` - Build orchestration +- `.changeset/config.json` - Version management +- `lefthook.yml` - Git hooks diff --git a/.agents/context/decisions/003-dual-primitive-strategy.md b/.agents/context/decisions/003-dual-primitive-strategy.md new file mode 100644 index 0000000..44a73e1 --- /dev/null +++ b/.agents/context/decisions/003-dual-primitive-strategy.md @@ -0,0 +1,82 @@ +# ADR-003: Dual Primitive Strategy (Radix + Base) + +## Status + +**Accepted** - January 2025 + +## Context + +When building meter components, we needed to decide how to handle accessibility primitives: + +1. **Radix-only**: All components use Radix UI primitives +2. **Base-only**: All components use plain HTML with manual ARIA +3. **Dual versions**: Provide both Radix and Base versions + +## Decision + +Implement **dual versions** for core meter components: +- `usage-meter.tsx` - Radix-based (full accessibility) +- `usage-meter-base.tsx` - Lightweight (no Radix) + +## Rationale + +### Why Not Radix-Only + +1. **Bundle size**: Some users prioritize minimal bundle size +2. **Simple use cases**: Not all meters need full keyboard nav +3. **Framework flexibility**: Some frameworks don't work well with Radix + +### Why Not Base-Only + +1. **Accessibility compliance**: Many projects require WCAG compliance +2. **Complex interactions**: Radix handles edge cases we'd miss +3. **shadcn alignment**: shadcn/ui uses Radix for all interactive components + +### Why Dual Versions + +1. **User choice**: Let users pick based on their requirements +2. **Documentation value**: Shows both accessible and minimal patterns +3. **Gradual adoption**: Users can start with base, upgrade to Radix + +## Implementation + +``` +packages/ui/src/components/registry/usage-meter/ +├── usage-meter.tsx # Radix Progress primitive +├── usage-meter-base.tsx # div with role="progressbar" +└── index.ts # Exports both +``` + +### When to Create Both Versions + +| Component Type | Radix | Base | +|----------------|-------|------| +| Core meters | ✅ | ✅ | +| Cards (use shadcn Card) | ❌ | ❌ | +| Indicators (simple display) | ❌ | ✅ | +| Charts (Tremor-based) | ❌ | ❌ | + +## Consequences + +### Positive +- Maximum flexibility for users +- Clear accessibility story +- Smaller bundle for simple use cases + +### Negative +- More code to maintain +- Must keep both versions in sync +- Documentation complexity + +## Notes + +The base version MUST include manual ARIA attributes: +```tsx +
+``` diff --git a/.agents/context/decisions/004-migration-hybrid-approach.md b/.agents/context/decisions/004-migration-hybrid-approach.md new file mode 100644 index 0000000..cdee1bf --- /dev/null +++ b/.agents/context/decisions/004-migration-hybrid-approach.md @@ -0,0 +1,104 @@ +# ADR-004: Hybrid Monorepo Migration Approach + +## Status + +**Accepted** - January 2025 + +## Context + +When migrating Usage UI from single-app to monorepo structure, we needed to choose between: + +1. **CLI Scaffold**: Use `npx shadcn@latest init` with monorepo preset to generate correct configs +2. **Manual Migration**: Manually restructure directories and create configs +3. **Hybrid Approach**: Use CLI to generate reference configs, manually migrate code + +Key considerations: +- 46+ shadcn components already installed with customizations +- Custom theme (OKLCH colors, nature palette) +- Git history preservation important for accountability +- Configuration errors are common and hard to debug + +## Decision + +Adopt a **hybrid migration approach**: + +1. Generate a reference monorepo using shadcn CLI in a temp directory +2. Use reference configs as authoritative source +3. Execute migration manually to preserve git history +4. Validate structure with automated checks + +## Rationale + +### Why Not CLI Scaffold + +- Loses all git history +- Requires manually copying 46+ components +- Custom theme would need re-implementation +- High risk of missing customizations + +### Why Not Pure Manual + +- shadcn monorepo config is complex (components.json aliases, exports) +- Easy to make subtle errors in turbo.json, tsconfig.json paths +- No authoritative reference for correct configuration +- Higher chance of "works on my machine" issues + +### Why Hybrid + +| Benefit | How It Helps | +|---------|--------------| +| Correct configs | Reference from CLI is guaranteed correct | +| Preserved history | Manual moves keep git blame | +| Validation | Can diff against reference | +| Customizations kept | Copy files, don't regenerate | + +## Implementation + +### Reference Generation + +```bash +mkdir -p /tmp/shadcn-monorepo-reference +cd /tmp/shadcn-monorepo-reference +pnpm dlx shadcn@latest init # Select "Next.js (Monorepo)" +``` + +Reference files to use: +- `turbo.json` — Build orchestration +- `pnpm-workspace.yaml` — Workspace definition +- `apps/web/components.json` — App-level shadcn config +- `packages/ui/components.json` — Package-level shadcn config +- `packages/ui/package.json` — Exports configuration + +### Migration Execution + +See [execute-monorepo-migration.md](../../workflows/execute-monorepo-migration.md) for step-by-step workflow. + +### Validation + +See [monorepo-validation skill](../../skills/monorepo-validation/SKILL.md) for automated structure checks. + +## Consequences + +### Positive + +- Git history preserved for all components +- Configs match shadcn CLI expectations exactly +- Automated validation catches drift +- Documented process is repeatable + +### Negative + +- More steps than pure CLI scaffold +- Requires understanding of both approaches +- Reference must be regenerated if shadcn CLI changes + +### Mitigations + +- Workflow documentation reduces complexity +- Validation script catches most errors +- Reference can be cached or checked into `.references/` if needed + +## Related + +- [ADR-002: Adopt Lightweight Monorepo Structure](./002-monorepo-structure.md) — Why monorepo +- [MONOREPO-MIGRATION.md](../../MONOREPO-MIGRATION.md) — Detailed migration guide diff --git a/.agents/context/glossary.md b/.agents/context/glossary.md new file mode 100644 index 0000000..bb2fa69 --- /dev/null +++ b/.agents/context/glossary.md @@ -0,0 +1,144 @@ +# Project Glossary + +> Domain-specific terminology for Usage UI. AI agents should reference this when encountering unfamiliar terms. + +--- + +## Architecture Terms + +### Monorepo +A single repository containing multiple packages/projects. Usage UI uses pnpm workspaces + Turborepo. + +### Workspace +A pnpm workspace is a package within the monorepo. We have: +- `@usage-ui/www` - Documentation site (apps/www) +- `@usage-ui/ui` - Component registry (packages/ui) + +### Turborepo +Build orchestration tool that caches builds and runs tasks in parallel across workspaces. + +### Changesets +Versioning and changelog management tool for monorepos. Creates `.changeset/*.md` files that become CHANGELOG entries. + +--- + +## shadcn/ui Terms + +### Registry +A collection of components distributed via the shadcn CLI. NOT an npm package. Components are copied into user projects. + +### Registry Item +A single distributable component in the registry, defined in `registry.json`. + +### Base shadcn Components +Components in `packages/ui/src/components/ui/`. These are upstream shadcn components. **DO NOT MODIFY**. + +### Registry Components +Components in `packages/ui/src/components/registry/`. These are YOUR custom components. + +### registry.json +The manifest file listing all distributable components. Location: `packages/ui/registry.json`. **CRITICAL FILE**. + +### components.json +Configuration for the shadcn CLI. Defines aliases, styles, and paths. + +### registryDependencies +Other registry items that must be installed alongside a component (e.g., `["card", "usage-meter"]`). + +--- + +## Styling Terms + +### OKLCH +A perceptually uniform color space used for CSS variables. Format: `oklch(lightness chroma hue)`. +```css +--meter-success: oklch(0.723 0.191 142.5); +``` + +### CSS Variables +Theme tokens defined in `globals.css`. Always use these instead of hardcoded colors. +```tsx +// ✅ Correct +className="bg-primary text-primary-foreground" + +// ❌ Wrong +className="bg-blue-500 text-white" +``` + +### cn() Utility +Class merging function from `@/lib/utils`. Combines clsx + tailwind-merge. + +### data-slot +A `data-*` attribute convention for styling hooks. Required on all components. +```tsx +
+``` + +--- + +## Component Terms + +### Radix Version +Component built on Radix UI primitives. Full accessibility (ARIA, keyboard nav, focus management). Slightly larger bundle. + +### Base Version +Component without Radix dependency. Pure HTML + manual ARIA. Smaller bundle. User adds accessibility as needed. + +### Core Meters +Fundamental meter components: `usage-meter`, `circular-meter`, `segmented-meter`, `stacked-meter`, `stepped-meter`, `gradient-meter`. + +### Compound Components +Components composed of other components (e.g., `quota-card` uses `card` + `usage-meter`). + +### Indicators +Visual status components: `usage-badge`, `threshold-indicator`, `limit-warning`, `overage-indicator`. + +--- + +## React/Next.js Terms + +### RSC (React Server Component) +Default component type in Next.js App Router. No `"use client"` directive. Cannot use hooks or browser APIs. + +### Client Component +Component with `"use client"` directive. Can use hooks, state, and browser APIs. Required for Radix components. + +### forwardRef +React pattern for passing refs through components. Required for DOM-forwarding components. + +--- + +## File Location Reference + +| Term | Location | +|------|----------| +| Base shadcn (don't modify) | `packages/ui/src/components/ui/` | +| Your components | `packages/ui/src/components/registry/` | +| Registry manifest | `packages/ui/registry.json` | +| CSS variables | `packages/ui/src/styles/globals.css` | +| Generated JSON | `apps/www/public/r/` (auto-generated, never edit) | +| Documentation | `apps/www/src/app/docs/` | + +--- + +## Command Reference + +| Term | Command | +|------|---------| +| Build all | `pnpm build` | +| Dev all | `pnpm dev` | +| Build UI only | `pnpm build --filter=@usage-ui/ui` | +| Add dep to UI | `pnpm add --filter=@usage-ui/ui` | +| Create changeset | `pnpm changeset` | + +--- + +## Abbreviations + +| Abbrev | Meaning | +|--------|---------| +| ADR | Architecture Decision Record | +| CLI | Command Line Interface | +| RSC | React Server Component | +| CVA | Class Variance Authority | +| PR | Pull Request | diff --git a/.agents/context/research/agent-skills-research-recommendations.md b/.agents/context/research/agent-skills-research-recommendations.md new file mode 100644 index 0000000..2c7d459 --- /dev/null +++ b/.agents/context/research/agent-skills-research-recommendations.md @@ -0,0 +1,107 @@ +# Claude Skills — AI Agent Review (Prioritized) +# +# Scope: Usage UI shadcn registry (usage meters, strict registry compliance, +# OKLCH theming, future monorepo migration). +# +# Format: each item includes What it does + Why add it. +# +# --------------------------------------------------------------------------- +# P0 — Highest Impact / Lowest Risk +# --------------------------------------------------------------------------- +# +# 1) Registry-Aware Component Scaffolding +# What it does: +# - Generates new components with correct patterns (Radix + Base when needed), +# data-slot, cn(), "use client" where required, proper file paths, and +# index.ts exports. +# +# Why add it: +# - Prevents inconsistent component patterns across 20+ planned components and +# reduces time-to-create for each item. +# +# 2) Registry.json Schema + Dependency Validator +# What it does: +# - Validates registry.json entries for correct schema, file paths, targets, +# type, and registryDependencies vs actual files. +# +# Why add it: +# - registry.json is critical; a single mistake breaks installs for users. +# This catches issues before build/ship. +# +# 3) Theme and Styling Compliance Auditor +# What it does: +# - Flags hardcoded colors, missing OKLCH CSS variables, relative imports, and +# missing data-slot usage. +# +# Why add it: +# - Ensures components remain themeable and copy-safe in user projects; +# prevents regressions in theming and installability. +# +# 4) Build + Install Smoke Test Skill +# What it does: +# - Runs pnpm build and an automated npx shadcn add using local registry JSON to +# confirm install success. +# +# Why add it: +# - Mirrors required quality gates and catches regressions before release. +# +# --------------------------------------------------------------------------- +# P1 — High Value / Medium Effort +# --------------------------------------------------------------------------- +# +# 5) Accessibility and ARIA Audit +# What it does: +# - Verifies ARIA roles/attributes in Base components and ensures Radix usage +# includes "use client" and correct semantics. +# +# Why add it: +# - Usage meters are UI-critical; accessibility issues create trust and +# compliance problems. +# +# 6) Docs + Demo Generator +# What it does: +# - Creates demo pages, MDX docs, and updates llms.txt/README entries based on +# component props. +# +# Why add it: +# - Documentation drives adoption; automation keeps docs consistent and removes +# a major bottleneck. +# +# 7) Component Dependency Graph Awareness +# What it does: +# - Checks dependency rules (e.g., segmented-meter depends on usage-meter) and +# flags missing registry dependencies. +# +# Why add it: +# - Prevents install/runtime failures caused by missing or incorrect +# dependency links. +# +# --------------------------------------------------------------------------- +# P2 — Strategic / Forward-Looking +# --------------------------------------------------------------------------- +# +# 8) Tremor/Recharts Wrapper Skill +# What it does: +# - Generates Tremor chart wrappers styled with shadcn variables and consistent +# props. +# +# Why add it: +# - Planned data-viz components depend on Tremor; this standardizes charts and +# reduces boilerplate. +# +# 9) Monorepo Migration and Structure Guardrail +# What it does: +# - Validates the repo layout against the target apps/www + packages/ui +# structure and required workspace configs. +# +# Why add it: +# - Prevents drift during migration and ensures tooling expectations stay +# aligned. +# +# 10) Changeset and Release Automation +# What it does: +# - Generates changesets, validates conventional commits, and checks release +# readiness. +# +# Why add it: +# - Scales cleanly as component count grows and reduces release errors. diff --git a/.agents/context/research/ai-context-management.md b/.agents/context/research/ai-context-management.md new file mode 100644 index 0000000..d8afd5c --- /dev/null +++ b/.agents/context/research/ai-context-management.md @@ -0,0 +1,143 @@ +# AI Context Management Research + +> Research findings on managing AI agent context effectively. Last updated: January 2026. + +--- + +## Executive Summary + +AI coding assistants rely on **context** to generate accurate code. Without proper configuration, these tools lack memory between sessions. The solution isn't "more context" — it's **structured, on-demand context**. + +### Key Finding + +Research from Anthropic shows that **accuracy drops from 99% to ~50%** as context window fills, even with million-token windows. + +--- + +## Industry Standards (2025-2026) + +### AGENTS.md + +The de facto universal standard with 60,000+ GitHub adoptions. Supported by: +- GitHub Copilot +- OpenAI Codex +- Google Gemini CLI +- Cursor +- Claude Code +- Devin, Aider, VS Code + +### Tool-Specific Files + +| Tool | Primary File | Directory | +|------|-------------|-----------| +| Universal | `AGENTS.md` | Root, subdirs | +| Cursor | `.cursor/rules/*.mdc` | `.cursor/rules/` | +| Claude Code | `CLAUDE.md` | Root, `.claude/` | +| Gemini CLI | `GEMINI.md` | Root | + +--- + +## Context Architecture + +### The Two-Layer Model + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ALWAYS LOADED │ +│ AGENTS.md + .cursor/rules/*.mdc (auto-attached) │ +│ Keep these SMALL (<500 lines total) │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ triggers skill +┌─────────────────────────────────────────────────────────────┐ +│ ON-DEMAND (Skills) │ +│ .agents/skills/[name]/SKILL.md + references/ │ +│ Loaded when user requests or agent determines need │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ deep research +┌─────────────────────────────────────────────────────────────┐ +│ DEEP CONTEXT (Rarely) │ +│ .agents/context/research/ + decisions/ │ +│ Loaded only for deep investigation │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Content Categories + +| Category | Purpose | Load Pattern | +|----------|---------|--------------| +| **Rules** | ALWAYS enforce | Auto-loaded every session | +| **Instructions** | HOW to do things | On-demand (skill trigger) | +| **Reference** | WHAT things are | Just-in-time retrieval | +| **Research** | Raw learnings | Rarely (deep dives only) | + +--- + +## Context Rot Prevention + +### What Is Context Rot? + +Performance degradation as context grows stale, bloated, or misaligned with current state. + +### Prevention Strategies + +| Strategy | Implementation | +|----------|----------------| +| **Keep AGENTS.md lean** | <500 lines, essential info only | +| **Use skills for modularity** | Don't dump everything in one file | +| **Just-in-time loading** | Let agents `Read` files when needed | +| **Explicit scope boundaries** | Document what to ignore | +| **Monthly review** | Prune stale context regularly | +| **Session hygiene** | Clear between unrelated tasks | + +### File Size Guidelines + +| File Type | Max Lines | Rationale | +|-----------|-----------|-----------| +| `AGENTS.md` | 500 | Loaded every session | +| `SKILL.md` | 200 | Core instructions only | +| `.cursor/rules/*.mdc` | 100 | Always applied | +| `references/*.md` | No limit | Loaded on-demand | +| `context/research/*.md` | No limit | Rarely accessed | + +--- + +## Separation Pattern + +### Instructions vs Reference + +**Instructions (SKILL.md, workflows/)** +- Action-oriented +- Step-by-step procedures +- "HOW to do X" +- Loaded when performing tasks + +**Reference (references/, context/)** +- Knowledge-oriented +- Background information +- "WHAT is X" +- Loaded when understanding is needed + +### Example Skill Structure + +``` +.agents/skills/shadcn-ui/ +├── SKILL.md # Instructions (<200 lines) +├── commands/ # Step-by-step procedures +│ └── add-component.md +└── references/ # Background knowledge + ├── api-reference.md + └── patterns.md +``` + +--- + +## Sources + +- Anthropic context window research (2024) +- AGENTS.md specification +- Cursor documentation +- Claude Code documentation +- llms.txt specification +- shadcn/ui registry patterns diff --git a/.agents/skills/monorepo-validation/SKILL.md b/.agents/skills/monorepo-validation/SKILL.md new file mode 100644 index 0000000..9a64c3b --- /dev/null +++ b/.agents/skills/monorepo-validation/SKILL.md @@ -0,0 +1,118 @@ +--- +name: monorepo-validation +description: Validate Usage UI monorepo structure, configuration, and package setup. Use when checking migration progress, debugging workspace issues, verifying structure before commits, or when "pnpm install" or "pnpm build" fails with workspace errors. +--- + +# Monorepo Validation + +Validates Usage UI monorepo structure against expected configuration. + +## Quick Validation + +Run the validation script to check all aspects: + +```bash +.agents/skills/monorepo-validation/scripts/validate-monorepo.sh +``` + +Or run individual checks manually below. + +## Validation Checks + +### 1. Directory Structure + +Expected layout: + +``` +usage-ui/ +├── apps/www/ # Docs site +├── packages/ui/ # Component registry +├── tooling/typescript/ # Shared TS config +├── .changeset/ # Version management +├── pnpm-workspace.yaml # Workspace definition +├── turbo.json # Build orchestration +└── lefthook.yml # Git hooks +``` + +Verify: + +```bash +# All required directories exist +ls -d apps/www packages/ui tooling/typescript .changeset 2>/dev/null && echo "✅ Directories OK" || echo "❌ Missing directories" +``` + +### 2. Workspace Configuration + +Check `pnpm-workspace.yaml`: + +```bash +cat pnpm-workspace.yaml +# Must contain: +# packages: +# - "apps/*" +# - "packages/*" +# - "tooling/*" +``` + +### 3. Package Names + +Verify package names match expected: + +```bash +# Should be @usage-ui/www +grep '"name"' apps/www/package.json + +# Should be @usage-ui/ui +grep '"name"' packages/ui/package.json +``` + +### 4. Workspace Links + +Test that pnpm recognizes workspaces: + +```bash +pnpm ls -r --depth 0 +# Should list both @usage-ui/www and @usage-ui/ui +``` + +### 5. TypeScript Path Resolution + +Check `apps/www/tsconfig.json` paths: + +```bash +grep -A5 '"paths"' apps/www/tsconfig.json +# Must include: +# "@usage-ui/ui": ["../../packages/ui/src"] +# "@usage-ui/ui/*": ["../../packages/ui/src/*"] +``` + +### 6. Registry Configuration + +Verify `packages/ui/registry.json`: + +```bash +cat packages/ui/registry.json | head -10 +# Must have $schema and items array +``` + +### 7. Build Output + +After `pnpm build`, verify: + +```bash +ls apps/www/public/r/*.json 2>/dev/null && echo "✅ Registry JSON generated" || echo "❌ No registry JSON" +``` + +## Common Issues + +| Symptom | Cause | Fix | +|---------|-------|-----| +| "Workspace not found" | Missing `pnpm-workspace.yaml` | Create file at root | +| "Cannot find @usage-ui/ui" | Wrong tsconfig paths | Update `apps/www/tsconfig.json` | +| "No registry JSON" | Build not run | Run `pnpm build --filter=@usage-ui/ui` | +| Filter not matching | Wrong package name | Check exact name in `package.json` | + +## References + +- [Structure Checks](references/structure-checks.md) — Detailed validation criteria +- [Package Exports](references/package-exports.md) — Required exports configuration diff --git a/.agents/skills/monorepo-validation/references/package-exports.md b/.agents/skills/monorepo-validation/references/package-exports.md new file mode 100644 index 0000000..4e7d348 --- /dev/null +++ b/.agents/skills/monorepo-validation/references/package-exports.md @@ -0,0 +1,123 @@ +# Package Exports Configuration + +How package exports work in the Usage UI monorepo. + +## @usage-ui/ui Exports + +The UI package uses the `exports` field to define what can be imported: + +```json +{ + "exports": { + "./registry.json": "./registry.json", + "./components": "./src/components/index.ts", + "./components/*": "./src/components/*/index.ts", + "./hooks/*": "./src/hooks/*.ts", + "./lib/*": "./src/lib/*.ts", + "./styles/*": "./src/styles/*" + } +} +``` + +### Export Patterns + +| Export Path | Resolves To | Example Import | +|-------------|-------------|----------------| +| `./registry.json` | `registry.json` | `import "@usage-ui/ui/registry.json"` | +| `./components` | `src/components/index.ts` | `import { Button } from "@usage-ui/ui/components"` | +| `./components/*` | `src/components/*/index.ts` | `import { UsageMeter } from "@usage-ui/ui/components/registry/usage-meter"` | +| `./lib/*` | `src/lib/*.ts` | `import { cn } from "@usage-ui/ui/lib/utils"` | +| `./styles/*` | `src/styles/*` | `@import "@usage-ui/ui/styles/globals.css"` | + +### Barrel Files + +Each component directory needs an `index.ts` for the wildcard export to work: + +``` +packages/ui/src/components/ +├── index.ts # Re-exports ui/ and registry/ +├── ui/ +│ └── index.ts # Re-exports all shadcn components +└── registry/ + ├── index.ts # Re-exports all registry components + └── usage-meter/ + └── index.ts # Exports UsageMeter +``` + +Example `packages/ui/src/components/registry/usage-meter/index.ts`: + +```ts +export { UsageMeter } from "./usage-meter" +export type { UsageMeterProps } from "./usage-meter" +``` + +## Workspace Protocol + +Internal dependencies use the `workspace:*` protocol: + +```json +// apps/www/package.json +{ + "dependencies": { + "@usage-ui/ui": "workspace:*" + } +} +``` + +This tells pnpm to: +1. Link directly to the local package (not npm) +2. Always use the current workspace version +3. Publish with actual version numbers (e.g., `^0.1.0`) + +## TypeScript Path Aliases + +Exports work at runtime. For TypeScript to understand imports at compile time, paths must be configured: + +### In apps/www/tsconfig.json + +```json +{ + "paths": { + "@usage-ui/ui": ["../../packages/ui/src"], + "@usage-ui/ui/*": ["../../packages/ui/src/*"] + } +} +``` + +### Alias vs Export Mapping + +| Import | TypeScript Resolves To | Runtime Resolves To | +|--------|------------------------|---------------------| +| `@usage-ui/ui/lib/utils` | `packages/ui/src/lib/utils.ts` | `packages/ui/src/lib/utils.ts` | +| `@usage-ui/ui/components` | `packages/ui/src/components/index.ts` | `packages/ui/src/components/index.ts` | + +## Validation Commands + +Check exports are configured: + +```bash +# Verify exports field exists +grep -A10 '"exports"' packages/ui/package.json + +# Verify barrel files exist +ls packages/ui/src/components/index.ts +ls packages/ui/src/components/ui/index.ts 2>/dev/null || echo "ui/index.ts missing" +ls packages/ui/src/components/registry/index.ts 2>/dev/null || echo "registry/index.ts missing" +``` + +Test import resolution: + +```bash +# From apps/www directory +cd apps/www +pnpm exec tsc --noEmit --traceResolution 2>&1 | grep "@usage-ui/ui" +``` + +## Common Export Issues + +| Issue | Symptom | Fix | +|-------|---------|-----| +| Missing index.ts | "Cannot find module" | Create barrel file | +| Wrong export path | Import resolves to undefined | Check exports field mapping | +| Missing tsconfig paths | TS error but runtime works | Add paths to tsconfig | +| Circular exports | Build hangs or fails | Restructure barrel files | diff --git a/.agents/skills/monorepo-validation/references/structure-checks.md b/.agents/skills/monorepo-validation/references/structure-checks.md new file mode 100644 index 0000000..51fce3b --- /dev/null +++ b/.agents/skills/monorepo-validation/references/structure-checks.md @@ -0,0 +1,202 @@ +# Monorepo Structure Checks + +Detailed validation criteria for Usage UI monorepo structure. + +## Required Files + +### Root Level + +| File | Required | Purpose | +|------|----------|---------| +| `pnpm-workspace.yaml` | ✅ | Workspace definition | +| `turbo.json` | ✅ | Build orchestration | +| `package.json` | ✅ | Root scripts | +| `tsconfig.json` | ✅ | Project references | +| `biome.json` | ✅ | Linting config | +| `lefthook.yml` | ⚠️ | Git hooks (optional but recommended) | + +### apps/www/ + +| File | Required | Purpose | +|------|----------|---------| +| `package.json` | ✅ | App dependencies | +| `tsconfig.json` | ✅ | TS config with paths | +| `components.json` | ✅ | shadcn CLI config | +| `next.config.ts` | ✅ | Next.js config | +| `postcss.config.mjs` | ✅ | PostCSS config | +| `src/` | ✅ | Source directory | +| `public/` | ✅ | Static assets | + +### packages/ui/ + +| File | Required | Purpose | +|------|----------|---------| +| `package.json` | ✅ | Package exports | +| `tsconfig.json` | ✅ | TS config | +| `components.json` | ✅ | shadcn CLI config | +| `registry.json` | ✅ | Component manifest | +| `src/components/ui/` | ✅ | Base shadcn components | +| `src/components/registry/` | ✅ | Custom meter components | +| `src/lib/utils.ts` | ✅ | Utilities (cn, etc.) | +| `src/styles/globals.css` | ✅ | CSS variables | + +### tooling/typescript/ + +| File | Required | Purpose | +|------|----------|---------| +| `package.json` | ✅ | Package identity | +| `base.json` | ✅ | Shared TS config | + +### .changeset/ + +| File | Required | Purpose | +|------|----------|---------| +| `config.json` | ✅ | Changeset config | + +## Package.json Validations + +### Root package.json + +Required scripts: + +```json +{ + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev", + "lint": "turbo run lint", + "typecheck": "turbo run typecheck" + } +} +``` + +Required devDependencies: + +- `turbo` +- `@biomejs/biome` +- `typescript` + +### apps/www/package.json + +Required fields: + +```json +{ + "name": "@usage-ui/www", + "private": true, + "dependencies": { + "@usage-ui/ui": "workspace:*" + } +} +``` + +### packages/ui/package.json + +Required fields: + +```json +{ + "name": "@usage-ui/ui", + "exports": { + "./registry.json": "./registry.json", + "./components": "./src/components/index.ts", + "./components/*": "./src/components/*/index.ts", + "./lib/*": "./src/lib/*.ts", + "./styles/*": "./src/styles/*" + } +} +``` + +## tsconfig.json Validations + +### apps/www/tsconfig.json + +Must extend shared config and define paths: + +```json +{ + "extends": "../../tooling/typescript/base.json", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"], + "@usage-ui/ui": ["../../packages/ui/src"], + "@usage-ui/ui/*": ["../../packages/ui/src/*"] + } + } +} +``` + +### packages/ui/tsconfig.json + +Must extend shared config: + +```json +{ + "extends": "../../tooling/typescript/base.json", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} +``` + +## components.json Validations + +### apps/www/components.json + +Critical aliases: + +```json +{ + "aliases": { + "utils": "@usage-ui/ui/lib/utils", + "ui": "@usage-ui/ui/components" + }, + "tailwind": { + "css": "../../packages/ui/src/styles/globals.css" + } +} +``` + +### packages/ui/components.json + +Standard aliases: + +```json +{ + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui" + } +} +``` + +## Turbo Configuration + +### turbo.json + +Required tasks: + +```json +{ + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**", "dist/**", "public/r/**"] + }, + "dev": { + "cache": false, + "persistent": true + }, + "lint": {}, + "typecheck": {} + } +} +``` + +Key points: +- `build` must include `"dependsOn": ["^build"]` for proper ordering +- `dev` must have `"cache": false` and `"persistent": true` +- `public/r/**` must be in build outputs for registry JSON diff --git a/.agents/skills/monorepo-validation/scripts/validate-monorepo.sh b/.agents/skills/monorepo-validation/scripts/validate-monorepo.sh new file mode 100755 index 0000000..c748365 --- /dev/null +++ b/.agents/skills/monorepo-validation/scripts/validate-monorepo.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# Monorepo Validation Script +# Run from repository root: .agents/skills/monorepo-validation/scripts/validate-monorepo.sh + +set -e + +echo "🔍 Validating Usage UI Monorepo Structure..." +echo "" + +ERRORS=0 +WARNINGS=0 + +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +pass() { + echo -e "${GREEN}✅ $1${NC}" +} + +fail() { + echo -e "${RED}❌ $1${NC}" + ERRORS=$((ERRORS + 1)) +} + +warn() { + echo -e "${YELLOW}⚠️ $1${NC}" + WARNINGS=$((WARNINGS + 1)) +} + +# Extract package name from package.json +# Usage: get_package_name "path/to/package.json" +get_package_name() { + grep '"name"' "$1" | head -1 | sed 's/.*"\(.*\)".*/\1/' | sed 's/.*: "//' | sed 's/".*//' +} + +# ============================================================================ +# 1. Directory Structure +# ============================================================================ +echo "📁 Checking directory structure..." + +[ -d "apps/www" ] && pass "apps/www exists" || fail "apps/www missing" +[ -d "packages/ui" ] && pass "packages/ui exists" || fail "packages/ui missing" +[ -d "tooling/typescript" ] && pass "tooling/typescript exists" || fail "tooling/typescript missing" +[ -d ".changeset" ] && pass ".changeset exists" || fail ".changeset missing" + +echo "" + +# ============================================================================ +# 2. Root Configuration Files +# ============================================================================ +echo "📄 Checking root configuration..." + +[ -f "pnpm-workspace.yaml" ] && pass "pnpm-workspace.yaml exists" || fail "pnpm-workspace.yaml missing" +[ -f "turbo.json" ] && pass "turbo.json exists" || fail "turbo.json missing" +[ -f "biome.json" ] && pass "biome.json exists" || fail "biome.json missing" +[ -f "lefthook.yml" ] && pass "lefthook.yml exists" || warn "lefthook.yml missing (optional)" + +echo "" + +# ============================================================================ +# 3. apps/www Files +# ============================================================================ +echo "📦 Checking apps/www..." + +if [ -d "apps/www" ]; then + [ -f "apps/www/package.json" ] && pass "apps/www/package.json exists" || fail "apps/www/package.json missing" + [ -f "apps/www/tsconfig.json" ] && pass "apps/www/tsconfig.json exists" || fail "apps/www/tsconfig.json missing" + [ -f "apps/www/components.json" ] && pass "apps/www/components.json exists" || fail "apps/www/components.json missing" + [ -f "apps/www/next.config.ts" ] && pass "apps/www/next.config.ts exists" || fail "apps/www/next.config.ts missing" + [ -d "apps/www/src" ] && pass "apps/www/src exists" || fail "apps/www/src missing" + [ -d "apps/www/public" ] && pass "apps/www/public exists" || fail "apps/www/public missing" +fi + +echo "" + +# ============================================================================ +# 4. packages/ui Files +# ============================================================================ +echo "📦 Checking packages/ui..." + +if [ -d "packages/ui" ]; then + [ -f "packages/ui/package.json" ] && pass "packages/ui/package.json exists" || fail "packages/ui/package.json missing" + [ -f "packages/ui/tsconfig.json" ] && pass "packages/ui/tsconfig.json exists" || fail "packages/ui/tsconfig.json missing" + [ -f "packages/ui/registry.json" ] && pass "packages/ui/registry.json exists" || fail "packages/ui/registry.json missing" + [ -f "packages/ui/components.json" ] && pass "packages/ui/components.json exists" || fail "packages/ui/components.json missing" + [ -d "packages/ui/src/components/ui" ] && pass "packages/ui/src/components/ui exists" || fail "packages/ui/src/components/ui missing" + [ -f "packages/ui/src/lib/utils.ts" ] && pass "packages/ui/src/lib/utils.ts exists" || fail "packages/ui/src/lib/utils.ts missing" +fi + +echo "" + +# ============================================================================ +# 5. Package Names +# ============================================================================ +echo "🏷️ Checking package names..." + +if [ -f "apps/www/package.json" ]; then + WWW_NAME=$(get_package_name "apps/www/package.json") + if [[ "$WWW_NAME" == "@usage-ui/www" ]]; then + pass "apps/www name is @usage-ui/www" + else + fail "apps/www name should be @usage-ui/www (got: $WWW_NAME)" + fi +fi + +if [ -f "packages/ui/package.json" ]; then + UI_NAME=$(get_package_name "packages/ui/package.json") + if [[ "$UI_NAME" == "@usage-ui/ui" ]]; then + pass "packages/ui name is @usage-ui/ui" + else + fail "packages/ui name should be @usage-ui/ui (got: $UI_NAME)" + fi +fi + +echo "" + +# ============================================================================ +# 6. Workspace Configuration +# ============================================================================ +echo "🔗 Checking workspace configuration..." + +if [ -f "pnpm-workspace.yaml" ]; then + if grep -q "apps/\*" pnpm-workspace.yaml && grep -q "packages/\*" pnpm-workspace.yaml; then + pass "pnpm-workspace.yaml includes apps/* and packages/*" + else + fail "pnpm-workspace.yaml missing required patterns" + fi +fi + +echo "" + +# ============================================================================ +# 7. TypeScript Paths +# ============================================================================ +echo "🛤️ Checking TypeScript paths..." + +if [ -f "apps/www/tsconfig.json" ]; then + if grep -q "@usage-ui/ui" apps/www/tsconfig.json; then + pass "apps/www/tsconfig.json has @usage-ui/ui paths" + else + fail "apps/www/tsconfig.json missing @usage-ui/ui paths" + fi +fi + +echo "" + +# ============================================================================ +# 8. Build Output (if exists) +# ============================================================================ +echo "🏗️ Checking build output..." + +if [ -d "apps/www/public/r" ]; then + JSON_COUNT=$(ls apps/www/public/r/*.json 2>/dev/null | wc -l | tr -d ' ') + if [ "$JSON_COUNT" -gt 0 ]; then + pass "Registry JSON files generated ($JSON_COUNT files)" + else + warn "No registry JSON files in apps/www/public/r/" + fi +else + warn "apps/www/public/r/ not found (run pnpm build)" +fi + +echo "" + +# ============================================================================ +# 9. Site Registry File (if needed) +# ============================================================================ +echo "📋 Checking site registry file..." + +if [ -f "apps/www/src/lib/registry.ts" ]; then + if grep -q "@/registry.json" apps/www/src/lib/registry.ts 2>/dev/null; then + if [ -f "apps/www/src/registry.json" ]; then + pass "apps/www/src/registry.json exists (required by registry.ts)" + else + fail "apps/www/src/registry.json missing (registry.ts imports it)" + fi + else + pass "registry.ts doesn't require @/registry.json" + fi +else + pass "No registry.ts file (check not needed)" +fi + +echo "" + +# ============================================================================ +# 10. Valid Module Exports +# ============================================================================ +echo "📤 Checking module exports..." + +if [ -f "packages/ui/src/components/registry/index.ts" ]; then + if grep -q "export" packages/ui/src/components/registry/index.ts; then + pass "packages/ui/src/components/registry/index.ts has exports" + else + fail "packages/ui/src/components/registry/index.ts missing exports (add 'export {}')" + fi +else + warn "packages/ui/src/components/registry/index.ts not found" +fi + +echo "" + +# ============================================================================ +# 11. Core shadcn Dependencies +# ============================================================================ +echo "📦 Checking shadcn dependencies..." + +if [ -f "packages/ui/package.json" ]; then + for dep in "class-variance-authority" "clsx" "tailwind-merge" "radix-ui"; do + if grep -q "\"$dep\"" packages/ui/package.json; then + pass "$dep in packages/ui" + else + fail "$dep missing in packages/ui/package.json" + fi + done +fi + +echo "" + +# ============================================================================ +# Summary +# ============================================================================ +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then + echo -e "${GREEN}✅ All checks passed!${NC}" +elif [ $ERRORS -eq 0 ]; then + echo -e "${YELLOW}⚠️ $WARNINGS warning(s), 0 errors${NC}" +else + echo -e "${RED}❌ $ERRORS error(s), $WARNINGS warning(s)${NC}" +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +exit $ERRORS diff --git a/.agents/skills/shadcn-ui/SKILL.md b/.agents/skills/shadcn-ui/SKILL.md new file mode 100644 index 0000000..04377ff --- /dev/null +++ b/.agents/skills/shadcn-ui/SKILL.md @@ -0,0 +1,1616 @@ +--- +name: shadcn-ui +description: Complete shadcn/ui component library guide including installation, configuration, and implementation of accessible React components. Use when setting up shadcn/ui, installing components, building forms with React Hook Form and Zod, customizing themes with Tailwind CSS, or implementing UI patterns like buttons, dialogs, dropdowns, tables, and complex form layouts. +language: typescript,tsx +framework: react,nextjs,tailwindcss +license: MIT +allowed-tools: Read, Write, Bash, Edit, Glob +--- + +# shadcn/ui Component Patterns + +Expert guide for building accessible, customizable UI components with shadcn/ui, Radix UI, and Tailwind CSS. + +## Table of Contents + +- [When to Use](#when-to-use) +- [Quick Start](#quick-start) +- [Installation & Setup](#installation--setup) +- [Project Configuration](#project-configuration) +- [Core Components](#core-components) + - [Button](#button-component) + - [Input & Form Fields](#input--form-fields) + - [Forms with Validation](#forms-with-validation) + - [Card](#card-component) + - [Dialog (Modal)](#dialog-modal-component) + - [Select (Dropdown)](#select-dropdown-component) + - [Sheet (Slide-over)](#sheet-slide-over-component) + - [Menubar & Navigation](#menubar--navigation) + - [Table](#table-component) + - [Toast Notifications](#toast-notifications) +- [Advanced Patterns](#advanced-patterns) +- [Customization](#customization) +- [Next.js Integration](#nextjs-integration) +- [Best Practices](#best-practices) +- [Common Component Combinations](#common-component-combinations) + +## When to Use + +- Setting up a new project with shadcn/ui +- Installing or configuring individual components +- Building forms with React Hook Form and Zod validation +- Creating accessible UI components (buttons, dialogs, dropdowns, sheets) +- Customizing component styling with Tailwind CSS +- Implementing design systems with shadcn/ui +- Building Next.js applications with TypeScript +- Creating complex layouts and data displays + +## Quick Start + +For new projects, use the automated setup: + +```bash +# Create Next.js project with shadcn/ui +npx create-next-app@latest my-app --typescript --tailwind --eslint --app +cd my-app +npx shadcn@latest init + +# Install essential components +npx shadcn@latest add button input form card dialog select +``` + +For existing projects: + +```bash +# Install dependencies +npm install tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react + +# Initialize shadcn/ui +npx shadcn@latest init +``` + +## What is shadcn/ui? + +shadcn/ui is **not** a traditional component library or npm package. Instead: + +- It's a **collection of reusable components** that you can copy into your project +- Components are **yours to customize** - you own the code +- Built with **Radix UI** primitives for accessibility +- Styled with **Tailwind CSS** utilities +- Includes CLI tool for easy component installation + +## Installation & Setup + +### Initial Setup + +```bash +# Initialize shadcn/ui in your project +npx shadcn@latest init +``` + +During setup, you'll configure: +- TypeScript or JavaScript +- Style (Default, New York, etc.) +- Base color theme +- CSS variables or Tailwind CSS classes +- Component installation path + +### Installing Individual Components + +```bash +# Install a single component +npx shadcn@latest add button + +# Install multiple components +npx shadcn@latest add button input form + +# Install all components +npx shadcn@latest add --all +``` + +### Manual Installation + +If you prefer manual setup: + +```bash +# Install dependencies for a specific component +npm install @radix-ui/react-slot + +# Copy component code from ui.shadcn.com +# Place in src/components/ui/ +``` + +## Project Configuration + +### Required Dependencies + +```json +{ + "dependencies": { + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "lucide-react": "^0.294.0", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7" + } +} +``` + +### TSConfig Configuration + +```json +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "es6"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "baseUrl": ".", + "paths": { + "@/components/*": ["./src/components/*"], + "@/lib/*": ["./src/lib/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} +``` + +### Tailwind Configuration + +```js +// tailwind.config.js +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} +``` + +### CSS Variables (globals.css) + +```css +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} +``` + +## Core Components + +### Button Component + +Installation: + +```bash +npx shadcn@latest add button +``` + +Basic usage: + +```tsx +import { Button } from "@/components/ui/button"; + +export function ButtonDemo() { + return ; +} +``` + +Button variants: + +```tsx +import { Button } from "@/components/ui/button"; + +export function ButtonVariants() { + return ( +
+ + + + + + +
+ ); +} +``` + +Button sizes: + +```tsx +
+ + + + +
+``` + +With loading state: + +```tsx +import { Button } from "@/components/ui/button"; +import { Loader2 } from "lucide-react"; + +export function ButtonLoading() { + return ( + + ); +} +``` + +### Input & Form Fields + +#### Input Component + +Installation: + +```bash +npx shadcn@latest add input +``` + +Basic input: + +```tsx +import { Input } from "@/components/ui/input"; + +export function InputDemo() { + return ; +} +``` + +Input with label: + +```tsx +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export function InputWithLabel() { + return ( +
+ + +
+ ); +} +``` + +Input with button: + +```tsx +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; + +export function InputWithButton() { + return ( +
+ + +
+ ); +} +``` + +### Forms with Validation + +Installation: + +```bash +npx shadcn@latest add form +``` + +This installs React Hook Form, Zod, and form components. + +Complete form example: + +```tsx +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import * as z from "zod" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { toast } from "@/components/ui/use-toast" + +const formSchema = z.object({ + username: z.string().min(2, { + message: "Username must be at least 2 characters.", + }), + email: z.string().email({ + message: "Please enter a valid email address.", + }), +}) + +export function ProfileForm() { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "", + email: "", + }, + }) + + function onSubmit(values: z.infer) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(values, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + + ( + + Email + + + + + + )} + /> + + + + + ) +} +``` + +### Card Component + +Installation: + +```bash +npx shadcn@latest add card +``` + +Basic card: + +```tsx +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" + +export function CardDemo() { + return ( + + + Card Title + Card Description + + +

Card Content

+
+ +

Card Footer

+
+
+ ) +} +``` + +Card with form: + +```tsx +import { Button } from "@/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" + +export function CardWithForm() { + return ( + + + Create project + Deploy your new project in one-click. + + +
+
+
+ + +
+
+
+
+ + + + +
+ ) +} +``` + +### Dialog (Modal) Component + +Installation: + +```bash +npx shadcn@latest add dialog +``` + +Basic dialog: + +```tsx +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" + +export function DialogDemo() { + return ( + + + + + + + Edit profile + + Make changes to your profile here. Click save when you're done. + + +
+
+ + +
+
+ + + +
+
+ ) +} +``` + +### Sheet (Slide-over) Component + +Installation: + +```bash +npx shadcn@latest add sheet +``` + +Basic sheet: + +```tsx +import { Button } from "@/components/ui/button" +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet" + +export function SheetDemo() { + return ( + + + + + + + Edit profile + + Make changes to your profile here. Click save when you're done. + + +
+
+ + +
+
+ + +
+
+
+
+ ) +} +``` + +Sheet with side placement: + +```tsx + + + + + + + Settings + + Configure your application settings here. + + + {/* Settings content */} + + +``` + +### Menubar & Navigation + +#### Menubar Component + +Installation: + +```bash +npx shadcn@latest add menubar +``` + +Basic menubar: + +```tsx +import { + Menubar, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarSeparator, + MenubarShortcut, + MenubarSub, + MenubarSubContent, + MenubarSubTrigger, + MenubarTrigger, +} from "@/components/ui/menubar" + +export function MenubarDemo() { + return ( + + + File + + + New Tab ⌘T + + + New Window ⌘N + + + Share + + Print + + + + Edit + + + Undo ⌘Z + + + Redo ⌘Y + + + + Find + + Search the web + Find... + Find Next + Find Previous + + + + + + ) +} +``` + +### Select (Dropdown) Component + +Installation: + +```bash +npx shadcn@latest add select +``` + +Basic select: + +```tsx +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" + +export function SelectDemo() { + return ( + + ) +} +``` + +Select in form: + +```tsx + ( + + Role + + + + )} +/> +``` + +### Toast Notifications + +Installation: + +```bash +npx shadcn@latest add toast +``` + +Setup toast provider in root layout: + +```tsx +import { Toaster } from "@/components/ui/toaster" + +export default function RootLayout({ children }) { + return ( + + + {children} + + + + ) +} +``` + +Using toast: + +```tsx +import { useToast } from "@/components/ui/use-toast" +import { Button } from "@/components/ui/button" + +export function ToastDemo() { + const { toast } = useToast() + + return ( + + ) +} +``` + +Toast variants: + +```tsx +// Success +toast({ + title: "Success", + description: "Your changes have been saved.", +}) + +// Error +toast({ + variant: "destructive", + title: "Error", + description: "Something went wrong.", +}) + +// With action +toast({ + title: "Uh oh! Something went wrong.", + description: "There was a problem with your request.", + action: Try again, +}) +``` + +### Table Component + +Installation: + +```bash +npx shadcn@latest add table +``` + +Basic table: + +```tsx +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" + +const invoices = [ + { invoice: "INV001", status: "Paid", method: "Credit Card", amount: "$250.00" }, + { invoice: "INV002", status: "Pending", method: "PayPal", amount: "$150.00" }, +] + +export function TableDemo() { + return ( + + A list of your recent invoices. + + + Invoice + Status + Method + Amount + + + + {invoices.map((invoice) => ( + + {invoice.invoice} + {invoice.status} + {invoice.method} + {invoice.amount} + + ))} + +
+ ) +} +``` + +## Customization + +### Theming with CSS Variables + +shadcn/ui uses CSS variables for theming. Configure in `globals.css`: + +```css +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + /* ... other dark mode variables */ + } +} +``` + +### Customizing Components + +Since you own the code, customize directly: + +```tsx +// components/ui/button.tsx +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent", + // Add custom variant + custom: "bg-gradient-to-r from-purple-500 to-pink-500 text-white", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + // Add custom size + xl: "h-14 rounded-md px-10 text-lg", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } +``` + +## Next.js Integration + +### App Router Setup + +For Next.js 13+ with App Router, ensure components use `"use client"` directive: + +```tsx +// src/components/ui/button.tsx +"use client" + +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" + +// ... rest of component +``` + +### Layout Integration + +Add the Toaster to your root layout: + +```tsx +// app/layout.tsx +import { Toaster } from "@/components/ui/toaster" +import "./globals.css" + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + + ) +} +``` + +### Server Components + +When using shadcn/ui components in Server Components, wrap them in a Client Component: + +```tsx +// app/dashboard/page.tsx +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { ButtonClient } from "@/components/ui/button-client" + +export default function DashboardPage() { + return ( +
+ + + Dashboard + + + Interactive Button + + +
+ ) +} +``` + +```tsx +// src/components/ui/button-client.tsx +"use client" + +import { Button } from "./button" + +export function ButtonClient(props: React.ComponentProps) { + return