@nqlib/nqui 0.4.3 → 0.4.5

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 (156) hide show
  1. package/INSTALLATION.md +234 -0
  2. package/README.md +109 -151
  3. package/dist/button-CJHdCq9I.js +155 -0
  4. package/dist/button-R304rhsj.cjs +1 -0
  5. package/dist/calendar.cjs.js +1 -1
  6. package/dist/calendar.es.js +1 -1
  7. package/dist/carousel-D1FMVglR.cjs +1 -0
  8. package/dist/carousel-U7RZhYZj.js +179 -0
  9. package/dist/carousel.cjs.js +1 -1
  10. package/dist/carousel.es.js +1 -1
  11. package/dist/command-palette-DCtLpM3Q.js +694 -0
  12. package/dist/command-palette-MHc03bBf.cjs +5 -0
  13. package/dist/command.cjs.js +1 -1
  14. package/dist/command.es.js +1 -1
  15. package/dist/components/custom/color-picker.d.ts +1 -1
  16. package/dist/components/custom/color-picker.d.ts.map +1 -1
  17. package/dist/components/custom/color-slider.d.ts +4 -10
  18. package/dist/components/custom/color-slider.d.ts.map +1 -1
  19. package/dist/components/custom/enhanced-radio-group.d.ts +13 -4
  20. package/dist/components/custom/enhanced-radio-group.d.ts.map +1 -1
  21. package/dist/components/custom/enhanced-tabs.d.ts.map +1 -1
  22. package/dist/components/debug/debug-features.d.ts +29 -0
  23. package/dist/components/debug/debug-features.d.ts.map +1 -0
  24. package/dist/components/debug/debug-panel.d.ts.map +1 -1
  25. package/dist/components/error-boundary.d.ts +20 -0
  26. package/dist/components/error-boundary.d.ts.map +1 -0
  27. package/dist/components/index.d.ts +103 -0
  28. package/dist/components/index.d.ts.map +1 -0
  29. package/dist/components/ui/badge.d.ts +16 -5
  30. package/dist/components/ui/badge.d.ts.map +1 -1
  31. package/dist/components/ui/button.d.ts +38 -4
  32. package/dist/components/ui/button.d.ts.map +1 -1
  33. package/dist/components/ui/checkbox.d.ts +16 -2
  34. package/dist/components/ui/checkbox.d.ts.map +1 -1
  35. package/dist/components/ui/combobox.d.ts +2 -1
  36. package/dist/components/ui/combobox.d.ts.map +1 -1
  37. package/dist/components/ui/frosted-glass.d.ts.map +1 -1
  38. package/dist/components/ui/input-group.d.ts +1 -1
  39. package/dist/components/ui/input-group.d.ts.map +1 -1
  40. package/dist/components/ui/pagination.d.ts +3 -2
  41. package/dist/components/ui/pagination.d.ts.map +1 -1
  42. package/dist/components/ui/radio-group.d.ts +3 -1
  43. package/dist/components/ui/radio-group.d.ts.map +1 -1
  44. package/dist/components/ui/select.d.ts +6 -1
  45. package/dist/components/ui/select.d.ts.map +1 -1
  46. package/dist/components/ui/sidebar.d.ts +1 -1
  47. package/dist/components/ui/sidebar.d.ts.map +1 -1
  48. package/dist/components/ui/slider.d.ts +10 -2
  49. package/dist/components/ui/slider.d.ts.map +1 -1
  50. package/dist/components/ui/sonner.d.ts +18 -2
  51. package/dist/components/ui/sonner.d.ts.map +1 -1
  52. package/dist/components/ui/spinner.d.ts +2 -1
  53. package/dist/components/ui/spinner.d.ts.map +1 -1
  54. package/dist/components/ui/switch.d.ts +15 -2
  55. package/dist/components/ui/switch.d.ts.map +1 -1
  56. package/dist/components/ui/tabs.d.ts +1 -1
  57. package/dist/components/ui/tabs.d.ts.map +1 -1
  58. package/dist/components/ui/toggle.d.ts +1 -1
  59. package/dist/components/ui/toggle.d.ts.map +1 -1
  60. package/dist/debug-panel-CNKk-No5.cjs +75 -0
  61. package/dist/debug-panel-pg39-6xw.js +9011 -0
  62. package/dist/debug.cjs.js +1 -0
  63. package/dist/debug.es.js +7 -0
  64. package/dist/{drawer-CU4lkcz7.js → drawer-DO26uhym.js} +31 -31
  65. package/dist/drawer-DVarEy65.cjs +1 -0
  66. package/dist/drawer.cjs.js +1 -1
  67. package/dist/drawer.es.js +1 -1
  68. package/dist/{enhanced-calendar-BENbxw7_.js → enhanced-calendar-BGlsSYJd.js} +1 -1
  69. package/dist/{enhanced-calendar-5PA8CeF7.cjs → enhanced-calendar-C7EQIr6i.cjs} +1 -1
  70. package/dist/entries/debug.d.ts +14 -0
  71. package/dist/entries/debug.d.ts.map +1 -0
  72. package/dist/entries/sonner.d.ts +1 -2
  73. package/dist/entries/sonner.d.ts.map +1 -1
  74. package/dist/hooks/use-mobile.d.ts.map +1 -1
  75. package/dist/hooks/use-scroll-spy.d.ts.map +1 -1
  76. package/dist/index-CI756mSv.cjs +41 -0
  77. package/dist/index-CgfzsUO6.js +1069 -0
  78. package/dist/index.d.ts +2 -98
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/lib/index.d.ts +9 -0
  81. package/dist/lib/index.d.ts.map +1 -0
  82. package/dist/lib/wrap-inline-label-text.d.ts +7 -0
  83. package/dist/lib/wrap-inline-label-text.d.ts.map +1 -0
  84. package/dist/nqui.cjs.js +49 -245
  85. package/dist/nqui.es.js +7402 -16735
  86. package/dist/sonner-CpmECDBk.js +179 -0
  87. package/dist/sonner-nE9hIalJ.cjs +48 -0
  88. package/dist/sonner.cjs.js +1 -1
  89. package/dist/sonner.es.js +3 -2
  90. package/dist/styles.css +237 -10
  91. package/docs/components/README.md +109 -10
  92. package/docs/components/nqui-badge.md +1 -0
  93. package/docs/components/nqui-button.md +3 -1
  94. package/docs/components/nqui-card.md +8 -0
  95. package/docs/components/nqui-carousel.md +6 -0
  96. package/docs/components/nqui-checkbox.md +38 -1
  97. package/docs/components/nqui-color-slider.md +5 -3
  98. package/docs/components/nqui-combobox.md +58 -37
  99. package/docs/components/nqui-drawer.md +1 -1
  100. package/docs/components/nqui-frosted-glass.md +83 -5
  101. package/docs/components/nqui-radio-group.md +47 -2
  102. package/docs/components/nqui-scroll-area.md +1 -1
  103. package/docs/components/nqui-select.md +2 -2
  104. package/docs/components/nqui-sheet.md +1 -1
  105. package/docs/components/nqui-slider.md +13 -0
  106. package/docs/components/nqui-spinner.md +6 -1
  107. package/docs/components/nqui-switch.md +23 -1
  108. package/docs/components/nqui-tabs.md +11 -1
  109. package/docs/components/nqui-toaster.md +5 -1
  110. package/docs/internal-notes/PUBLISHING.md +46 -4
  111. package/docs/nqui-skills/SKILL.md +106 -0
  112. package/docs/nqui-skills/design-system.md +143 -0
  113. package/docs/nqui-skills/rules/composition.md +183 -0
  114. package/docs/nqui-skills/rules/forms.md +190 -0
  115. package/docs/nqui-skills/rules/icons.md +158 -0
  116. package/docs/nqui-skills/rules/styling.md +192 -0
  117. package/package.json +23 -12
  118. package/scripts/build-styles.js +16 -0
  119. package/scripts/cli.js +1 -0
  120. package/scripts/download-skills.js +91 -0
  121. package/scripts/examples/nextjs-layout-sidebar.tsx +100 -0
  122. package/scripts/examples/nextjs-page-sidebar.tsx +81 -0
  123. package/scripts/examples/vite-app.tsx +135 -0
  124. package/scripts/examples/vite-main.tsx +17 -0
  125. package/scripts/examples.js +92 -6
  126. package/scripts/generate-docs.js +169 -0
  127. package/scripts/init-css.js +34 -14
  128. package/scripts/init-cursor.js +8 -0
  129. package/scripts/init-debug-css.js +4 -2
  130. package/scripts/post-install.js +41 -9
  131. package/scripts/publish-npmjs.js +17 -3
  132. package/scripts/resolve-target-dir.js +20 -1
  133. package/scripts/setup-helper.js +13 -1
  134. package/scripts/verify-build.js +1 -1
  135. package/scripts/wizard.js +12 -7
  136. package/dist/button-CYFTFDKe.cjs +0 -1
  137. package/dist/button-nJvDl3w8.js +0 -44
  138. package/dist/carousel-DEyyJi49.js +0 -179
  139. package/dist/carousel-Dhhz8m5V.cjs +0 -1
  140. package/dist/command-palette-UHk8zZOg.cjs +0 -45
  141. package/dist/command-palette-d-TrdBsM.js +0 -1778
  142. package/dist/components/custom/enhanced-badge.d.ts +0 -33
  143. package/dist/components/custom/enhanced-badge.d.ts.map +0 -1
  144. package/dist/components/custom/enhanced-button.d.ts +0 -34
  145. package/dist/components/custom/enhanced-button.d.ts.map +0 -1
  146. package/dist/components/custom/enhanced-checkbox.d.ts +0 -28
  147. package/dist/components/custom/enhanced-checkbox.d.ts.map +0 -1
  148. package/dist/components/custom/enhanced-combobox.d.ts +0 -35
  149. package/dist/components/custom/enhanced-combobox.d.ts.map +0 -1
  150. package/dist/components/custom/enhanced-select.d.ts +0 -30
  151. package/dist/components/custom/enhanced-select.d.ts.map +0 -1
  152. package/dist/components/custom/enhanced-sonner.d.ts +0 -16
  153. package/dist/components/custom/enhanced-sonner.d.ts.map +0 -1
  154. package/dist/drawer-BcIxWRN8.cjs +0 -1
  155. package/dist/sonner-Co6YpYVs.js +0 -546
  156. package/dist/sonner-DbQhVp8m.cjs +0 -330
@@ -1,6 +1,6 @@
1
1
  # nqui Spinner
2
2
 
3
- > Loading spinner.
3
+ > Dual-arc loader (primary **`linearGradient`** strokes, rotating shell). Keyframes live in **`packages/nqui/src/index.css`** (`.nqui-spinner-*`).
4
4
 
5
5
  ## Import
6
6
 
@@ -14,3 +14,8 @@ import { Spinner } from "@nqlib/nqui"
14
14
  <Spinner />
15
15
  <Spinner className="size-6" />
16
16
  ```
17
+
18
+ ## Notes
19
+
20
+ - Root is a `div` with `role="status"` and `aria-label="Loading"`.
21
+ - Strokes use `var(--primary)` and `color-mix(in oklch, var(--primary) …, white)` so light/dark themes stay on-brand.
@@ -1,6 +1,6 @@
1
1
  # nqui Switch
2
2
 
3
- > Toggle switch. Core Radix.
3
+ > Toggle switch. **Radix** primitive with **size** variants and **capsule thumb** (white, shadow). Sizes align with control scale: `sm` / `default` / `lg`.
4
4
 
5
5
  ## Import
6
6
 
@@ -13,3 +13,25 @@ import { Switch } from "@nqlib/nqui"
13
13
  ```tsx
14
14
  <Switch checked={checked} onCheckedChange={setChecked} />
15
15
  ```
16
+
17
+ ## Sizes
18
+
19
+ ```tsx
20
+ <Switch size="sm" />
21
+ <Switch size="default" />
22
+ <Switch size="lg" />
23
+ ```
24
+
25
+ ## Hit area
26
+
27
+ The switch root has no `::before` styling, so you can add [hit-area utilities](https://bazza.dev/craft/2026/hit-area) on the same element:
28
+
29
+ ```tsx
30
+ <Switch className="hit-area-4" id="notifications" />
31
+ ```
32
+
33
+ Use sparingly in dense toolbars so expanded targets do not overlap.
34
+
35
+ ## Notes
36
+
37
+ - Thumb travel and track sizes are fixed per `size`; overriding height on the root without matching thumb classes may look off.
@@ -31,5 +31,15 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@nqlib/nqui"
31
31
 
32
32
  ## Notes
33
33
 
34
- - Sliding indicator needs `position: relative` on TabsList container.
34
+ - **Sliding indicator:** Requires `position: relative` on TabsList container (handled automatically by the component).
35
+ - **Responsive behavior:** On small screens, TabsList can overflow horizontally. Use `overflow-x-auto` on parent if needed.
36
+ - **Container requirements:** For the sliding indicator to work correctly, the TabsList container needs:
37
+ ```css
38
+ .sliding-indicator-container {
39
+ position: relative;
40
+ overflow: hidden;
41
+ min-width: 0;
42
+ }
43
+ ```
44
+ - **Resize handling:** The indicator recalculates position on window resize (built-in).
35
45
  - Use `EnhancedTabsList` props for additional customization.
@@ -1,6 +1,6 @@
1
1
  # nqui Toaster
2
2
 
3
- > Toast notifications (sonner). Semantic types, promise pattern.
3
+ > Toast notifications via **Sonner**. Default toast is **pill-shaped**; default **normal** toast uses **inverted** surface (dark fill in light theme, light fill in dark theme) using `--foreground` / `--background`. Implemented in **`packages/nqui/src/components/ui/sonner.tsx`** (`Toaster`). Compatibility aliases: `EnhancedSonner`, `CoreToaster` → same component.
4
4
 
5
5
  ## Import
6
6
 
@@ -42,3 +42,7 @@ toast.promise(fetchData(), {
42
42
  toast.success("Msg", { duration: 5000 })
43
43
  // Toaster: position, richColors, etc.
44
44
  ```
45
+
46
+ ## Notes
47
+
48
+ - **Subpath import:** `@nqlib/nqui/sonner` re-exports the same `Toaster`.
@@ -230,15 +230,57 @@ If a version had bugs (e.g. init-cursor routed to wrong script, skills not worki
230
230
  npm deprecate @nqlib/nqui@0.4.1 "Use 0.4.2 instead. See changelog."
231
231
  ```
232
232
 
233
- 4. **Unpublish** (rare, npm restrictions apply):
234
- - Must be within 72 hours of publish
235
- - No other packages can depend on it
236
- - `npm unpublish @nqlib/nqui@0.4.1 --force`
233
+ 4. **Unpublish** (rare, npm restrictions apply): See [Unpublishing](#unpublishing) below.
237
234
 
238
235
  **Recommendation:** Usually deprecate + publish patch. Unpublish only for security issues.
239
236
 
240
237
  ---
241
238
 
239
+ ## Unpublishing
240
+
241
+ ### npmjs.com
242
+
243
+ **Rules:**
244
+ - **Within 72 hours of publish**: You can unpublish. The version is then available for re-publish.
245
+ - **After 72 hours**: Unpublish is disallowed. Versions are permanently locked. Use deprecation instead.
246
+
247
+ **Unpublish a specific version** (within 72 hours):
248
+ ```bash
249
+ cd packages/nqui
250
+ npm unpublish @nqlib/nqui@0.4.1 --force --registry=https://registry.npmjs.com
251
+ ```
252
+
253
+ **Unpublish entire package** (within 72 hours, removes all versions):
254
+ ```bash
255
+ npm unpublish @nqlib/nqui --force --registry=https://registry.npmjs.com
256
+ ```
257
+
258
+ **After 72 hours – deprecate instead:**
259
+ ```bash
260
+ npm deprecate @nqlib/nqui@0.4.1 "Superseded by 0.4.2. Use npm install @nqlib/nqui@0.4.2"
261
+ ```
262
+
263
+ ### GitHub Packages
264
+
265
+ **Rules:**
266
+ - You can delete any version at any time.
267
+ - Deleted versions can be re-published (unlike npm).
268
+
269
+ **Delete via GitHub UI:**
270
+ 1. Go to your repo → **Packages** (right sidebar) or `https://github.com/ORG/REPO/packages`
271
+ 2. Click the package `nqui`
272
+ 3. **Manage package** → **Delete package** (entire package) or **Manage versions** → delete specific versions
273
+
274
+ **Delete via GitHub API** (for automation):
275
+ ```bash
276
+ # Requires GITHUB_TOKEN with delete:packages
277
+ curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
278
+ "https://api.github.com/orgs/nqlib/packages/npm/nqui/versions/VERSION_ID"
279
+ ```
280
+ (Get `VERSION_ID` from the package page or API.)
281
+
282
+ ---
283
+
242
284
  ## Version Management
243
285
 
244
286
  The `npm version` command automatically:
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: nqui Components
3
+ description: Component implementation guide for nqui. Use when building UI, designing app layouts, or implementing components with @nqlib/nqui.
4
+ ---
5
+
6
+ # nqui Components Guide
7
+
8
+ Reference `packages/nqui/docs/components/README.md` for the full component index and implementation rules.
9
+
10
+ ## Quick Reference
11
+
12
+ 1. **Main index:** `packages/nqui/docs/components/README.md` - all components, use cases, prerequisites
13
+ 2. **Per-component:** `packages/nqui/docs/components/nqui-<name>.md` - import, examples, variants
14
+ 3. **Design system:** this folder - sizing, grouped controls
15
+
16
+ ## App Design Rule: Inline Selection → ToggleGroup
17
+
18
+ When designing app UI (toolbars, headers, inline controls):
19
+
20
+ | Context | Use | NOT |
21
+ |---------|-----|-----|
22
+ | View mode (List/Grid/Table), scale (Linear/Log), size (S/M/L) | **ToggleGroup** `type="single"` | RadioGroup |
23
+ | Format toolbar (Bold/Italic/Underline), multi-toggle | **ToggleGroup** `type="multiple"` | Multiple Checkboxes |
24
+ | Toolbar actions (Undo/Redo, align) | **ButtonGroup** | - |
25
+ | Single on/off (Bold, Mute) | **Toggle** | - |
26
+
27
+ **Rule:** Inline/toolbar selection = ToggleGroup. Use RadioGroup only for form context (settings page, modal form, stacked list).
28
+
29
+ ## App Design Rule: Context-First (Toolbar in Real Environment)
30
+
31
+ **Rule:** Never show Toggle/ToggleGroup/ButtonGroup in isolation. Always place them in realistic app context.
32
+
33
+ | Context | Layout | Reference |
34
+ |---------|--------|-----------|
35
+ | Document editor | Toolbar above content; `bg-muted/30` container; `Separator` between groups | ComponentShowcase → Toggle & ToggleGroup |
36
+ | Chart/settings | Label + inline controls; `rounded-lg border bg-muted/30 p-3` | ComponentShowcase → Chart settings |
37
+ | Standalone | Inline with related UI | ComponentShowcase → Standalone toggle |
38
+
39
+ **Canonical implementation:** `packages/nqui/src/pages/ComponentShowcase.tsx` — Toggle & ToggleGroup section.
40
+
41
+ ## Design System Conventions
42
+
43
+ See **`design-system.md`** in this folder for sizing, grouped controls (including pill **ButtonGroup** / **ToggleGroup** shells), and file paths under `packages/nqui/src/components/ui/`.
44
+
45
+ ### Control Sizing
46
+ - sm = h-6
47
+ - default = h-7
48
+ - lg = h-8
49
+
50
+ ### Z-Index
51
+ Always use CSS variables from elevation.css:
52
+ - `--z-content` (10) - standard content
53
+ - `--z-sticky-content` (15) - sticky within containers
54
+ - `--z-sticky-page` (20) - page-level sticky
55
+ - `--z-floating` (30) - floating panels
56
+ - `--z-modal-backdrop` (40)
57
+ - `--z-modal` (50)
58
+ - `--z-popover` (60)
59
+ - `--z-tooltip` (70)
60
+
61
+ ### FrostedGlass (sticky glass headers)
62
+
63
+ `FrostedGlass` is only the blur layer. Implementations need a **second row** with `bg-background/40`, `relative z-[var(--z-content)]`, and content that **scrolls behind** the sticky header. Full checklist, props, and troubleshooting: **`packages/nqui/docs/components/nqui-frosted-glass.md`**. Canonical page header: `AppLayout`; card: **Card** `stickyHeader`.
64
+
65
+ ### Component Naming
66
+ - Default exports are the enhanced/polished variants; use **Core\*** for plain primitives
67
+ - Implementations are consolidated under **`ui/`** (not separate `custom/enhanced-*` per component for Button, Badge, Checkbox, Select, Combobox, Sonner)
68
+ - File names: kebab-case
69
+ - Component names: PascalCase
70
+
71
+ ### Hit area (optional)
72
+
73
+ Library CSS includes [Bazza hit-area](https://bazza.dev/craft/2026/hit-area) utilities. For **Checkbox** / **Switch** in padded tables or lists, pass **`className="hit-area-6"`** (or `hit-area-4`, axis variants) on the **component root**, not on a wrapper-only parent. Opt-in only; use **`hit-area-debug`** while tuning. Details: `packages/nqui/docs/components/nqui-checkbox.md`, `nqui-switch.md`; examples: `ComponentShowcase` Checkbox + Switch sections.
74
+
75
+ ## Key Dependencies
76
+
77
+ Required peer dependencies:
78
+ - `@hugeicons/react`
79
+ - `@hugeicons/core-free-icons`
80
+
81
+ Optional:
82
+ - `next-themes` - for theme toggle
83
+ - `tw-animate-css` - for animations
84
+
85
+ ## Installation & Setup
86
+
87
+ ```bash
88
+ # Quick setup
89
+ npx @nqlib/nqui init-css
90
+
91
+ # App setup with sidebar
92
+ npx @nqlib/nqui init-css --sidebar
93
+
94
+ # Install peer dependencies
95
+ npx @nqlib/nqui install-peers
96
+ ```
97
+
98
+ ## CSS Variables
99
+
100
+ All components use CSS variables. Key ones:
101
+ - `--background`, `--foreground`
102
+ - `--muted`, `--muted-foreground`
103
+ - `--accent`, `--accent-foreground`
104
+ - `--border`, `--ring`
105
+ - `--primary`, `--primary-foreground`
106
+ - `--destructive`, `--destructive-foreground`
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: nqui-design-system
3
+ description: Design system conventions for nqui component library. Use when creating or modifying nqui UI components to ensure sizing, spacing, and styling consistency.
4
+ ---
5
+
6
+ # nqui Design System
7
+
8
+ Guidelines for AI agents implementing or modifying nqui components. Follow these rules to maintain visual consistency.
9
+
10
+ ## Control Size Scale
11
+
12
+ All interactive controls (buttons, toggles, inputs, selects) use a unified scale:
13
+
14
+ | Size | Height | Min Width | Use Case |
15
+ |----------|--------|-----------|-----------------------------------|
16
+ | `sm` | h-6 (24px) | min-w-6 | Compact UIs, dense tables, toolbars |
17
+ | `default`| h-7 (28px) | min-w-7 | Standard forms, primary actions |
18
+ | `lg` | h-8 (32px) | min-w-8 | Emphasis, secondary actions |
19
+
20
+ ### Semantic Rule
21
+
22
+ `size="sm"` on Button MUST produce the same height as `size="sm"` on ToggleGroupItem, SegmentedControlItem, SelectTrigger, etc. Never introduce component-specific scales.
23
+
24
+ ## Layout Heights
25
+
26
+ | Element | Height | Use Case |
27
+ |---------|--------|----------|
28
+ | Header | h-12 (48px) | Page header, app bar, sticky nav |
29
+ | Sidebar header | h-12 (48px) | Sidebar section title bar |
30
+
31
+ Fits 4px/8px grid. Default button (h-7) in 48px header leaves 10px top/bottom; h-8 leaves 8px. Use px-4, gap-2 or gap-4 for horizontal spacing.
32
+
33
+ ## Component Size Mapping
34
+
35
+ | Component | sm | default | lg |
36
+ |-----------|-----|---------|-----|
37
+ | Button | h-6 min-w-6 px-2 text-xs | h-7 min-w-7 px-3 | h-8 min-w-8 px-4 |
38
+ | Button (icon) | — | h-7 w-7 p-0 | — |
39
+ | Toggle | h-6 min-w-6 px-1.5 | h-7 min-w-7 px-2 | h-8 min-w-8 px-2 |
40
+ | ToggleGroupItem | (uses Toggle) | | |
41
+ | SelectTrigger | h-6 | h-7 | — |
42
+ | Input | — | h-7 px-3 py-1.5 text-sm | — |
43
+ | InputGroup | — | h-7 | — |
44
+
45
+ ## Border Radius & Nested Radius
46
+
47
+ | Token | Formula | Use Case |
48
+ |-------|---------|----------|
49
+ | --radius-sm | calc(--radius - 4px) | Compact controls, kbd |
50
+ | --radius-md | calc(--radius - 2px) | Default (Button, Input, Card) |
51
+ | --radius-lg | var(--radius) | Large surfaces |
52
+ | --radius-xl | calc(--radius + 4px) | Modals, sheets |
53
+
54
+ **Nested radius formula:** `R_inner = R_outer - offset`. When a smaller element sits inside a larger one with padding, use concentric radius so corners align.
55
+
56
+ ```
57
+ /* Inner card with p-3 (12px) inset inside radius-lg card */
58
+ border-radius: calc(var(--radius-lg) - var(--spacing-3));
59
+ ```
60
+
61
+ - **Standalone** components (Button, Input) use radius tokens directly.
62
+ - **Nested** elements (Card inside Card, panel in modal) use `calc(outer - offset)`.
63
+ - Clamp to avoid negatives: `max(0px, calc(...))` when offset ≥ outer.
64
+
65
+ ## Typography
66
+
67
+ | Size | Class | Use Case |
68
+ |------|-------|----------|
69
+ | sm | `text-xs` or `text-[0.625rem]` | Compact controls, labels |
70
+ | default | `text-sm` | Body, inputs, buttons |
71
+ | base | `text-base` | Section titles, headings |
72
+
73
+ Font: `--font-sans` (Inter Variable). Leading: `leading-normal` or `text-xs/relaxed`.
74
+
75
+ ## When Adding a New Component
76
+
77
+ 1. **Use the scale** – If the component has a `size` prop, map `sm`→h-6, `default`→h-7, `lg`→h-8.
78
+ 2. **Match padding** – Text controls: px-2–3, py-1.5. Icon-only: p-0 with explicit size.
79
+ 3. **Text size** – sm: `text-xs` or `text-[0.625rem]`, default: `text-sm`, lg: `text-sm`.
80
+ 4. **Border radius** – Use `rounded-md` (default) or `rounded-[min(var(--radius-md),8px)]` for sm; **Button** uses full pill (`rounded-full`). For nested layouts, use `calc(outer - offset)`.
81
+
82
+ ## Grouped Controls (ButtonGroup, ToggleGroup)
83
+
84
+ - **Shared border** – Container: `rounded-full border border-input overflow-hidden` (pill outer edge)
85
+ - **Child borders** – Items: `border-0` (container provides the border)
86
+ - **Dividers** – ToggleGroup: item borders (`border-foreground/20`) between items. Or `ToggleGroupSeparator` when `separator={false}`.
87
+ - **Corners** – First item: rounded-l (or rounded-t vertical), last: rounded-r (or rounded-b)
88
+ - **Several groups on one horizontal row** – `ButtonGroup`’s container is **`inline-flex`**, so multiple sibling groups in normal flow behave like **inline boxes** and align to **baseline**. Mixed content (labels vs icons vs `ButtonGroupText`) then produces inconsistent vertical alignment. Prefer an explicit row container: **`flex flex-wrap items-center gap-*`**, or **grid** columns—so you choose **cross-axis alignment** (`items-center`, `items-end`, etc.) instead of inheriting baseline alignment from inline layout.
89
+
90
+ ## Toggle & ToggleGroup Visual Treatment (Visibility on Any Background)
91
+
92
+ Toggles and ToggleGroup items must remain visible on card, sidebar, and varied backgrounds:
93
+
94
+ | State | Default/Outline | Segmented (single select) |
95
+ |-------|-----------------|---------------------------|
96
+ | **Off** | `border border-input/60` (or `border-input`), `shadow-sm`, `bg-background/50` (or transparent) | Transparent |
97
+ | **On** | `bg-secondary` + `nqui-button-gradient` + `nqui-button-shadow` (secondary-like layering) | `bg-primary` + gradient + shadow |
98
+ | **Active (pressed)** | `active:shadow-[inset_0_3px_5px_rgba(0,0,0,0.125)]` | Same |
99
+
100
+ Never use flat `bg-muted` only for selected state; always add gradient + shadow for depth and visibility.
101
+
102
+ ## Toolbar & In-Context Design
103
+
104
+ **Rule:** Show controls in realistic app context, not isolated. Reference: `packages/nqui/src/pages/ComponentShowcase.tsx` (Toggle & ToggleGroup section).
105
+
106
+ | Context | Layout | Example |
107
+ |---------|--------|---------|
108
+ | Document editor | Toolbar above content area, `bg-muted/30` container, `Separator` between control groups | Bold/Italic/Underline \| Align Left/Center/Right |
109
+ | Chart/settings panel | Label + inline controls, `rounded-lg border bg-muted/30 p-3` | Y-axis: Linear/Log; Size: S/M/L |
110
+ | Standalone | Inline with related UI, not floating alone | Pin, Mute, single Toggle |
111
+
112
+ ## Bounded content (default UX)
113
+
114
+ - **Rule:** Interactive surfaces should not **visually bleed** outside their box: prefer **ellipsis** (`truncate`), **line-clamp**, **controlled scroll**, or **wrapping** (`break-words` / `whitespace-normal`) instead of letting text or icons paint past padding in narrow layouts.
115
+ - **Not universal:** Portals (dropdowns, popovers), **tooltips**, and **drag overlays** intentionally escape bounds. **Data tables** may use horizontal scroll. Choose per component.
116
+ - **Flex gotcha:** `inline-flex` + icon + `whitespace-nowrap` + raw **string** children keeps an implicit **min-width: min-content**; parents need **`min-w-0`** (and often **`max-w-full`**) on the control and a truncating inner wrapper for long labels.
117
+ - **Built-in helpers in nqui:** Shared `wrapInlineLabelTextNodes` + `min-w-0 max-w-full` on **Button**, **Toggle**, **TabsTrigger**, **Badge**, **ComboboxChip**; **SelectTrigger** uses **`min-w-0 max-w-full`** and **`min-w-0`** on the value slot with **`line-clamp-1`**. **Carousel** prev/next sit **inside** the carousel box so they don’t spill out of **Card** (override via `className` if you want outside arrows).
118
+ - **Card:** **Card** uses **`min-w-0`** on shell + header/content/footer for flex/grid safety but keeps **`overflow-visible`** so overlays aren’t clipped; don’t rely on **`overflow-hidden`** on Card as a global “catch-all” unless you accept clipping dropdowns/focus.
119
+
120
+ ## Customization
121
+
122
+ - End users override via `className` or `size` when supported.
123
+ - Do NOT hardcode heights in consumer code when the component supports `size`.
124
+ - Prefer semantic sizes over pixel values in component defaults.
125
+
126
+ ## Hit area utilities
127
+
128
+ **Hit area** expands the pointer target; it does **not** replace control **size** (sm / default / lg). Use **opt-in** on **Checkbox** / **Switch** for padded rows or cells—not as a global default. Avoid large overlapping hit areas in dense toolbars. See **`SKILL.md`** (Hit area) in this folder and `packages/nqui/docs/components/nqui-checkbox.md` / `nqui-switch.md`.
129
+
130
+ ## Files to Check for Consistency
131
+
132
+ - `packages/nqui/src/components/ui/button.tsx`
133
+ - `packages/nqui/src/components/ui/toggle.tsx`
134
+ - `packages/nqui/src/components/ui/toggle-group.tsx`
135
+ - `packages/nqui/src/components/ui/input.tsx`
136
+ - `packages/nqui/src/components/ui/select.tsx`
137
+ - `packages/nqui/src/components/ui/combobox.tsx`
138
+
139
+ ## Anti-Patterns
140
+
141
+ - **Different heights for same size** – Button h-10 while Toggle h-7 = inconsistent.
142
+ - **Component-specific scales** – Do not add `xs` or `xl` without updating the design system doc.
143
+ - **Overriding in showcase** – If showcase needs `className="h-9"` to look right, the component default is wrong.
@@ -0,0 +1,183 @@
1
+ # Component Composition
2
+
3
+ Best practices for composing nqui components.
4
+
5
+ ## Contents
6
+
7
+ - Items always inside their Group component
8
+ - Callouts use Alert
9
+ - Empty states use Empty component
10
+ - Toast notifications use sonner
11
+ - Choosing between overlay components
12
+ - Dialog, Sheet, and Drawer always need a Title
13
+ - Card structure
14
+ - TabsTrigger must be inside TabsList
15
+ - Avatar always needs AvatarFallback
16
+ - Use Separator instead of raw hr or border divs
17
+ - Use Skeleton for loading placeholders
18
+ - Use Badge instead of custom styled spans
19
+
20
+ ---
21
+
22
+ ## Items always inside their Group component
23
+
24
+ Never render items directly inside the content container.
25
+
26
+ **Incorrect:**
27
+
28
+ ```tsx
29
+ <SelectContent>
30
+ <SelectItem value="apple">Apple</SelectItem>
31
+ <SelectItem value="banana">Banana</SelectItem>
32
+ </SelectContent>
33
+ ```
34
+
35
+ **Correct:**
36
+
37
+ ```tsx
38
+ <SelectContent>
39
+ <SelectGroup>
40
+ <SelectItem value="apple">Apple</SelectItem>
41
+ <SelectItem value="banana">Banana</SelectItem>
42
+ </SelectGroup>
43
+ </SelectContent>
44
+ ```
45
+
46
+ This applies to all group-based components:
47
+
48
+ | Item | Group |
49
+ |------|-------|
50
+ | `SelectItem`, `SelectLabel` | `SelectGroup` |
51
+ | `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSub` | `DropdownMenuGroup` |
52
+ | `MenubarItem` | `MenubarGroup` |
53
+ | `ContextMenuItem` | `ContextMenuGroup` |
54
+ | `CommandItem` | `CommandGroup` |
55
+
56
+ ---
57
+
58
+ ## Callouts use Alert
59
+
60
+ ```tsx
61
+ <Alert>
62
+ <AlertTitle>Warning</AlertTitle>
63
+ <AlertDescription>Something needs attention.</AlertDescription>
64
+ </Alert>
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Empty states use Empty component
70
+
71
+ ```tsx
72
+ <Empty>
73
+ <EmptyHeader>
74
+ <EmptyMedia variant="icon"><FolderIcon /></EmptyMedia>
75
+ <EmptyTitle>No projects yet</EmptyTitle>
76
+ <EmptyDescription>Get started by creating a new project.</EmptyDescription>
77
+ </EmptyHeader>
78
+ <EmptyContent>
79
+ <Button>Create Project</Button>
80
+ </EmptyContent>
81
+ </Empty>
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Toast notifications use sonner
87
+
88
+ ```tsx
89
+ import { toast } from "sonner"
90
+
91
+ toast.success("Changes saved.")
92
+ toast.error("Something went wrong.")
93
+ toast("File deleted.", {
94
+ action: { label: "Undo", onClick: () => undoDelete() },
95
+ })
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Choosing between overlay components
101
+
102
+ | Use case | Component |
103
+ |----------|-----------|
104
+ | Focused task that requires input | `Dialog` |
105
+ | Destructive action confirmation | `AlertDialog` |
106
+ | Side panel with details or filters | `Sheet` |
107
+ | Mobile-first bottom panel | `Drawer` |
108
+ | Quick info on hover | `HoverCard` |
109
+ | Small contextual content on click | `Popover` |
110
+
111
+ ---
112
+
113
+ ## Dialog, Sheet, and Drawer always need a Title
114
+
115
+ `DialogTitle`, `SheetTitle`, `DrawerTitle` are required for accessibility. Use `className="sr-only"` if visually hidden.
116
+
117
+ ```tsx
118
+ <DialogContent>
119
+ <DialogHeader>
120
+ <DialogTitle>Edit Profile</DialogTitle>
121
+ <DialogDescription>Update your profile.</DialogDescription>
122
+ </DialogHeader>
123
+ ...
124
+ </DialogContent>
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Card structure
130
+
131
+ Use full composition — don't dump everything into `CardContent`:
132
+
133
+ ```tsx
134
+ <Card>
135
+ <CardHeader>
136
+ <CardTitle>Team Members</CardTitle>
137
+ <CardDescription>Manage your team.</CardDescription>
138
+ </CardHeader>
139
+ <CardContent>...</CardContent>
140
+ <CardFooter>
141
+ <Button>Invite</Button>
142
+ </CardFooter>
143
+ </Card>
144
+ ```
145
+
146
+ ---
147
+
148
+ ## TabsTrigger must be inside TabsList
149
+
150
+ Never render `TabsTrigger` directly inside `Tabs` — always wrap in `TabsList`:
151
+
152
+ ```tsx
153
+ <Tabs defaultValue="account">
154
+ <TabsList>
155
+ <TabsTrigger value="account">Account</TabsTrigger>
156
+ <TabsTrigger value="password">Password</TabsTrigger>
157
+ </TabsList>
158
+ <TabsContent value="account">...</TabsContent>
159
+ </Tabs>
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Avatar always needs AvatarFallback
165
+
166
+ Always include `AvatarFallback` for when the image fails to load:
167
+
168
+ ```tsx
169
+ <Avatar>
170
+ <AvatarImage src="/avatar.png" alt="User" />
171
+ <AvatarFallback>JD</AvatarFallback>
172
+ </Avatar>
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Use existing components instead of custom markup
178
+
179
+ | Instead of | Use |
180
+ |------------|-----|
181
+ | `<hr>` or `<div className="border-t">` | `<Separator />` |
182
+ | `<div className="animate-pulse">` with styled divs | `<Skeleton className="h-4 w-3/4" />` |
183
+ | `<span className="rounded-full bg-green-100 ...">` | `<Badge variant="secondary">` |