@withmata/blueprints 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/.claude/skills/audit/SKILL.md +4 -4
  2. package/.claude/skills/blueprint-catalog/SKILL.md +17 -7
  3. package/.claude/skills/copywrite/SKILL.md +187 -0
  4. package/.claude/skills/copywrite-landing/SKILL.md +489 -0
  5. package/.claude/skills/design-system/SKILL.md +970 -0
  6. package/.claude/skills/new-project/SKILL.md +168 -112
  7. package/.claude/skills/scaffold-auth/SKILL.md +9 -9
  8. package/.claude/skills/scaffold-db/SKILL.md +14 -14
  9. package/.claude/skills/scaffold-env/SKILL.md +4 -4
  10. package/.claude/skills/scaffold-foundation/SKILL.md +15 -15
  11. package/.claude/skills/scaffold-tailwind/SKILL.md +17 -3
  12. package/.claude/skills/scaffold-ui/SKILL.md +155 -36
  13. package/ENGINEERING.md +2 -2
  14. package/blueprints/discovery/design-system/BLUEPRINT.md +1479 -0
  15. package/blueprints/discovery/marketing-copywriting/BLUEPRINT.md +664 -0
  16. package/blueprints/features/auth-better-auth/BLUEPRINT.md +20 -22
  17. package/blueprints/features/db-drizzle-postgres/BLUEPRINT.md +12 -12
  18. package/blueprints/features/db-drizzle-postgres/files/db/src/example-entity.ts +1 -1
  19. package/blueprints/features/db-drizzle-postgres/files/db/src/scripts/seed.ts +1 -1
  20. package/blueprints/features/env-t3/BLUEPRINT.md +1 -1
  21. package/blueprints/features/tailwind-v4/BLUEPRINT.md +9 -2
  22. package/blueprints/features/tailwind-v4/files/tailwind-config/shared-styles.css +80 -1
  23. package/blueprints/features/ui-shared-components/BLUEPRINT.md +411 -78
  24. package/blueprints/features/ui-shared-components/files/ui/components/ui/alert-dialog.tsx +192 -0
  25. package/blueprints/features/ui-shared-components/files/ui/components/ui/avatar.tsx +71 -0
  26. package/blueprints/features/ui-shared-components/files/ui/components/ui/badge.tsx +52 -0
  27. package/blueprints/features/ui-shared-components/files/ui/components/ui/breadcrumb.tsx +122 -0
  28. package/blueprints/features/ui-shared-components/files/ui/components/ui/button.tsx +56 -0
  29. package/blueprints/features/ui-shared-components/files/ui/components/ui/card-select.tsx +72 -0
  30. package/blueprints/features/ui-shared-components/files/ui/components/ui/card.tsx +100 -0
  31. package/blueprints/features/ui-shared-components/files/ui/components/ui/collapsible.tsx +34 -0
  32. package/blueprints/features/ui-shared-components/files/ui/components/ui/combobox.tsx +301 -0
  33. package/blueprints/features/ui-shared-components/files/ui/components/ui/dropdown-menu.tsx +264 -0
  34. package/blueprints/features/ui-shared-components/files/ui/components/ui/empty-state.tsx +43 -0
  35. package/blueprints/features/ui-shared-components/files/ui/components/ui/entity-select.tsx +110 -0
  36. package/blueprints/features/ui-shared-components/files/ui/components/ui/field.tsx +237 -0
  37. package/blueprints/features/ui-shared-components/files/ui/components/ui/form-field.tsx +217 -0
  38. package/blueprints/features/ui-shared-components/files/ui/components/ui/input-group.tsx +161 -0
  39. package/blueprints/features/ui-shared-components/files/ui/components/ui/input.tsx +20 -0
  40. package/blueprints/features/ui-shared-components/files/ui/components/ui/label.tsx +20 -0
  41. package/blueprints/features/ui-shared-components/files/ui/components/ui/org-switcher.tsx +114 -0
  42. package/blueprints/features/ui-shared-components/files/ui/components/ui/page-header.tsx +45 -0
  43. package/blueprints/features/ui-shared-components/files/ui/components/ui/pagination.tsx +52 -0
  44. package/blueprints/features/ui-shared-components/files/ui/components/ui/pill-select.tsx +151 -0
  45. package/blueprints/features/ui-shared-components/files/ui/components/ui/popover.tsx +41 -0
  46. package/blueprints/features/ui-shared-components/files/ui/components/ui/search-input.tsx +49 -0
  47. package/blueprints/features/ui-shared-components/files/ui/components/ui/select.tsx +205 -0
  48. package/blueprints/features/ui-shared-components/files/ui/components/ui/selected-entity-card.tsx +47 -0
  49. package/blueprints/features/ui-shared-components/files/ui/components/ui/separator.tsx +25 -0
  50. package/blueprints/features/ui-shared-components/files/ui/components/ui/sidebar.tsx +389 -0
  51. package/blueprints/features/ui-shared-components/files/ui/components/ui/status-filter.tsx +43 -0
  52. package/blueprints/features/ui-shared-components/files/ui/components/ui/tag-input.tsx +131 -0
  53. package/blueprints/features/ui-shared-components/files/ui/components/ui/textarea.tsx +18 -0
  54. package/blueprints/features/ui-shared-components/files/ui/components/ui/user-menu.tsx +149 -0
  55. package/blueprints/features/ui-shared-components/files/ui/components.json +11 -8
  56. package/blueprints/features/ui-shared-components/files/ui/package.json +20 -11
  57. package/blueprints/foundation/monorepo-turbo/BLUEPRINT.md +19 -20
  58. package/blueprints/foundation/monorepo-turbo/files/root/package.json +1 -1
  59. package/dist/index.js +241 -100
  60. package/package.json +1 -1
  61. package/blueprints/features/tailwind-v4/files/tailwind-config/package.json +0 -20
  62. package/blueprints/foundation/monorepo-turbo/files/root/pnpm-workspace.yaml +0 -5
@@ -1,29 +1,27 @@
1
- # UI Shared Components Blueprint — shadcn/ui + Monorepo
1
+ # UI Shared Components Blueprint — Base UI + shadcn/ui Component Library
2
2
 
3
3
  ## Tier
4
4
 
5
5
  Feature
6
6
 
7
- ## Status
7
+ ## Problem
8
8
 
9
- Complete.
9
+ Building a UI component library for a TypeScript project requires solving several hard problems at once: accessible headless primitives, a variant system that scales, styling that works across package boundaries, and a component authoring pattern that balances consistency with flexibility. Teams either adopt a full component library and fight its opinions, or build from scratch and lose months on accessibility and API design. This blueprint provides 31 production-ready components built on Base UI headless primitives, a compound component architecture with `data-slot` CSS targeting, CVA-driven variant systems, and full shadcn CLI compatibility for adding more components — all exported as TypeScript source with no build step.
10
10
 
11
- ## Problem
11
+ ## Status
12
12
 
13
- Sharing UI components across multiple apps in a monorepo requires careful package structure, export patterns, and styling integration. Without a standard setup, teams duplicate components, fight build tooling issues, and waste time wiring shadcn/ui to work in a workspace package. This blueprint creates a `packages/ui/` package with path-based exports, shadcn CLI compatibility, and proper Tailwind style integration.
13
+ Complete.
14
14
 
15
15
  ## Blueprint Dependencies
16
16
 
17
17
  | Blueprint | Type | Why |
18
18
  |-----------|------|-----|
19
- | `features/tailwind-v4` | required | UI components depend on design tokens and theme setup. Without it, components lack consistent styling. |
20
- | `foundation/monorepo-turbo` | recommended | Provides workspace structure, TypeScript config, and package conventions. Without it, the UI package is created as a local directory. |
19
+ | `features/tailwind-v4` | required | Components depend on design tokens, theme variables, and animation classes from the Tailwind design system. Without it, component styles break or fall back to browser defaults. |
20
+ | `foundation/monorepo-turbo` | recommended | Provides workspace structure, TypeScript config, and package conventions. Without it, components are created as a local directory with direct imports instead of workspace packages. |
21
21
 
22
22
  ### If `tailwind-v4` is not installed
23
23
 
24
- The scaffold command should ask: "UI components need the Tailwind design system. Install tailwind-v4 first? (Y/n)"
25
-
26
- If declined, scaffold with basic Tailwind defaults and warn about missing design tokens.
24
+ The scaffold command automatically installs the Tailwind design system as a prerequisite — no user prompt needed. The design system (tokens, animations, typography) is a hard requirement for component styling. The scaffold skill runs the tailwind-v4 blueprint inline before proceeding with UI scaffolding.
27
25
 
28
26
  ---
29
27
 
@@ -31,23 +29,37 @@ If declined, scaffold with basic Tailwind defaults and warn about missing design
31
29
 
32
30
  ### Core Pattern
33
31
 
34
- The UI package is a workspace package (`packages/ui/`) that exports components, utilities, styles, and icons via **path-based exports** in `package.json`. Consuming apps import directly from specific paths (`@scope/ui/components/ui/button`) rather than through barrel re-exports. This enables better tree-shaking and clearer dependency graphs.
32
+ The UI package is a workspace package (`packages/ui/`) that exports 31 base components, utilities, styles, and icons via **path-based exports** in `package.json`. Components are built on **Base UI** (`@base-ui/react`) headless primitives with **CVA** for variant management and the **compound component pattern** for complex UI elements. Every component and sub-component receives a `data-slot` attribute for CSS targeting. Consuming apps import directly from specific paths (`@scope/ui/components/ui/button`) rather than through barrel re-exports.
35
33
 
36
- Components use shadcn/ui conventions: Radix UI primitives for accessibility, `class-variance-authority` for variant management, and the `cn()` utility for class merging.
34
+ The library ships with a custom **`base-maia` shadcn style** so that `shadcn add` generates Base UI components (not Radix) that are compatible with the existing library.
37
35
 
38
36
  ### Key Decisions
39
37
 
38
+ - **Base UI as primary headless library** (required) — Base UI (`@base-ui/react`) is built by the same team that created Radix, with a modernized API. It uses `data-*` attributes for state instead of Radix's `data-[state=*]` convention, which produces cleaner CSS selectors. It provides first-class render props for polymorphism instead of the `asChild` pattern. Every new component in this blueprint uses Base UI primitives.
39
+
40
+ - **Legacy Radix for popover and collapsible** (current state) — `collapsible.tsx` and `popover.tsx` still use Radix primitives because Base UI equivalents are not yet available or stable. These components work correctly alongside Base UI components. They can be migrated to Base UI when those primitives ship.
41
+
42
+ - **`data-slot` attribute convention** (required) — Every component root and sub-component element gets `data-slot="component-name"`. This enables CSS targeting via `[data-slot=button]` selectors, which are more stable than class-based selectors that break when Tailwind classes change. It also enables parent components to style children without prop drilling.
43
+
44
+ - **Compound component pattern** (required) — Complex components export multiple named parts that consumers compose freely. For example, `Card` exports `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, and `CardFooter`. This gives consumers full control over layout and ordering while keeping each part's styling encapsulated.
45
+
46
+ - **Phosphor Icons** (recommended) — Richer icon set than Lucide with six weights per icon (regular, bold, duotone, fill, light, thin). Tree-shakeable via deep imports (`@phosphor-icons/react/ArrowRight`). Consistent API across all icons. Can be swapped to Lucide or any other library.
47
+
40
48
  - **Path-based exports** (required) — Instead of `import { Button } from "@scope/ui"`, use `import { Button } from "@scope/ui/components/ui/button"`. This avoids barrel export issues, enables per-component tree-shaking, and makes dependencies explicit. Package.json uses wildcard exports (`"./components/*": "./components/*.tsx"`).
41
49
 
42
50
  - **Subpath imports with `#` prefix** (required) — Internal imports within the UI package use Node.js subpath imports (`#components/ui/button`). This provides clean paths without TypeScript path aliases and works natively with Node.js module resolution.
43
51
 
44
- - **shadcn CLI compatibility** (required) — The `components.json` file configures shadcn to generate components in the correct locations within the UI package. Run `pnpm dlx shadcn@latest add <component>` from `packages/ui/` to add new components.
52
+ - **Blueprint-owned vs user-owned components** (required) — The 31 template components shipped with this blueprint are blueprint-owned. They can be updated during `/scaffold-ui` upgrade mode. Components added via `shadcn add` are user-owned and never touched by upgrades. This separation allows the library to evolve without overwriting user customizations.
53
+
54
+ - **`base-maia` shadcn style** (recommended) — A custom shadcn style that generates Base UI components instead of Radix. Required for `shadcn add` to produce components compatible with the existing library. Configured in `components.json`. Can be changed to `new-york` or `default` if Radix compatibility is preferred.
45
55
 
46
56
  - **No build step** (required) — The UI package exports TypeScript source directly (`.tsx` files). Consuming Next.js apps transpile via their own bundler. This eliminates a build step and enables hot module replacement for components during development.
47
57
 
48
- - **`cn()` utility** (required) — Combines `clsx` for conditional classes and `tailwind-merge` for deduplication. Every component should use `cn()` for className composition. Located at `utils/cn.ts`.
58
+ - **`cn()` utility** (required) — Combines `clsx` for conditional classes and `tailwind-merge` for deduplication. Every component uses `cn()` for className composition. Located at `utils/cn.ts`.
59
+
60
+ - **CVA for variants** (required) — `class-variance-authority` for defining component variants. Re-exported from `utils/cva.ts` for consistency. Button alone has 6 variants x 8 sizes. Badge has 6 variants. Sidebar has multiple CVA-driven sub-component styles.
49
61
 
50
- - **CVA for variants** (recommended) — `class-variance-authority` for defining component variants (size, color, style). Re-exported from `utils/cva.ts` for consistency.
62
+ - **Form system** (recommended) — `Field` (compound form field layout), `FormField` (higher-order field with hint popover system using IntersectionObserver for scroll detection and auto-show on focus), `TagInput` (controlled tag input with chips, max items, duplicate detection), and `PillSelect` (generic single-select pill buttons with search, collapse/expand, deselect) form a cohesive form building system.
51
63
 
52
64
  - **Separate `icons/` directory** (recommended) — SVG icons and brand logos in their own directory with path-based exports (`@scope/ui/icons/Logo`). Keeps component imports clean.
53
65
 
@@ -58,19 +70,91 @@ Components use shadcn/ui conventions: Radix UI primitives for accessibility, `cl
58
70
  ## Hard Requirements vs Recommended Defaults
59
71
 
60
72
  **Hard requirements:**
73
+ - Base UI as the headless primitive layer
74
+ - `data-slot` attribute on every component and sub-component
75
+ - Compound component pattern for complex components
61
76
  - Path-based exports (no barrel re-exports for components)
62
77
  - shadcn/ui CLI compatibility (`components.json`)
63
78
  - `cn()` utility using clsx + tailwind-merge
79
+ - CVA for variant management
64
80
  - TypeScript source exports (no build step)
65
81
  - Styles import chain through tailwind-config
82
+ - `#` subpath imports for internal references
66
83
 
67
84
  **Recommended defaults — ask during scaffolding:**
68
85
 
69
86
  | Choice | Recommended | Alternatives |
70
87
  |---|---|---|
71
- | shadcn style | `"new-york"` | `"default"` or any shadcn style |
72
- | Icon library | `"lucide"` | `"phosphor"`, `"radix"`, or custom |
73
- | Include CVA utility | Yes | No |
88
+ | shadcn style | `"base-maia"` | `"new-york"`, `"default"`, or any shadcn style |
89
+ | Icon library | `"phosphor"` | `"lucide"`, `"radix"`, or custom |
90
+ | Include base components | Yes (all 31) | No (skeleton only — empty `components/ui/`) |
91
+ | Include form system | Yes | No (skip `form-field`, `field`, `tag-input`, `pill-select`) |
92
+ | Which consuming apps get globals.css update | Ask during scaffolding | — |
93
+
94
+ ---
95
+
96
+ ## Components
97
+
98
+ ### Primitives (no internal UI deps)
99
+
100
+ | Component | File | Headless Primitive | Notes |
101
+ |---|---|---|---|
102
+ | Button | `button.tsx` | Base UI `ButtonPrimitive` | 6 variants (default, destructive, outline, secondary, ghost, link) x 8 sizes. CVA-driven. |
103
+ | Input | `input.tsx` | Base UI `InputPrimitive` | Standard text input with `data-slot="input"`. |
104
+ | Label | `label.tsx` | Native `<label>` | Styled label with `data-slot="label"`. |
105
+ | Textarea | `textarea.tsx` | Native `<textarea>` | Auto-styled textarea with `data-slot="textarea"`. |
106
+ | Badge | `badge.tsx` | Base UI `useRender` | 6 variants. Polymorphic via render prop — can render as links, buttons, etc. |
107
+ | Separator | `separator.tsx` | Base UI `SeparatorPrimitive` | Horizontal/vertical with `data-slot="separator"`. |
108
+
109
+ ### Compound Components
110
+
111
+ | Component | File | Headless Primitive | Exports |
112
+ |---|---|---|---|
113
+ | Card | `card.tsx` | None (semantic HTML) | 7 parts: Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter |
114
+ | Alert Dialog | `alert-dialog.tsx` | Base UI `AlertDialogPrimitive` | 12 parts including AlertDialogMedia for icons/illustrations |
115
+ | Select | `select.tsx` | Base UI `SelectPrimitive` | 10 parts with Portal/Positioner, scroll buttons |
116
+ | Combobox | `combobox.tsx` | Base UI `ComboboxPrimitive` | 16 exports with chips support, InputGroup integration |
117
+ | Dropdown Menu | `dropdown-menu.tsx` | Base UI `MenuPrimitive` | 15 exports with sub-menus, checkbox items, radio items |
118
+ | Sidebar | `sidebar.tsx` | Context-based | 14 exports with cookie persistence, CVA variants for width/behavior |
119
+ | Field | `field.tsx` | None (compound layout) | 10 parts: Field, FieldGroup, FieldSet, FieldLabel, FieldDescription, FieldError, FieldSeparator, FieldContent, FieldTitle, FieldLegend |
120
+ | Input Group | `input-group.tsx` | None (compound layout) | 6 parts: InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea |
121
+
122
+ ### Navigation/Utility
123
+
124
+ | Component | File | Headless Primitive | Notes |
125
+ |---|---|---|---|
126
+ | Breadcrumb | `breadcrumb.tsx` | Base UI `useRender` | 7 exports. Polymorphic links via render prop. |
127
+ | Collapsible | `collapsible.tsx` | Radix `CollapsiblePrimitive` | Legacy Radix — awaiting Base UI port. |
128
+ | Popover | `popover.tsx` | Radix `PopoverPrimitive` | Legacy Radix — forwardRef pattern. Awaiting Base UI port. |
129
+
130
+ ### Composed Form Widgets
131
+
132
+ | Component | File | Deps | Notes |
133
+ |---|---|---|---|
134
+ | Form Field | `form-field.tsx` | Field, Popover | Higher-order FormField with hint popover system. `FieldHint` interface, IntersectionObserver for scroll detection, auto-show hints on focus. |
135
+ | Tag Input | `tag-input.tsx` | Button | Controlled tag input with chips, max items, duplicate detection, counter display. |
136
+ | Pill Select | `pill-select.tsx` | Button, Input | Generic single-select pill buttons with search, collapse/expand, deselect support. |
137
+ | Card Select | `card-select.tsx` | Badge | Grid of selectable option cards with label + description. Configurable columns (2/3/4), disabled state with badge label. |
138
+ | Entity Select | `entity-select.tsx` | Combobox | Generic type-safe combobox with render props for options/selected state, "Create new" action, loading/empty states. |
139
+ | Selected Entity Card | `selected-entity-card.tsx` | Button | Compact card showing a selected entity with remove button and optional edit action. Companion to EntitySelect. |
140
+
141
+ ### Data Display
142
+
143
+ | Component | File | Deps | Notes |
144
+ |---|---|---|---|
145
+ | Pagination | `pagination.tsx` | Button | Offset-based prev/next pagination with "Showing X-Y of Z" display. Auto-hides when total ≤ limit. |
146
+ | Empty State | `empty-state.tsx` | Button | Centered layout with icon circle, heading, description, and optional CTA button. For empty lists/tables. |
147
+ | Avatar | `avatar.tsx` | None | Image with initials fallback, error handling. CVA-driven size variants (xs/sm/default/lg/xl). |
148
+
149
+ ### App Shell & Layout
150
+
151
+ | Component | File | Deps | Notes |
152
+ |---|---|---|---|
153
+ | User Menu | `user-menu.tsx` | Sidebar, DropdownMenu, Avatar | Sidebar footer user menu with avatar, name/email, dropdown actions (profile, settings, sign out). Adapts to collapsed sidebar state. |
154
+ | Org Switcher | `org-switcher.tsx` | DropdownMenu, Button | Organization switcher dropdown with current org display, switch list with check indicator, and "Create Organization" action with PlusIcon. |
155
+ | Page Header | `page-header.tsx` | Button | Page title + description + action button (defaults to PlusIcon). Standard header for list/entity pages. |
156
+ | Status Filter | `status-filter.tsx` | Button | Pill-style filter tabs toggling active variant. Supports optional count display. Generic for any status enum. |
157
+ | Search Input | `search-input.tsx` | Input | Debounced search input with MagnifyingGlass icon. Configurable delay (default 300ms). |
74
158
 
75
159
  ---
76
160
 
@@ -81,19 +165,23 @@ Components use shadcn/ui conventions: Radix UI primitives for accessibility, `cl
81
165
  **packages/ui:**
82
166
  | Package | Version | Purpose |
83
167
  |---|---|---|
84
- | `{{SCOPE}}/tailwind-config` | `workspace:*` | Shared design tokens |
85
- | `@radix-ui/react-slot` | ^1.2 | Polymorphic component support (asChild pattern) |
86
- | `class-variance-authority` | ^0.7 | Component variant management |
87
- | `clsx` | ^2.1 | Conditional className utility |
88
- | `tailwind-merge` | ^3 | Smart Tailwind class deduplication |
89
- | `tw-animate-css` | ^1 | Animation classes |
90
- | `react` | ^19 | React |
91
- | `react-dom` | ^19 | React DOM |
92
- | `shadcn` | ^3 | Component generation CLI |
168
+ | `@base-ui/react` | ^1.0.0 | Headless UI primitives (Button, Input, Select, Combobox, Menu, AlertDialog, Separator, Badge) |
169
+ | `@phosphor-icons/react` | ^2.1.10 | Icon library with 6 weights per icon |
170
+ | `@radix-ui/react-collapsible` | ^1.1.12 | Collapsible primitive (legacy, pending Base UI port) |
171
+ | `@radix-ui/react-popover` | ^1.1.15 | Popover primitive (legacy, pending Base UI port) |
172
+ | `@radix-ui/react-slot` | ^1.2.4 | Polymorphic component support (used by some composed components) |
173
+ | `class-variance-authority` | ^0.7.1 | Component variant management |
174
+ | `clsx` | ^2.1.1 | Conditional className utility |
175
+ | `tailwind-merge` | ^3.4.0 | Smart Tailwind class deduplication |
176
+ | `tw-animate-css` | ^1.4.0 | Animation classes for transitions |
177
+ | `shadcn` | ^3.6.2 | Component generation CLI |
178
+ | `react` | ^19.1.0 | React |
179
+ | `react-dom` | ^19.1.0 | React DOM |
93
180
 
94
181
  **packages/ui (dev):**
95
182
  | Package | Version | Purpose |
96
183
  |---|---|---|
184
+ | `{{SCOPE}}/tailwind-config` | `workspace:*` | Shared design tokens |
97
185
  | `{{SCOPE}}/typescript-config` | `workspace:*` | Shared TypeScript config |
98
186
  | `@tailwindcss/postcss` | ^4 | PostCSS plugin |
99
187
  | `tailwindcss` | ^4 | Tailwind CSS |
@@ -132,7 +220,27 @@ Components use shadcn/ui conventions: Radix UI primitives for accessibility, `cl
132
220
  ```
133
221
  packages/ui/
134
222
  ├── components/
135
- │ └── ui/ # shadcn-generated components go here
223
+ │ └── ui/ # 31 base components (blueprint-owned)
224
+ │ ├── button.tsx
225
+ │ ├── input.tsx
226
+ │ ├── label.tsx
227
+ │ ├── textarea.tsx
228
+ │ ├── badge.tsx
229
+ │ ├── separator.tsx
230
+ │ ├── card.tsx
231
+ │ ├── alert-dialog.tsx
232
+ │ ├── select.tsx
233
+ │ ├── combobox.tsx
234
+ │ ├── dropdown-menu.tsx
235
+ │ ├── sidebar.tsx
236
+ │ ├── field.tsx
237
+ │ ├── input-group.tsx
238
+ │ ├── breadcrumb.tsx
239
+ │ ├── collapsible.tsx
240
+ │ ├── popover.tsx
241
+ │ ├── form-field.tsx
242
+ │ ├── tag-input.tsx
243
+ │ └── pill-select.tsx
136
244
  ├── icons/
137
245
  │ └── logos/ # Brand logos (SVG as React components)
138
246
  ├── styles/
@@ -140,7 +248,7 @@ packages/ui/
140
248
  ├── utils/
141
249
  │ ├── cn.ts # Class merging utility
142
250
  │ └── cva.ts # CVA re-export
143
- ├── components.json # shadcn CLI config
251
+ ├── components.json # shadcn CLI config (base-maia style)
144
252
  ├── package.json # Exports, imports, deps
145
253
  └── tsconfig.json # Extends shared react-library config
146
254
  ```
@@ -162,8 +270,9 @@ The `@source` directive tells Tailwind to scan the UI package for classes. The r
162
270
  **Usage in components:**
163
271
  ```typescript
164
272
  import { Button } from "{{SCOPE}}/ui/components/ui/button";
273
+ import { Card, CardHeader, CardTitle, CardContent } from "{{SCOPE}}/ui/components/ui/card";
165
274
  import { cn } from "{{SCOPE}}/ui/utils/cn";
166
- import { Logo } from "{{SCOPE}}/ui/icons/logos/Logo";
275
+ import { ArrowRight } from "@phosphor-icons/react/ArrowRight";
167
276
  ```
168
277
 
169
278
  ---
@@ -176,20 +285,26 @@ For standalone applications without a monorepo, UI components live alongside the
176
285
 
177
286
  | Aspect | Monorepo | Single-Repo |
178
287
  |--------|----------|-------------|
179
- | UI code location | `packages/ui/` (workspace package) | `src/ui/` or `src/components/` |
288
+ | UI code location | `packages/ui/` (workspace package) | `src/ui/` |
289
+ | Component location | `packages/ui/components/ui/` | `src/ui/components/ui/` |
180
290
  | Import pattern | `import { x } from "@scope/ui/components/ui/button"` | `import { x } from "@/ui/components/ui/button"` |
181
291
  | Package dependencies | `workspace:*` protocol | Direct — packages at root `package.json` |
182
292
  | TypeScript config | Extends `@scope/typescript-config/react-library.json` | Inherits from root `tsconfig.json` |
183
293
  | Styles import | `@import "{{SCOPE}}/ui/styles"` in consuming app | Not needed (same bundle) |
184
294
  | `@source` directive | Required (cross-package scanning) | Not needed (same project) |
185
295
  | shadcn CLI | Run from `packages/ui/` | Run from project root |
296
+ | `package.json` / `tsconfig.json` | `packages/ui/package.json` + `packages/ui/tsconfig.json` | N/A (use root configs) |
186
297
 
187
298
  ### What Stays the Same
188
299
 
300
+ - Base UI headless primitives and `data-slot` convention
301
+ - Compound component pattern and component API shapes
189
302
  - Path-based exports pattern (via `package.json` exports or TypeScript paths)
190
- - `cn()` utility and CVA pattern
191
- - `components.json` for shadcn CLI
303
+ - `cn()` utility and CVA patterns
304
+ - `components.json` for shadcn CLI (base-maia style)
192
305
  - Component file organization (`components/ui/`, `icons/`, `utils/`)
306
+ - All 31 base components — identical source files
307
+ - Phosphor Icons integration
193
308
 
194
309
  ---
195
310
 
@@ -197,12 +312,48 @@ For standalone applications without a monorepo, UI components live alongside the
197
312
 
198
313
  | File | Purpose | Monorepo Target | Single-Repo Target |
199
314
  |---|---|---|---|
200
- | `files/ui/styles/globals.css` | UI package styles (imports tailwind-config) | `packages/ui/styles/globals.css` | `src/ui/styles/globals.css` |
315
+ | **Config/Utilities** | | | |
316
+ | `files/ui/package.json` | Package config with exports, imports, deps, Base UI + Radix + Phosphor dependencies | `packages/ui/package.json` | N/A (merge deps into root `package.json`) |
317
+ | `files/ui/tsconfig.json` | TypeScript config extending shared react-library base | `packages/ui/tsconfig.json` | N/A (use root `tsconfig.json`) |
318
+ | `files/ui/components.json` | shadcn CLI configuration with `base-maia` style | `packages/ui/components.json` | `components.json` (project root) |
319
+ | `files/ui/styles/globals.css` | UI package styles (imports tailwind-config, animation classes) | `packages/ui/styles/globals.css` | `src/ui/styles/globals.css` |
201
320
  | `files/ui/utils/cn.ts` | Class merging utility (clsx + tailwind-merge) | `packages/ui/utils/cn.ts` | `src/ui/utils/cn.ts` |
202
321
  | `files/ui/utils/cva.ts` | CVA re-export for component variants | `packages/ui/utils/cva.ts` | `src/ui/utils/cva.ts` |
203
- | `files/ui/components.json` | shadcn CLI configuration | `packages/ui/components.json` | `components.json` (project root) |
204
- | `files/ui/package.json` | Package config with exports, imports, deps | `packages/ui/package.json` | N/A (use root `package.json`) |
205
- | `files/ui/tsconfig.json` | TypeScript config extending shared base | `packages/ui/tsconfig.json` | N/A (use root `tsconfig.json`) |
322
+ | **Primitive Components** | | | |
323
+ | `files/ui/components/ui/button.tsx` | Button with Base UI primitive, 6 variants x 8 sizes, CVA | `packages/ui/components/ui/button.tsx` | `src/ui/components/ui/button.tsx` |
324
+ | `files/ui/components/ui/input.tsx` | Text input with Base UI InputPrimitive | `packages/ui/components/ui/input.tsx` | `src/ui/components/ui/input.tsx` |
325
+ | `files/ui/components/ui/label.tsx` | Form label with data-slot targeting | `packages/ui/components/ui/label.tsx` | `src/ui/components/ui/label.tsx` |
326
+ | `files/ui/components/ui/textarea.tsx` | Textarea with data-slot targeting | `packages/ui/components/ui/textarea.tsx` | `src/ui/components/ui/textarea.tsx` |
327
+ | `files/ui/components/ui/badge.tsx` | Badge with Base UI useRender, 6 variants, polymorphic render prop | `packages/ui/components/ui/badge.tsx` | `src/ui/components/ui/badge.tsx` |
328
+ | `files/ui/components/ui/separator.tsx` | Separator with Base UI SeparatorPrimitive | `packages/ui/components/ui/separator.tsx` | `src/ui/components/ui/separator.tsx` |
329
+ | **Compound Components** | | | |
330
+ | `files/ui/components/ui/card.tsx` | Card with 7 compound parts (Header, Title, Description, Action, Content, Footer) | `packages/ui/components/ui/card.tsx` | `src/ui/components/ui/card.tsx` |
331
+ | `files/ui/components/ui/alert-dialog.tsx` | Alert dialog with Base UI AlertDialogPrimitive, 12 parts including AlertDialogMedia | `packages/ui/components/ui/alert-dialog.tsx` | `src/ui/components/ui/alert-dialog.tsx` |
332
+ | `files/ui/components/ui/select.tsx` | Select with Base UI SelectPrimitive, Portal/Positioner, scroll buttons, 10 parts | `packages/ui/components/ui/select.tsx` | `src/ui/components/ui/select.tsx` |
333
+ | `files/ui/components/ui/combobox.tsx` | Combobox with Base UI ComboboxPrimitive, chips support, InputGroup integration, 16 exports | `packages/ui/components/ui/combobox.tsx` | `src/ui/components/ui/combobox.tsx` |
334
+ | `files/ui/components/ui/dropdown-menu.tsx` | Dropdown menu with Base UI MenuPrimitive, sub-menus, checkbox/radio items, 15 exports | `packages/ui/components/ui/dropdown-menu.tsx` | `src/ui/components/ui/dropdown-menu.tsx` |
335
+ | `files/ui/components/ui/sidebar.tsx` | Sidebar with context-based state, cookie persistence, CVA variants, 14 exports | `packages/ui/components/ui/sidebar.tsx` | `src/ui/components/ui/sidebar.tsx` |
336
+ | `files/ui/components/ui/field.tsx` | Field compound layout with 10 parts (Field, FieldGroup, FieldSet, FieldLabel, etc.) | `packages/ui/components/ui/field.tsx` | `src/ui/components/ui/field.tsx` |
337
+ | `files/ui/components/ui/input-group.tsx` | Input group compound with 6 parts (Addon, Button, Text, Input, Textarea) | `packages/ui/components/ui/input-group.tsx` | `src/ui/components/ui/input-group.tsx` |
338
+ | **Navigation/Utility** | | | |
339
+ | `files/ui/components/ui/breadcrumb.tsx` | Breadcrumb with Base UI useRender for polymorphic links, 7 exports | `packages/ui/components/ui/breadcrumb.tsx` | `src/ui/components/ui/breadcrumb.tsx` |
340
+ | `files/ui/components/ui/collapsible.tsx` | Collapsible with Radix CollapsiblePrimitive (legacy) | `packages/ui/components/ui/collapsible.tsx` | `src/ui/components/ui/collapsible.tsx` |
341
+ | `files/ui/components/ui/popover.tsx` | Popover with Radix PopoverPrimitive, forwardRef pattern (legacy) | `packages/ui/components/ui/popover.tsx` | `src/ui/components/ui/popover.tsx` |
342
+ | **Composed Form Widgets** | | | |
343
+ | `files/ui/components/ui/form-field.tsx` | Higher-order FormField with hint popover system (FieldHint interface, IntersectionObserver scroll detection, auto-show on focus) | `packages/ui/components/ui/form-field.tsx` | `src/ui/components/ui/form-field.tsx` |
344
+ | `files/ui/components/ui/tag-input.tsx` | Controlled tag input with chips, max items, duplicate detection, counter | `packages/ui/components/ui/tag-input.tsx` | `src/ui/components/ui/tag-input.tsx` |
345
+ | `files/ui/components/ui/pill-select.tsx` | Generic single-select pill buttons with search, collapse/expand, deselect | `packages/ui/components/ui/pill-select.tsx` | `src/ui/components/ui/pill-select.tsx` |
346
+ | `files/ui/components/ui/card-select.tsx` | Grid of selectable option cards with label, description, disabled state | `packages/ui/components/ui/card-select.tsx` | `src/ui/components/ui/card-select.tsx` |
347
+ | `files/ui/components/ui/entity-select.tsx` | Generic combobox with render props, "Create new" action, loading/empty states | `packages/ui/components/ui/entity-select.tsx` | `src/ui/components/ui/entity-select.tsx` |
348
+ | `files/ui/components/ui/selected-entity-card.tsx` | Compact selected entity display with remove/edit actions | `packages/ui/components/ui/selected-entity-card.tsx` | `src/ui/components/ui/selected-entity-card.tsx` |
349
+ | `files/ui/components/ui/pagination.tsx` | Offset-based prev/next pagination with count display | `packages/ui/components/ui/pagination.tsx` | `src/ui/components/ui/pagination.tsx` |
350
+ | `files/ui/components/ui/empty-state.tsx` | Centered empty state with icon, heading, description, CTA | `packages/ui/components/ui/empty-state.tsx` | `src/ui/components/ui/empty-state.tsx` |
351
+ | `files/ui/components/ui/avatar.tsx` | Image avatar with initials fallback and size variants | `packages/ui/components/ui/avatar.tsx` | `src/ui/components/ui/avatar.tsx` |
352
+ | `files/ui/components/ui/user-menu.tsx` | Sidebar footer user menu with avatar, dropdown actions, collapsed state | `packages/ui/components/ui/user-menu.tsx` | `src/ui/components/ui/user-menu.tsx` |
353
+ | `files/ui/components/ui/org-switcher.tsx` | Organization switcher dropdown with switch list and create action | `packages/ui/components/ui/org-switcher.tsx` | `src/ui/components/ui/org-switcher.tsx` |
354
+ | `files/ui/components/ui/page-header.tsx` | Page title + description + action button with PlusIcon default | `packages/ui/components/ui/page-header.tsx` | `src/ui/components/ui/page-header.tsx` |
355
+ | `files/ui/components/ui/status-filter.tsx` | Pill-style status filter tabs with count display | `packages/ui/components/ui/status-filter.tsx` | `src/ui/components/ui/status-filter.tsx` |
356
+ | `files/ui/components/ui/search-input.tsx` | Debounced search input with magnifying glass icon | `packages/ui/components/ui/search-input.tsx` | `src/ui/components/ui/search-input.tsx` |
206
357
 
207
358
  ---
208
359
 
@@ -211,57 +362,73 @@ For standalone applications without a monorepo, UI components live alongside the
211
362
  ### Phase 1: Package Setup
212
363
 
213
364
  1. Create `packages/ui/` directory
214
- 2. Copy `package.json` — replace `{{SCOPE}}`
215
- 3. Copy `tsconfig.json` — replace `{{SCOPE}}`
216
- 4. Copy `components.json`
365
+ 2. Copy `files/ui/package.json` — replace `{{SCOPE}}` with project scope
366
+ 3. Copy `files/ui/tsconfig.json` — replace `{{SCOPE}}`
367
+ 4. Copy `files/ui/components.json`
217
368
 
218
369
  ### Phase 2: Utilities and Styles
219
370
 
220
371
  5. Create `utils/` directory
221
- 6. Copy `cn.ts`
222
- 7. Copy `cva.ts`
372
+ 6. Copy `files/ui/utils/cn.ts`
373
+ 7. Copy `files/ui/utils/cva.ts`
223
374
  8. Create `styles/` directory
224
- 9. Copy `globals.css` — replace `{{SCOPE}}`
375
+ 9. Copy `files/ui/styles/globals.css` — replace `{{SCOPE}}`
225
376
 
226
377
  ### Phase 3: Directory Structure
227
378
 
228
- 10. Create `components/ui/` directory (empty — shadcn will populate it)
229
- 11. Create `icons/` directory (empty — user adds brand assets)
230
- 12. Create `icons/logos/` directory
379
+ 10. Create `components/ui/` directory
380
+ 11. Create `icons/` directory (empty — user adds custom icons)
381
+ 12. Create `icons/logos/` directory (empty — user adds brand assets)
382
+
383
+ ### Phase 4: Base Components
231
384
 
232
- ### Phase 4: Consuming App Integration
385
+ 13. Copy all 31 component files from `files/ui/components/ui/` to `packages/ui/components/ui/`:
386
+ - Primitives: `button.tsx`, `input.tsx`, `label.tsx`, `textarea.tsx`, `badge.tsx`, `separator.tsx`
387
+ - Compound: `card.tsx`, `alert-dialog.tsx`, `select.tsx`, `combobox.tsx`, `dropdown-menu.tsx`, `sidebar.tsx`, `field.tsx`, `input-group.tsx`
388
+ - Navigation: `breadcrumb.tsx`, `collapsible.tsx`, `popover.tsx`
389
+ - Form widgets: `form-field.tsx`, `tag-input.tsx`, `pill-select.tsx`, `card-select.tsx`, `entity-select.tsx`, `selected-entity-card.tsx`
390
+ - Data display: `pagination.tsx`, `empty-state.tsx`, `avatar.tsx`
391
+ - App shell: `user-menu.tsx`, `org-switcher.tsx`, `page-header.tsx`, `status-filter.tsx`, `search-input.tsx`
233
392
 
234
- 13. Update `apps/web/app/globals.css`:
393
+ ### Phase 5: Consuming App Integration
394
+
395
+ 14. Update `apps/web/app/globals.css`:
235
396
  - Add `@import "{{SCOPE}}/ui/styles";` after the tailwind-config import
236
397
  - Add `@plugin "@tailwindcss/typography";`
237
398
  - Add `@source "../../../packages/ui";` (compute relative path based on app location)
238
- 14. Verify `pnpm-workspace.yaml` includes `packages/*`
399
+ 15. Verify the workspace configuration in root `package.json` includes `packages/*`
239
400
 
240
- ### Phase 5: Dependency Installation
401
+ ### Phase 6: Dependency Installation
241
402
 
242
- 15. Run `pnpm install` to wire workspace dependencies
243
- 16. Verify no peer dependency warnings
403
+ 16. Run `bun install` to wire workspace dependencies and install Base UI, Phosphor, Radix legacy, CVA, and other packages
404
+ 17. Verify no peer dependency warnings
244
405
 
245
- ### Phase 6: Verification
406
+ ### Phase 7: Verification
246
407
 
247
- 17. Run `pnpm dev` — no CSS/import errors
248
- 18. Try adding a shadcn component: `cd packages/ui && pnpm dlx shadcn@latest add button`
249
- 19. Import and use the button in `apps/web/` — verify it renders with correct styles
408
+ 18. Run `bun dev` — no CSS/import errors
409
+ 19. Import a component in `apps/web/` and verify it renders with correct styles:
410
+ ```typescript
411
+ import { Button } from "@scope/ui/components/ui/button";
412
+ ```
413
+ 20. Verify `data-slot` attributes appear in rendered HTML via browser dev tools
414
+ 21. Try adding a shadcn component: `cd packages/ui && bunx shadcn@latest add tooltip` — verify it uses `base-maia` style and produces Base UI output
250
415
 
251
416
  ---
252
417
 
253
418
  ## Adding Components
254
419
 
255
- After initial scaffolding, add shadcn/ui components:
420
+ ### Via shadcn CLI (user-owned)
421
+
422
+ After initial scaffolding, add more shadcn/ui components. The `base-maia` style ensures generated components use Base UI primitives and follow the `data-slot` convention:
256
423
 
257
424
  ```bash
258
425
  # From packages/ui/
259
- pnpm dlx shadcn@latest add button
260
- pnpm dlx shadcn@latest add card
261
- pnpm dlx shadcn@latest add input
426
+ bunx shadcn@latest add tooltip
427
+ bunx shadcn@latest add tabs
428
+ bunx shadcn@latest add dialog
262
429
  ```
263
430
 
264
- Components are generated in `components/ui/` and immediately available to consuming apps via path-based exports.
431
+ Components generated via `shadcn add` are **user-owned** the blueprint never modifies them during upgrades.
265
432
 
266
433
  ### Custom Components
267
434
 
@@ -279,7 +446,7 @@ interface FeatureCardProps {
279
446
 
280
447
  export function FeatureCard({ title, className, children }: FeatureCardProps) {
281
448
  return (
282
- <div className={cn("rounded-lg border p-6", className)}>
449
+ <div data-slot="feature-card" className={cn("rounded-lg border p-6", className)}>
283
450
  <h3 className="text-lg font-semibold">{title}</h3>
284
451
  {children}
285
452
  </div>
@@ -291,26 +458,160 @@ Import in consuming apps: `import { FeatureCard } from "@scope/ui/components/fea
291
458
 
292
459
  ---
293
460
 
461
+ ## Usage Patterns
462
+
463
+ These patterns document **how to compose** the base components into real application features. They are conventions extracted from production usage — not additional components, but recipes for building with the component library.
464
+
465
+ ### Icon Conventions
466
+
467
+ - **Create/add actions**: Always use `PlusIcon` before the label: `<Button><PlusIcon className="size-4" />New Item</Button>`
468
+ - **Navigation arrows**: `CaretLeftIcon` / `CaretRightIcon` for pagination, `CaretUpDownIcon` for dropdown triggers
469
+ - **Menu triggers**: `DotsThreeIcon weight="bold"` for action menus on cards/rows
470
+ - **Import style**: Prefer deep imports for tree-shaking: `import { PlusIcon } from "@phosphor-icons/react/Plus"`
471
+ - **Default icon size**: `size-4` (16px) inside buttons and menu items, `size-3.5` for inline indicators
472
+
473
+ ### Form Integration with React Hook Form + Zod
474
+
475
+ The recommended form stack is **react-hook-form** + **@hookform/resolvers** + **zod** + **FormField**:
476
+
477
+ ```tsx
478
+ import { useForm, Controller } from "react-hook-form";
479
+ import { zodResolver } from "@hookform/resolvers/zod";
480
+
481
+ const { register, control, handleSubmit, formState: { errors } } = useForm({
482
+ resolver: zodResolver(schema),
483
+ defaultValues: { ... },
484
+ });
485
+ ```
486
+
487
+ **Field connection patterns:**
488
+ - **Simple inputs** — use `register()` directly: `<Input {...register("name")} />`
489
+ - **Custom/controlled fields** — use `Controller`: `<Controller name="field" control={control} render={({ field }) => <CardSelect value={field.value} onChange={field.onChange} />} />`
490
+ - **FormField wrapper** — wraps every field with label, error, description, and optional hint system
491
+
492
+ **Hint system wiring:**
493
+ ```tsx
494
+ const [showHints, setShowHints] = useState(true);
495
+ const [activeHintField, setActiveHintField] = useState<string | null>(null);
496
+
497
+ <FormField
498
+ label="Name" name="name" required error={errors.name?.message}
499
+ hint={{ directive: "Be specific", example: "Enterprise SaaS for HR teams" }}
500
+ showHints={showHints} isHintOpen={activeHintField === "name"}
501
+ onHintOpen={(name) => setActiveHintField(name)}
502
+ onHintClose={() => setActiveHintField(null)}
503
+ onToggleHints={() => setShowHints(prev => !prev)}
504
+ >
505
+ <Input {...register("name")} />
506
+ </FormField>
507
+ ```
508
+
509
+ ### Dialog Form Pattern
510
+
511
+ Dialogs that contain forms follow a consistent structure:
512
+
513
+ **Confirmation dialogs** (archive, delete, restore):
514
+ - `AlertDialog` + `AlertDialogContent size="sm"`
515
+ - `AlertDialogHeader` with `AlertDialogMedia` (icon in circle) + title + description
516
+ - `AlertDialogFooter` with Cancel + destructive/confirm Button
517
+ - Async action with loading state on the confirm button
518
+
519
+ **Multi-step form dialogs** (create entity):
520
+ 1. **Choice step**: Two-column grid of outline buttons choosing creation method (manual vs AI). `AlertDialogContent` at `max-w-lg`.
521
+ 2. **Form step**: Animated width transition to `max-w-[38rem]`. Scrollable form body with sticky footer. Close button positioned `absolute top-0 right-0`.
522
+ 3. On close: reset form, reset mutation state, then `setTimeout(() => setStep("choice"), 500)` for smooth animation.
523
+
524
+ **Single-step form dialogs**: Skip the choice step, go directly to the form. Same scrollable body + sticky footer pattern.
525
+
526
+ ### List Page Composition
527
+
528
+ Every entity list page follows this structure:
529
+
530
+ ```
531
+ PageHeader (title + description + "New X" button)
532
+
533
+ StatusFilter (active/archived tabs, or full status set)
534
+
535
+ SearchInput + sort Select (optional, for complex lists)
536
+
537
+ Content area:
538
+ - Loading: skeleton grid/table with animate-pulse
539
+ - Empty: EmptyState component
540
+ - Data: grid of EntityCards or DataTable + Pagination
541
+
542
+ Inline dialogs (create, edit, archive, restore)
543
+ ```
544
+
545
+ **Skeleton loading pattern:**
546
+ ```tsx
547
+ {isLoading && (
548
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
549
+ {[...Array(6)].map((_, i) => (
550
+ <Card key={i} className="animate-pulse">
551
+ <CardHeader><div className="h-5 bg-muted rounded w-3/4" /></CardHeader>
552
+ <CardContent><div className="h-4 bg-muted rounded w-1/2" /></CardContent>
553
+ </Card>
554
+ ))}
555
+ </div>
556
+ )}
557
+ ```
558
+
559
+ ### Sidebar App Shell
560
+
561
+ The recommended app shell layout:
562
+
563
+ ```
564
+ SidebarProvider
565
+ ├── Header (sticky top-0, contains OrgSwitcher + Separator + Breadcrumbs)
566
+ ├── Sidebar collapsible="icon"
567
+ │ ├── SidebarContent → Navigation (SidebarMenu with SidebarMenuButton)
568
+ │ ├── SidebarFooter → UserMenu
569
+ │ └── SidebarRail (edge toggle)
570
+ └── SidebarInset → main content area
571
+ ```
572
+
573
+ - Header height set via `--header-height` CSS variable
574
+ - Sidebar adapts to collapsed state — UserMenu shows only avatar, navigation shows only icons
575
+ - OrgSwitcher lives in the header, not the sidebar — stays visible when sidebar collapses
576
+
577
+ ### Entity Management Pattern
578
+
579
+ For CRUD entity pages (audiences, brand voices, CTAs), the pattern is:
580
+
581
+ 1. **List page**: `PageHeader` + `StatusFilter` + grid of entity `Card` components with action `DropdownMenu`
582
+ 2. **Entity cards**: `Card` + `CardHeader` (name + action menu) + `CardContent` (badges/metadata) + `CardFooter` (timestamp)
583
+ 3. **Action menus**: `DropdownMenu` triggered by 3-dot icon button. Edit + Archive (active) or Restore (archived) items. Destructive separator before archive/delete.
584
+ 4. **Inline dialogs**: All create/edit/archive/restore dialogs render at the page level, controlled by state. On success, invalidate SWR cache.
585
+ 5. **Entity selection in forms**: `EntitySelect` with render props for custom option/selected display. "Create new" action opens an inline dialog, auto-selects the created entity via `setValue()`.
586
+
587
+ ---
588
+
294
589
  ## Maintenance Hooks
295
590
 
296
591
  ### Component & Style Hooks
297
592
 
298
593
  | When you... | Then do... | Why |
299
594
  |---|---|---|
300
- | Add a shadcn component | Run `pnpm dlx shadcn@latest add <name>` from `packages/ui/` | Generates in the correct location with proper aliases |
595
+ | Add a shadcn component | Run `bunx shadcn@latest add <name>` from `packages/ui/` | Uses `base-maia` style to generate Base UI components in the correct location |
596
+ | Create a custom component | Add `data-slot="component-name"` to the root element | Maintains the CSS targeting convention across all components |
301
597
  | Add a new app that uses UI | Add `@import "{{SCOPE}}/ui/styles"` and `@source` directive to the app's `globals.css` | New apps don't automatically see UI package classes |
302
598
  | Add a new export directory | Add it to both `exports` and `imports` in `packages/ui/package.json` | Missing exports = import errors in consuming apps |
303
599
  | Modify component variants | Update CVA definitions and ensure `cn()` is used for className composition | Inconsistent class merging = broken styles |
304
600
  | Add an icon | Place in `icons/` or `icons/logos/` — export is automatic via wildcard | Follows the path-based export convention |
601
+ | Upgrade base components | Run `/scaffold-ui` in upgrade mode | Updates blueprint-owned components without touching user-owned ones |
305
602
 
306
- ### Condensed Rules for project rules file
603
+ ### Condensed Rules for project maintenance skill
307
604
 
308
605
  ```markdown
309
606
  ### ui-shared-components maintenance
310
- - Add shadcn components via `pnpm dlx shadcn@latest add <name>` from packages/ui/
607
+ - Add shadcn components via `bunx shadcn@latest add <name>` from packages/ui/ (uses base-maia style)
311
608
  - Use cn() for all className composition — never concatenate class strings manually
609
+ - Use data-slot attribute on every component root element and sub-components
610
+ - Import Phosphor icons from @phosphor-icons/react or @phosphor-icons/react/<IconName>
611
+ - Base UI primitives: import from @base-ui/react/<primitive-name>
312
612
  - Internal imports use #prefix (#components/ui/button); cross-package imports use @scope/ui/components/ui/button
313
- - After adding a new export directory: add to both exports and imports in packages/ui/package.json
613
+ - Base components in components/ui/ are blueprint-owned update via /scaffold-ui upgrade mode
614
+ - After adding new export directory: add to both exports and imports in packages/ui/package.json
314
615
  - After adding a new consuming app: add @import and @source directive to the app's globals.css
315
616
  ```
316
617
 
@@ -318,18 +619,23 @@ Import in consuming apps: `import { FeatureCard } from "@scope/ui/components/fea
318
619
 
319
620
  ## What's Configurable
320
621
 
321
- - shadcn style (`new-york`, `default`, or custom)
322
- - Icon library (`lucide`, `phosphor`, `radix`)
323
- - Which consuming apps get the globals.css update
324
- - Additional export directories
622
+ - **shadcn style** — `base-maia` (default), `new-york`, `default`, or any shadcn style
623
+ - **Icon library** — Phosphor (default), Lucide, Radix, or custom
624
+ - **Include base components** Yes (default, all 31 components), No (skeleton only with empty `components/ui/`)
625
+ - **Include form system** — Yes (default), No (skip `form-field`, `field`, `tag-input`, `pill-select`)
626
+ - **Which consuming apps get globals.css update** — Ask during scaffolding
325
627
 
326
628
  ## What's Opinionated
327
629
 
328
- - **Path-based exports** — no barrel re-exports for components
329
- - **No build step** — exports TypeScript source, consuming apps transpile
330
- - **shadcn/ui conventions** — `components.json`, `cn()`, CVA variants
331
- - **`#` subpath imports** — internal package imports use Node.js subpath imports
332
- - **Flat component structure** — `components/ui/` for shadcn, `components/` for custom, `icons/` for assets
630
+ - **Base UI as headless layer** — Modern API, `data-*` state attributes, render props for polymorphism
631
+ - **`data-slot` attribute convention** — Stable CSS targeting that survives class changes
632
+ - **Compound component pattern** — Named parts over monolithic prop APIs
633
+ - **Phosphor Icons** — Six weights, tree-shakeable, consistent API
634
+ - **Path-based exports** — No barrel re-exports for components
635
+ - **No build step** — Exports TypeScript source, consuming apps transpile
636
+ - **`#` prefix subpath imports** — Node.js native, no path alias configuration
637
+ - **Flat component structure** — `components/ui/` for all components, no nested directories
638
+ - **CVA for all variant systems** — Consistent variant definition pattern across components
333
639
 
334
640
  ## Project Context Output
335
641
 
@@ -340,9 +646,11 @@ Appends to `.project-context.md` under `## Installed Blueprints`:
340
646
  blueprint: ui-shared-components
341
647
  installed_at: <date>
342
648
  choices:
343
- shadcn_style: new-york
344
- icon_library: lucide
649
+ shadcn_style: base-maia
650
+ icon_library: phosphor
345
651
  include_cva: true
652
+ include_base_components: true
653
+ include_form_system: true
346
654
  files_created:
347
655
  - packages/ui/package.json
348
656
  - packages/ui/tsconfig.json
@@ -350,9 +658,33 @@ files_created:
350
658
  - packages/ui/styles/globals.css
351
659
  - packages/ui/utils/cn.ts
352
660
  - packages/ui/utils/cva.ts
661
+ - packages/ui/components/ui/button.tsx
662
+ - packages/ui/components/ui/input.tsx
663
+ - packages/ui/components/ui/label.tsx
664
+ - packages/ui/components/ui/textarea.tsx
665
+ - packages/ui/components/ui/card.tsx
666
+ - packages/ui/components/ui/badge.tsx
667
+ - packages/ui/components/ui/separator.tsx
668
+ - packages/ui/components/ui/alert-dialog.tsx
669
+ - packages/ui/components/ui/select.tsx
670
+ - packages/ui/components/ui/combobox.tsx
671
+ - packages/ui/components/ui/dropdown-menu.tsx
672
+ - packages/ui/components/ui/breadcrumb.tsx
673
+ - packages/ui/components/ui/collapsible.tsx
674
+ - packages/ui/components/ui/popover.tsx
675
+ - packages/ui/components/ui/sidebar.tsx
676
+ - packages/ui/components/ui/field.tsx
677
+ - packages/ui/components/ui/form-field.tsx
678
+ - packages/ui/components/ui/input-group.tsx
679
+ - packages/ui/components/ui/tag-input.tsx
680
+ - packages/ui/components/ui/pill-select.tsx
353
681
  globals_css_updated:
354
682
  - apps/web/app/globals.css
355
683
  packages_added:
684
+ - "@base-ui/react"
685
+ - "@phosphor-icons/react"
686
+ - "@radix-ui/react-collapsible"
687
+ - "@radix-ui/react-popover"
356
688
  - "@radix-ui/react-slot"
357
689
  - class-variance-authority
358
690
  - clsx
@@ -365,9 +697,10 @@ packages_added:
365
697
 
366
698
  ## References
367
699
 
700
+ - **Base UI:** https://base-ui.com
701
+ - **Phosphor Icons:** https://phosphoricons.com
368
702
  - **shadcn/ui Docs:** https://ui.shadcn.com/docs
369
703
  - **shadcn/ui Monorepo Guide:** https://ui.shadcn.com/docs/monorepo
370
704
  - **Tailwind CSS v4:** https://tailwindcss.com/docs
371
705
  - **Class Variance Authority:** https://cva.style/docs
372
706
  - **Node.js Subpath Imports:** https://nodejs.org/api/packages.html#subpath-imports
373
- - **Radix UI Primitives:** https://www.radix-ui.com/primitives