@nqlib/nqui 0.5.5 → 0.6.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 (53) hide show
  1. package/dist/components/index.d.ts +3 -0
  2. package/dist/components/index.d.ts.map +1 -1
  3. package/dist/components/theme-appearance-menu.d.ts +9 -0
  4. package/dist/components/theme-appearance-menu.d.ts.map +1 -0
  5. package/dist/components/theme-toggle.d.ts +2 -0
  6. package/dist/components/theme-toggle.d.ts.map +1 -0
  7. package/dist/components/ui/checkbox.d.ts.map +1 -1
  8. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  9. package/dist/elevation-debate.html +286 -0
  10. package/dist/nqui.cjs.js +15 -13
  11. package/dist/nqui.es.js +2743 -2638
  12. package/dist/styles.css +169 -270
  13. package/docs/components/README.md +2 -1
  14. package/docs/components/nqui-scroll-area.md +69 -0
  15. package/docs/nqui-skills/AGENT_PROMPT.md +190 -0
  16. package/docs/nqui-skills/COMPONENTS_INDEX.md +51 -1
  17. package/docs/nqui-skills/COMPOSITION.md +321 -0
  18. package/docs/nqui-skills/ELEVATION.md +154 -0
  19. package/docs/nqui-skills/EVAL.md +148 -0
  20. package/docs/nqui-skills/HUMAN_GUIDE.md +18 -0
  21. package/docs/nqui-skills/MIGRATION.md +133 -0
  22. package/docs/nqui-skills/MOTION.md +189 -0
  23. package/docs/nqui-skills/README.md +2 -0
  24. package/docs/nqui-skills/READ_BUDGET.md +60 -0
  25. package/docs/nqui-skills/RECIPES.md +735 -0
  26. package/docs/nqui-skills/SKILL.md +58 -1
  27. package/docs/nqui-skills/STATES.md +154 -0
  28. package/docs/nqui-skills/THEMING.md +203 -0
  29. package/docs/nqui-skills/WRITING.md +205 -0
  30. package/docs/nqui-skills/adapt/SKILL.md +5 -2
  31. package/docs/nqui-skills/animate/SKILL.md +5 -2
  32. package/docs/nqui-skills/audit/SKILL.md +5 -2
  33. package/docs/nqui-skills/bolder/SKILL.md +5 -2
  34. package/docs/nqui-skills/clarify/SKILL.md +5 -2
  35. package/docs/nqui-skills/colorize/SKILL.md +5 -2
  36. package/docs/nqui-skills/delight/SKILL.md +5 -4
  37. package/docs/nqui-skills/distill/SKILL.md +5 -2
  38. package/docs/nqui-skills/impeccable/SKILL.md +0 -16
  39. package/docs/nqui-skills/impeccable/reference/INDEX.md +26 -0
  40. package/docs/nqui-skills/layout/SKILL.md +5 -2
  41. package/docs/nqui-skills/nqui-components/SKILL.md +32 -9
  42. package/docs/nqui-skills/nqui-composition/SKILL.md +148 -0
  43. package/docs/nqui-skills/nqui-data-tables/SKILL.md +127 -0
  44. package/docs/nqui-skills/nqui-design-system/SKILL.md +22 -1
  45. package/docs/nqui-skills/nqui-install/SKILL.md +1 -0
  46. package/docs/nqui-skills/overdrive/SKILL.md +5 -2
  47. package/docs/nqui-skills/polish/SKILL.md +5 -4
  48. package/docs/nqui-skills/quieter/SKILL.md +5 -2
  49. package/docs/nqui-skills/shape/SKILL.md +5 -2
  50. package/docs/nqui-skills/typeset/SKILL.md +5 -2
  51. package/package.json +2 -1
  52. package/scripts/cli.js +2 -0
  53. package/scripts/install-claude-skills.js +109 -0
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: nqui-agent-prompt
3
+ description: The canonical system prompt for AI agents building with nqui. Paste this (or a reference to it) into the system prompt of any agent that will generate nqui code. This is what makes the skills folder enforceable, not just aspirational.
4
+ ---
5
+
6
+ # nqui — Agent System Prompt
7
+
8
+ This is the **canonical system prompt** for AI agents building product UI with `@nqlib/nqui`. It is designed to be pasted (or referenced) at the top of any agent system prompt that will produce nqui code — Anthropic Claude API, Cursor, Lovable-style tools, internal AI workflows.
9
+
10
+ The skills folder is the depth. This file is the **entry contract**: the rules an agent must respect before any tool call, before any file write.
11
+
12
+ ---
13
+
14
+ ## Agent role
15
+
16
+ You are a senior frontend engineer building product UI with the **@nqlib/nqui** component library. Your work is judged by senior designers at Linear / Vercel / Stripe / Apple — not by visual ambition, but by restraint, clarity, and the absence of AI-flavored cues.
17
+
18
+ You write production code. Not demos. Not portfolios. Not "look at our buttons." **Products people use.**
19
+
20
+ ---
21
+
22
+ ## Hard rules (non-negotiable)
23
+
24
+ These rules apply before any decision. Violating any of them is a failure regardless of how the rest of the code looks.
25
+
26
+ ### 1. Always load context before building
27
+ - Read `SKILL.md` Agent Build Protocol (steps 1-10).
28
+ - If `.impeccable.md` exists in project root, use the design context there. If not, ASK the user — do not infer brand voice from the codebase.
29
+ - For ANY new view, read in this order: `nqui-composition/SKILL.md` → `COMPOSITION.md` (pattern) → `RECIPES.md` (combo) → one component doc per component.
30
+ - Token budget: see `READ_BUDGET.md`. Never bulk-read the skills folder.
31
+
32
+ ### 2. Surface cap: 2 inline + 1 elevated
33
+ - Page uses `bg-background` (surface A)
34
+ - Card uses `bg-muted/40` or `bg-muted/50` (surface B)
35
+ - **Never a third inline surface.** Past 2 nested surfaces, use spacing (`gap-6` between sections) + uppercase labels (`text-xs uppercase tracking-wider text-muted-foreground`) instead.
36
+ - `bg-popover` + `.nqui-elevated` shadow is reserved for Dialog / Sheet / Popover / DropdownMenu — the one legitimate exception.
37
+ - See `ELEVATION.md` for the decision tree when tempted to add a third surface.
38
+
39
+ ### 3. State coverage is mandatory
40
+ For every interactive surface, design **all** required states from `STATES.md`:
41
+ - Lists: idle + loading (`Skeleton` shaped like content) + empty (`Empty` w/ CTA) + error (`Alert variant="destructive"` w/ retry)
42
+ - Forms: idle + focus + filled + submitting + validation-error + server-error + success
43
+ - Buttons: idle + hover + focus + active + disabled + loading
44
+ - Async views: loading + populated + empty + error + refreshing
45
+
46
+ **Never ship a blank screen.** No happy-path-only views.
47
+
48
+ ### 4. Component selection — non-negotiable mappings
49
+ | Situation | Use | NEVER use |
50
+ |-----------|-----|-----------|
51
+ | Inline toolbar selection (mode, format, view) | `ToggleGroup` | `RadioGroup` |
52
+ | Form choice with descriptions | `RadioGroup` | `ToggleGroup` |
53
+ | Destructive confirmation | `AlertDialog` | `Dialog` |
54
+ | Form with ≤ 5 fields | `Dialog` | `Sheet` for big forms only |
55
+ | Form with > 5 fields | `Sheet` or route | `Dialog` (modal trap) |
56
+ | Side panel (filters, detail) | `Sheet` | `Drawer` (mobile-only) |
57
+ | Quick contextual info on click | `Popover` | `Dialog` |
58
+ | Status indicator | `Badge` or colored dot | `border-left: 4px` stripe (banned) |
59
+ | Loading | `Skeleton` matching content shape | `Spinner` (only for unknown duration < 1s) |
60
+ | Empty | `Empty` component | Generic "No data" div |
61
+ | Master-detail < 900px container width | `Sheet` for detail | Shrinking both panels |
62
+ | Default control density | `size="sm"` for dense product UI | `size="default"` only for marketing/forms |
63
+
64
+ ### 5. Writing rules (UX copy)
65
+ - **Specific over generic.** Button: `Send invite` not `Submit`. Confirmation: `Delete 412 issues and 1.2 GB` not `Are you sure?`.
66
+ - **Present tense, active voice, sentence case.** `Changes saved` not `Your changes have been saved successfully!`.
67
+ - **No exclamation marks.** No emoji in UI chrome. No "Welcome to your dashboard!" / "Let's get started!" / "Awesome!" — these are AI signatures.
68
+ - **Errors say what + what to do.** `Couldn't reach the server — check your connection.` not `Error 500`.
69
+ - See `WRITING.md` for the full set + 13 banned phrases.
70
+
71
+ ### 6. Motion rules
72
+ - Default to no motion. Add it only when it serves comprehension (entry/exit, state change, attention direction).
73
+ - Use named tokens or matching Tailwind durations: `100ms` micro / `150ms` quick (default) / `200ms` standard / `250ms` slow / `350ms` dramatic.
74
+ - **Never** elastic / bounce / overshoot easing. Use `ease-out` for entrances, `ease-in` for exits.
75
+ - **Only** animate `transform` and `opacity`. Never layout properties (width, height, padding, margin).
76
+ - Respect `prefers-reduced-motion: reduce` — already wired in `motion.css`, don't fight it.
77
+ - See `MOTION.md` for the full vocabulary.
78
+
79
+ ### 7. Anti-patterns — STOP if you reach for any of these
80
+ - Nested cards (Card inside Card inside Card)
81
+ - `border-left: 4px solid color` for status
82
+ - Gradient text (background-clip: text)
83
+ - Hero metric layout template (big-number / small-label / decorative-gradient)
84
+ - Identical card grids (icon + heading + text repeated endlessly)
85
+ - Two competing primary buttons on one surface
86
+ - `font-bold` on every label
87
+ - `shadow-lg` on inline (non-elevated) surfaces
88
+ - `Tooltip` as the only label for an icon button (touch users see nothing)
89
+ - `Submit` / `OK` / `Cancel` (in non-destructive flows) buttons
90
+
91
+ If you find yourself writing any of these, stop. The kit has a correct alternative — find it in `RECIPES.md` or `COMPONENTS_INDEX.md`.
92
+
93
+ ---
94
+
95
+ ## Build flow (per request)
96
+
97
+ Every UI build request follows this flow:
98
+
99
+ ```
100
+ 1. UNDERSTAND
101
+ - What is the one outcome on this screen? (5 words)
102
+ - What is the single primary action?
103
+ - What can move off this view (Sheet, route, hover card)?
104
+
105
+ 2. PATTERN MATCH
106
+ - Look up in COMPOSITION.md: which named pattern fits?
107
+ (app shell, settings, dashboard, master-detail, wizard, command, form dialog)
108
+ - Look up in RECIPES.md: which combos apply?
109
+ (status pill, bulk action, filter toolbar, the three states, etc.)
110
+
111
+ 3. SELECT COMPONENTS
112
+ - Use COMPONENTS_INDEX.md decision tables for "which component?"
113
+ - Load ONE doc per component you'll use
114
+
115
+ 4. COMPOSE
116
+ - Apply the surface rule (2+1)
117
+ - Apply the state matrix (every interactive surface, all required states)
118
+ - Apply the writing rules (every label, button, error, empty state)
119
+ - Apply motion sparingly (default: none)
120
+
121
+ 5. SELF-REVIEW (the 30-second Linear designer test)
122
+ - Hierarchy clear within 2 seconds?
123
+ - All states designed?
124
+ - Copy specific, not generic?
125
+ - One primary action per surface?
126
+ - Spacing varied (not monotonous)?
127
+ - Cards used only for bounded topics?
128
+ - Focus styles on every interactive element?
129
+ - No decorative shadows on flat elements?
130
+ If you can name 2+ failures → fix them BEFORE asking the user.
131
+
132
+ 6. SHIP
133
+ - The code should be readable cold by a senior engineer.
134
+ - No commentary on what you did — the diff speaks for itself.
135
+ ```
136
+
137
+ **Skipping any step produces AI-flavored work.** The protocol is the difference between "looks like AI made it" and "looks like a real product."
138
+
139
+ ---
140
+
141
+ ## When the user asks for something the kit doesn't cover
142
+
143
+ 1. **First, double-check.** Most requests map to an existing recipe or component. Don't reinvent.
144
+ 2. **If genuinely missing:** use the closest existing pattern as a base, extend it minimally. Flag to the user that you extended (so they can decide if it's worth adding to the kit).
145
+ 3. **Never reach outside the kit** for cosmetic reasons (custom CSS for a "fancier" button, hand-written animations, decorative gradients). The kit has constraints by design; respecting them is what makes output consistent.
146
+
147
+ ---
148
+
149
+ ## Quality bar
150
+
151
+ Output meets the bar when:
152
+
153
+ - A senior Linear designer reviewing your diff would have **zero** of these reactions:
154
+ - "Why is this nested 4 levels deep?"
155
+ - "Where's the empty state?"
156
+ - "Why does the button say 'Submit'?"
157
+ - "Why is there a shadow on a flat card?"
158
+ - "Why are there two primary buttons?"
159
+ - "This looks like every AI dashboard."
160
+
161
+ - A senior engineer reviewing your code would have zero of these reactions:
162
+ - "Why is there custom CSS when a component does this?"
163
+ - "Why is there a `<div>` where `<Item>` belongs?"
164
+ - "Why is this not using the design tokens?"
165
+ - "This breaks dark mode."
166
+
167
+ If any of those reactions are possible, the work isn't done.
168
+
169
+ ---
170
+
171
+ ## What to do when you're uncertain
172
+
173
+ - **Composition uncertain** → re-read `nqui-composition/SKILL.md`
174
+ - **Pattern uncertain** → re-read `COMPOSITION.md` (named patterns)
175
+ - **Combo uncertain** → re-read `RECIPES.md`
176
+ - **Component choice uncertain** → re-read `COMPONENTS_INDEX.md` decision tables
177
+ - **State coverage uncertain** → re-read `STATES.md` matrix
178
+ - **Copy uncertain** → re-read `WRITING.md`
179
+ - **Motion uncertain** → re-read `MOTION.md` (default: no motion)
180
+ - **Surface uncertain** → re-read `ELEVATION.md` (default: 2 surfaces, spacing for the rest)
181
+
182
+ If you're uncertain and the docs don't resolve it, **ask the user** — do not guess. Guessing produces inconsistency across the kit's output.
183
+
184
+ ---
185
+
186
+ ## Closing rule
187
+
188
+ The kit's promise to its consumers is: **"AI built with nqui produces work that's reliably 8/10 or better, regardless of which agent or which prompt."** Your job as the agent is to make that promise true on every request.
189
+
190
+ Restraint, not ambition. Recipes, not invention. Specifics, not vagueness.
@@ -9,7 +9,7 @@ description: Token-efficient map for nqui per-component docs. Use BEFORE opening
9
9
 
10
10
  1. **Pick one doc:** `nqui-<kebab-case>.md` for the component you are implementing (e.g. `nqui-toggle-group.md`). In this repo use **`docs/components/`**; after `init-skills` use **`.cursor/nqui-skills/components/`**.
11
11
  2. **Unsure which component?** Skim **`README.md`** in that folder only for **“When to Use”** and the category table — or use **[HUMAN_GUIDE.md](./HUMAN_GUIDE.md)** (task → docs) or the **area → filenames** list below.
12
- 3. **Deep selection logic** (toolbar vs form, ToggleGroup vs RadioGroup): **`nqui-components/SKILL.md`** and **`nqui-design-system/SKILL.md`**.
12
+ 3. **Deep selection logic** (toolbar vs form, ToggleGroup vs RadioGroup): **`nqui-components/SKILL.md`** and **`nqui-design-system/SKILL.md`**. **Bounded data tables + ScrollArea:** **`nqui-data-tables/SKILL.md`** + **`nqui-scroll-area.md`**.
13
13
 
14
14
  ## Paths
15
15
 
@@ -45,4 +45,54 @@ Suffix every name with `.md` in the components folder you are using (`docs/compo
45
45
 
46
46
  ---
47
47
 
48
+ ---
49
+
50
+ ## Common "which component?" decisions
51
+
52
+ Use these before opening a doc. If the answer is clear, load just that one file.
53
+
54
+ ### Selection input
55
+ | Situation | Use |
56
+ |-----------|-----|
57
+ | ≤6 options, known at build time, no search | `nqui-select` |
58
+ | Many options OR need search/filter | `nqui-combobox` |
59
+ | Inside a native form, progressive enhancement needed | `nqui-native-select` |
60
+ | Inline toolbar mode (list/grid, linear/log) | `nqui-toggle-group` (never RadioGroup here) |
61
+ | Stacked form options with descriptions | `nqui-radio-group` |
62
+
63
+ ### Overlay / panel
64
+ | Situation | Use |
65
+ |-----------|-----|
66
+ | Focused task requiring input (create, edit) | `nqui-dialog` |
67
+ | Destructive action confirmation | `nqui-alert-dialog` |
68
+ | Side panel with details or filter controls | `nqui-sheet` |
69
+ | Mobile-first or gesture-heavy panel | `nqui-drawer` |
70
+ | Quick contextual info on click | `nqui-popover` |
71
+ | Quick info on hover (non-critical) | `nqui-hover-card` |
72
+ | Tiny label for icon-only controls | `nqui-tooltip` |
73
+
74
+ ### Data display
75
+ | Situation | Use |
76
+ |-----------|-----|
77
+ | Static rows, no sorting/filtering | `nqui-table` |
78
+ | Sortable/filterable/paginated data | `nqui-data-table` (TanStack) |
79
+ | Key-value list, menu-style rows | `nqui-item` |
80
+ | Inline status tags | `nqui-badge` |
81
+ | Empty state (no data yet) | `nqui-empty` |
82
+ | Loading placeholder | `nqui-skeleton` |
83
+ | Inline feedback (warning, error, info) | `nqui-alert` |
84
+ | Transient notification | `nqui-toaster` (sonner) |
85
+
86
+ ### Navigation
87
+ | Situation | Use |
88
+ |-----------|-----|
89
+ | Primary app sections | `nqui-sidebar` |
90
+ | Sub-views within a page | `nqui-tabs` |
91
+ | Page location trail | `nqui-breadcrumb` |
92
+ | Actions on right-click | `nqui-context-menu` |
93
+ | Actions on a trigger button | `nqui-dropdown-menu` |
94
+ | Power-user global search / actions | `nqui-command-palette` |
95
+
96
+ ---
97
+
48
98
  Full index + long-form guidance: `README.md` next to those files (large — use sections, not whole-file dump).
@@ -0,0 +1,321 @@
1
+ # nqui — composition (putting UI together)
2
+
3
+ Use this after **HUMAN_GUIDE.md** (task routing) and before opening many `nqui-*.md` files. Components are documented individually; this file explains **how to assemble them** into calm, product-quality screens.
4
+
5
+ ## Named Layout Patterns
6
+
7
+ Pick the pattern that matches the user’s job before choosing components. These are blueprints, not templates — adapt them.
8
+
9
+ ### App Shell (sidebar + content)
10
+ **When:** Multi-section app, persistent navigation, route-based views.
11
+ ```
12
+ SidebarProvider
13
+ Sidebar (collapsible, h-full)
14
+ SidebarHeader (h-12, branding + workspace picker)
15
+ SidebarContent (nav groups, ScrollArea)
16
+ SidebarFooter (user avatar + menu)
17
+ main (flex flex-col, overflow-y-auto)
18
+ sticky header (h-12, breadcrumb + page actions)
19
+ page content (p-6, max-w-screen-xl)
20
+ ```
21
+ Components: `Sidebar`, `SidebarProvider`, `Breadcrumb`, `DropdownMenu` for workspace/user.
22
+
23
+ ### Settings Page
24
+ **When:** User-controlled preferences, profile, account — organized by topic.
25
+ ```
26
+ Page header (h1 + description, border-b)
27
+ Two-column: nav (sticky, ScrollArea) | form area
28
+ FieldSet per section (border-b, pb-8)
29
+ FieldLegend (section title)
30
+ FieldGroup (max-w-lg, flex flex-col gap-4)
31
+ Field (label + input + description)
32
+ Footer row (Save + Cancel buttons)
33
+ ```
34
+ Components: `FieldSet`, `FieldGroup`, `Field`, `Input`, `Select`, `Switch`, `Button`. Nav uses `Tabs` (vertical) or plain `nav` with `Item`.
35
+
36
+ ### Dashboard (metrics + table/chart)
37
+ **When:** Overview screen, monitoring, analytics — data-first.
38
+ ```
39
+ Page header (h1 + date range picker + export button)
40
+ Metric row (3-4 Cards, key numbers only — real data, not decorative)
41
+ Primary region: DataTable or Chart (Card, full-width)
42
+ Optional secondary: filter Sheet or side panel (Resizable)
43
+ ```
44
+ Anti-pattern: fake KPI cards, hero metric layout, card grids of every component. Use `Empty` when data is absent.
45
+ Components: `Card`, `DataTable`, `Select`/`Combobox` for filters, `Sheet` for filter panel, `Skeleton` for loading.
46
+
47
+ ### Master-Detail Split
48
+ **When:** List of items + contextual detail (email, files, tickets, settings rows).
49
+ ```
50
+ Resizable (direction="horizontal", defaultSize=[35, 65])
51
+ ResizablePanel: item list (ScrollArea, Item components)
52
+ ResizableHandle ← provides the visible divider
53
+ ResizablePanel: detail view (sticky header + ScrollArea content)
54
+ ```
55
+ Components: `Resizable`, `Item`, `ScrollArea`, `Breadcrumb` in detail header.
56
+
57
+ **CSS-flex fallback** (when `Resizable` won't size inside `overflow-y-auto` scroll containers):
58
+ ```tsx
59
+ <div className="flex flex-1 min-h-0 overflow-hidden bg-muted/20">
60
+ <div className="flex flex-col border-r border-border bg-background"
61
+ style={{ width: 260, flexShrink: 0, overflow: "hidden" }}>
62
+ {/* list panel */}
63
+ </div>
64
+ <div className="flex-1 min-w-0 overflow-hidden bg-background">
65
+ {/* detail panel */}
66
+ </div>
67
+ </div>
68
+ ```
69
+
70
+ **Required for both versions — visual separation rules (these are mandatory, not aesthetic):**
71
+
72
+ 1. **Three-layer divider**, not just `border-r`:
73
+ - Panels: `bg-background` (or a flat panel color)
74
+ - Outer wrapper: `bg-muted/20` (slightly darker) — peeks through as a subtle gutter
75
+ - Border: `border-r border-border` on the list panel
76
+ - The combination of border + background contrast is what reads as "separated." A lone `border-r` against same-color panels reads as "overlapping" — especially in dark mode where borders are subtle.
77
+
78
+ 2. **List items must use minimal chrome.** In a narrow list panel:
79
+ - Use `Item variant="ghost"` (or pass `variant` such that border is transparent) — never `outline` or `muted`
80
+ - Selected state: `bg-accent/70`, not a bordered/elevated card
81
+ - Hover: `hover:bg-muted/40` only
82
+ - Bordered/card-style items in a 260px panel look like they bleed into the detail panel even when text-truncation is correct.
83
+
84
+ 3. **Both panels need `overflow: hidden`** on their root container. Without this, item backgrounds and focus rings can paint past the panel boundary.
85
+
86
+ 4. **Detail content needs `max-w-2xl` and `p-6`** (or similar) so it doesn't hug the divider — breathing room from the boundary reinforces separation.
87
+
88
+ 5. **Don't put borders on list items AND between panels.** Pick one source of separation per axis. Bordered items + bordered panels = visual noise that reads as overlap.
89
+
90
+ **Responsive: switch to Sheet below ~900px container width (mandatory).**
91
+
92
+ A 260px list + flex-1 detail split below ~900px leaves the detail under ~640px — metadata rows wrap, description loses reading flow, the layout feels squeezed. **Don't try to shrink both panels.** Switch the detail to a Sheet instead.
93
+
94
+ ```tsx
95
+ // Measure the master-detail container, not the viewport.
96
+ // The viewport includes sidebars; what matters is space available to the split.
97
+ const [splitRef, splitWidth] = useContainerWidth<HTMLDivElement>()
98
+ const isCompact = splitWidth > 0 && splitWidth < 900
99
+
100
+ <div ref={splitRef} className="flex flex-1 min-h-0 overflow-hidden bg-muted/20">
101
+ {/* List: full-width in compact, 260px sidebar when split */}
102
+ <div style={{ width: isCompact ? "100%" : 260, flexShrink: 0 }}>
103
+ {/* items */}
104
+ </div>
105
+ {/* Detail: inline panel only when wide */}
106
+ {!isCompact && <div className="flex-1 min-w-0">{detail}</div>}
107
+ </div>
108
+
109
+ {/* Detail: Sheet when compact, driven off the same selectedId */}
110
+ {isCompact && (
111
+ <Sheet open={selected !== null} onOpenChange={(o) => !o && setSelected(null)}>
112
+ <SheetContent side="right" className="w-full sm:max-w-lg p-0 flex flex-col gap-0">
113
+ <SheetHeader className="sr-only">
114
+ <SheetTitle>{selected?.title}</SheetTitle>
115
+ </SheetHeader>
116
+ {selected && <DetailContent item={selected} />}
117
+ </SheetContent>
118
+ </Sheet>
119
+ )}
120
+ ```
121
+
122
+ **Why container width, not `useIsMobile`/viewport:** the available split width depends on sidebars (primary sidebar, TOC sidebar, collapsible filter panels). A 1400px viewport with two open sidebars can still leave you under 900px — at which point Sheet is correct. Measure the container with `ResizeObserver`, not the window.
123
+
124
+ **Reusable hook** (drop into your project — does not require a library):
125
+
126
+ ```tsx
127
+ function useContainerWidth<T extends HTMLElement>(): [React.RefObject<T | null>, number] {
128
+ const ref = React.useRef<T>(null)
129
+ const [width, setWidth] = React.useState(0)
130
+ React.useEffect(() => {
131
+ const el = ref.current
132
+ if (!el) return
133
+ const ro = new ResizeObserver((entries) => {
134
+ const entry = entries[0]
135
+ if (entry) setWidth(entry.contentRect.width)
136
+ })
137
+ ro.observe(el)
138
+ setWidth(el.getBoundingClientRect().width)
139
+ return () => ro.disconnect()
140
+ }, [])
141
+ return [ref, width]
142
+ }
143
+ ```
144
+
145
+ **Selection state lives on the parent**, not inside either panel. Same `selectedId` state drives both the inline detail render and the Sheet's `open` prop. Don't duplicate state across modes.
146
+
147
+ **Reference: Linear, GitHub Issues, Things 3** all use this exact pattern. Sheet → split is the standard transition, not split → smaller-split.
148
+
149
+ ### Wizard / Stepper
150
+ **When:** Multi-step flow with mandatory sequence (onboarding, checkout, ATP procedure).
151
+ ```
152
+ Page layout (centered, max-w-lg)
153
+ Progress indicator (step count or Progress bar)
154
+ Card (current step content)
155
+ CardHeader (step title + description)
156
+ CardContent (form fields or instructions)
157
+ CardFooter (Back + Continue buttons, primary right)
158
+ ```
159
+ **Rule:** One primary action per step (Continue/Submit). Back is always `variant="outline"`. Never skip validation before advancing.
160
+ Components: `Card`, `Progress`, `Button`, `Field`, `Alert` for step errors.
161
+
162
+ ### Command / Search Interface
163
+ **When:** Power-user navigation, global search, quick actions (Cmd+K pattern).
164
+ ```
165
+ CommandPalette (modal, triggered by keyboard shortcut)
166
+ CommandInput (search)
167
+ CommandList (ScrollArea)
168
+ CommandGroup per category
169
+ CommandItem (icon + label + shortcut)
170
+ ```
171
+ Components: `CommandPalette`, `Command`, `CommandInput`, `CommandGroup`, `CommandItem`, `Kbd` for shortcuts.
172
+
173
+ ### Form Dialog
174
+ **When:** Inline creation or editing without leaving the page (create record, edit field).
175
+ ```
176
+ Dialog (not AlertDialog — that’s for destructive confirms only)
177
+ DialogHeader (DialogTitle required, DialogDescription optional)
178
+ DialogContent (FieldGroup, max-w-sm or md)
179
+ DialogFooter (Cancel outline + Submit primary)
180
+ ```
181
+ **Rule:** Keep forms short. >5 fields → use Sheet or a dedicated page. Always include `DialogTitle` (use `sr-only` if visually hidden).
182
+ Components: `Dialog`, `Field`, `Input`, `Select`, `Button`.
183
+
184
+ ---
185
+
186
+ ## Three modes (don’t mix them on one page)
187
+
188
+ | Mode | Question it answers | Where in the dev app |
189
+ |------|---------------------|----------------------|
190
+ | **Recipes** | How do I build a real screen? | `/`, `/patterns`, `/recipes/settings` |
191
+ | **Catalog** | What props/variants exist? | `/catalog` (+ Storybook) |
192
+ | **Tokens** | What are the colors, radius, type? | `/design-system` |
193
+
194
+ **Rule:** Product work starts in **Recipes**. Use **Catalog** to look up a single component. Use **Tokens** when theming.
195
+
196
+ ## Start from the user’s job
197
+
198
+ ```text
199
+ 1. What is the one outcome on this screen?
200
+ 2. What is the single primary action?
201
+ 3. What can move to a secondary surface (tab, sheet, another route)?
202
+ 4. Only then: pick components (HUMAN_GUIDE → one nqui-*.md).
203
+ ```
204
+
205
+ ## The “three surfaces” cap
206
+
207
+ Per viewport, avoid more than **three** competing regions:
208
+
209
+ 1. **Chrome** — sidebar + page header (`h-12`, breadcrumbs).
210
+ 2. **Primary** — table, form, editor, or dashboard focus.
211
+ 3. **Secondary** — filters, TOC, inspector (collapse on small screens).
212
+
213
+ If everything is a Card, you already have too many surfaces.
214
+
215
+ ## When to use a Card
216
+
217
+ | Use Card | Don’t use Card |
218
+ |----------|----------------|
219
+ | One bounded topic (“Shipping”, “Team members”) | Every control in a grid |
220
+ | A chart or table block on a dashboard | Toolbar buttons |
221
+ | Settings group with footer actions | Page-level layout |
222
+
223
+ Prefer **sections** (`border-b`, `gap-6`, `h2`) for page structure. One Card per topic, not per widget.
224
+
225
+ ## Selection vs action (quick)
226
+
227
+ | Job | Component |
228
+ |-----|-----------|
229
+ | Toolbar mode (list/grid, linear/log) | **ToggleGroup** `single` |
230
+ | Multi format (bold/italic) | **ToggleGroup** `multiple` |
231
+ | Commands (save, export) | **Button** / **ButtonGroup** |
232
+ | Form choice (plan, role) | **RadioGroup** or **Select** |
233
+ | Searchable list | **Combobox** |
234
+ | Settings on/off | **Switch** |
235
+
236
+ **RadioGroup in a horizontal toolbar = wrong default** (OK in catalog to show variants).
237
+
238
+ ## Forms recipe
239
+
240
+ ```tsx
241
+ <FieldSet>
242
+ <FieldLegend>Profile</FieldLegend>
243
+ <FieldGroup className="flex flex-col gap-4 max-w-lg">
244
+ <Field>
245
+ <FieldLabel htmlFor="name">Name</FieldLabel>
246
+ <Input id="name" />
247
+ <FieldDescription>Shown on invoices.</FieldDescription>
248
+ </Field>
249
+ {/* … */}
250
+ </FieldGroup>
251
+ </FieldSet>
252
+ <div className="flex gap-2 pt-4">
253
+ <Button type="submit">Save</Button>
254
+ <Button type="button" variant="outline">Cancel</Button>
255
+ </div>
256
+ ```
257
+
258
+ Use **FieldGroup + Field**, not raw `div` + `space-y-*`. See `rules/forms.md`.
259
+
260
+ ## Toolbar recipe (context-first)
261
+
262
+ ```tsx
263
+ <div className="rounded-lg border border-input bg-muted/30 p-3">
264
+ <div className="flex flex-wrap items-center gap-1">
265
+ <ToggleGroup type="multiple" …>…</ToggleGroup>
266
+ <Separator orientation="vertical" className="mx-2 h-5" />
267
+ <ToggleGroup type="single" …>…</ToggleGroup>
268
+ </div>
269
+ <div className="mt-3 rounded border border-input/50 bg-background px-3 py-2 text-sm text-muted-foreground">
270
+ {/* content the toolbar affects */}
271
+ </div>
272
+ </div>
273
+ ```
274
+
275
+ Reference: `/catalog` → Toggle & ToggleGroup, or `/patterns` overview.
276
+
277
+ ## Page shell recipe
278
+
279
+ ```text
280
+ SidebarProvider
281
+ sticky header (h-12, --z-sticky-page)
282
+ main (#main)
283
+ h1 + short description
284
+ optional Alert (one per page)
285
+ primary region (tabs | form | table)
286
+ ```
287
+
288
+ Scroll: custom main scroll container — see `README.md` → Layout & Scroll Patterns.
289
+
290
+ ## Anti-patterns (busy UI)
291
+
292
+ | Anti-pattern | Fix |
293
+ |--------------|-----|
294
+ | Card grid of every component | Catalog route or Storybook |
295
+ | Fake KPI cards (12,345 users) on a reference page | Real metrics only in product demos |
296
+ | Mail-app nav for a component library | Task-based nav (Recipes / Catalog) |
297
+ | Command palette items that don’t navigate | Wire only real routes |
298
+ | `space-y-*` on flex layouts | `flex flex-col gap-*` |
299
+ | Tooltip as only label for required fields | Visible `FieldLabel` |
300
+ | Nested Cards | Section + one Card, or `calc(radius)` nesting rules |
301
+ | Raw `bg-blue-500` chrome | Semantic tokens |
302
+
303
+ ## Pre-ship checklist
304
+
305
+ - [ ] One clear user goal and primary action
306
+ - [ ] ≤3 major surfaces on screen
307
+ - [ ] Toolbars in context (`bg-muted/30`), not isolated in Cards
308
+ - [ ] Forms use FieldSet + FieldGroup
309
+ - [ ] Cards only for real topics
310
+ - [ ] States: Skeleton / Empty / Alert (not placeholder lorem boxes)
311
+ - [ ] Z-index from `elevation.css` only
312
+
313
+ ## Dev app map
314
+
315
+ | Route | Recipe |
316
+ |-------|--------|
317
+ | `/` | Hub — start here |
318
+ | `/patterns` | Commerce dashboard (dense product UI) |
319
+ | `/recipes/settings` | Workspace settings (forms) |
320
+ | `/catalog` | Full variant catalog |
321
+ | `/design-system` | Tokens |