@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
@@ -17,7 +17,7 @@ Implementation guides for each component. **AI Skill:** Optimized for AI/LLM con
17
17
  | Dependency | Version | Notes |
18
18
  |------------|---------|-------|
19
19
  | **React** | 18+ or 19 | nqui supports React 18 and 19 via `^18.0.0 \|\| ^19.0.0` peer. |
20
- | **Tailwind CSS** | ^4.x | Required. Vite: `@tailwindcss/vite`. Next.js: `@source` directives in CSS. |
20
+ | **Tailwind CSS** | ^4.x | Required. Vite: `@tailwindcss/vite`. Both Vite and Next.js need `@source` for `node_modules/@nqlib/nqui/dist` (Tailwind does not scan node_modules). |
21
21
  | **tw-animate-css** | — | `@import "tw-animate-css"` in your CSS (added by init-css). |
22
22
  | **Radix UI** | — | Bundled (Dialog, Select, etc.). |
23
23
  | **Hugeicons** | @hugeicons/react, @hugeicons/core-free-icons | Required for icon display in components. |
@@ -28,16 +28,115 @@ Implementation guides for each component. **AI Skill:** Optimized for AI/LLM con
28
28
 
29
29
  ## Shared Conventions (AI Implementation Rules)
30
30
 
31
- - **Control sizes:** `sm`=h-6, `default`=h-7, `lg`=h-8. Button, Toggle, ToggleGroup, Input, Select use this scale. See `.cursor/skills/nqui-design-system/SKILL.md`.
31
+ - **Control sizes:** `sm`=h-6, `default`=h-7, `lg`=h-8. Button, Toggle, ToggleGroup, Input, Select, Switch, Slider use this scale. See `packages/nqui/docs/nqui-skills/design-system.md` or `.cursor/skills/nqui-design-system/SKILL.md`.
32
32
  - **CSS vars required:** nqui uses `--primary`, `--background`, `--foreground`, etc. Run `npx @nqlib/nqui init-css`.
33
- - **Enhanced vs Core:** Button, Badge, Checkbox, Select, etc. default to enhanced (3D, animations). Use `CoreButton`, `CoreBadge`, etc. for base shadcn. Separator: single component with `variant` prop (no CoreSeparator).
34
- - **Grouped controls:** ButtonGroup, ToggleGroup share border; ToggleGroup uses item dividers (`border-foreground/20`) or `ToggleGroupSeparator`.
33
+ - **Enhanced vs Core:** Default exports (`Button`, `Badge`, `Checkbox`, `Select`, etc.) are the polished/3D variants. Implementations live in **`packages/nqui/src/components/ui/*.tsx`** (single file per concern). Use `CoreButton`, `CoreBadge`, `CoreCheckbox`, etc. for base Radix/shadcn-style behavior. Separator: single component with `variant` prop (no CoreSeparator).
34
+ - **Grouped controls:** ButtonGroup, ToggleGroup share border; outer container uses **pill** radius (`rounded-full`). ToggleGroup uses item dividers (`border-foreground/20`) or `ToggleGroupSeparator`.
35
35
  - **Toolbar context:** Always show Toggle/ToggleGroup in realistic context (document toolbar, chart settings, etc.). Reference: `src/pages/ComponentShowcase.tsx`.
36
- - **Style injection:** Checkbox, Rating, Combobox inject `<style>` at mount → client-only guard for SSR.
36
+ - **Style injection:** Some components inject `<style>` once at mount (e.g. **Combobox** input chrome in `ui/combobox.tsx`) safe for SSR if the component is client-only (`"use client"`).
37
37
  - **OKLCH:** ColorPicker expects OKLCH strings (`oklch(0.5 0.15 240)`), not hex.
38
38
  - **SidebarProvider:** Must wrap entire layout (sidebar + content).
39
39
  - **Z-index:** Use CSS vars from `styles/elevation.css` (e.g. `z-[var(--z-modal)]`). Never hardcode `z-10`, `z-50`, etc.
40
40
  - **Keyboard:** Use `Keys`, `isMod`, `shouldIgnoreKeyboardShortcut` from `@/lib/keyboard` for custom shortcuts.
41
+ - **Bounded content:** Chips, pills, and inline controls should stay inside their box (ellipsis / line-clamp / scroll-by-design). See **Bounded content** in `packages/nqui/docs/nqui-skills/design-system.md`. Portals and tooltips are explicit exceptions.
42
+
43
+ ---
44
+
45
+ ## Layout & Scroll Patterns
46
+
47
+ This section documents the CSS patterns used in the showcase app to ensure proper scroll behavior and prevent sticky element issues.
48
+
49
+ ### Sticky Elements & Momentum Scroll
50
+
51
+ **The problem:** When using default body scroll, sticky elements can exhibit "momentum push" behavior during fast scrolling - they appear to float or lag before snapping back. This happens because sticky positioning is relative to the viewport, not the scroll container.
52
+
53
+ **The solution:** Use a custom scroll container instead of body/html scroll.
54
+
55
+ ```tsx
56
+ // App layout structure - prevents momentum push on sticky elements
57
+ <div className="h-screen overflow-hidden">
58
+ <header className="sticky top-0 z-[var(--z-sticky-page)]">
59
+ {/* Page header */}
60
+ </header>
61
+ <main className="flex-1 min-h-0 overflow-y-auto">
62
+ {/* Scrollable content */}
63
+ </main>
64
+ </div>
65
+ ```
66
+
67
+ ### Z-Index Layering for Sticky Elements
68
+
69
+ Use the correct z-index variables to prevent sticky element conflicts:
70
+
71
+ | Element | CSS Variable | Value |
72
+ |---------|-------------|-------|
73
+ | Page headers, navigation bars | `z-[var(--z-sticky-page)]` | 20 |
74
+ | Card headers, table headers | `z-[var(--z-sticky-content)]` | 15 |
75
+
76
+ **Rule:** Page-level sticky elements (20) must be above container-level sticky elements (15) to prevent scroll conflicts.
77
+
78
+ ### Flex Scroll Pattern
79
+
80
+ For nested scrolling to work correctly, flex children must have `min-height: 0` (or `min-h-0` in Tailwind):
81
+
82
+ ```tsx
83
+ // Correct - flex child can shrink and scroll
84
+ <div className="flex flex-col h-screen">
85
+ <header className="flex-shrink-0">...</header>
86
+ <main className="flex-1 min-h-0 overflow-y-auto">
87
+ {/* This will scroll */}
88
+ </main>
89
+ </div>
90
+
91
+ // Wrong - flex child cannot shrink below content height
92
+ <div className="flex flex-col h-screen">
93
+ <header>...</header>
94
+ <main className="flex-1 overflow-y-auto">
95
+ {/* May not scroll - overflows instead */}
96
+ </main>
97
+ </div>
98
+ ```
99
+
100
+ ### Body Scroll Prevention
101
+
102
+ For app-like experiences, disable body/html scroll:
103
+
104
+ ```tsx
105
+ useEffect(() => {
106
+ // Disable browser scroll restoration
107
+ if ('scrollRestoration' in history) {
108
+ history.scrollRestoration = 'manual'
109
+ }
110
+
111
+ // Prevent body/html from scrolling
112
+ document.body.style.overflow = 'hidden'
113
+ document.documentElement.style.overflow = 'hidden'
114
+
115
+ return () => {
116
+ document.body.style.overflow = ''
117
+ document.documentElement.style.overflow = ''
118
+ }
119
+ }, [])
120
+ ```
121
+
122
+ ### Sliding Indicator Containers
123
+
124
+ Components with sliding indicators (Tabs, RadioGroup `sliding` variant) need specific container styling:
125
+
126
+ ```tsx
127
+ // Required CSS for sliding indicator containers
128
+ .sliding-indicator-container {
129
+ position: relative;
130
+ overflow: hidden;
131
+ min-width: 0;
132
+ }
133
+
134
+ .sliding-indicator {
135
+ position: absolute;
136
+ overflow: visible;
137
+ /* transitions for smooth sliding */
138
+ }
139
+ ```
41
140
 
42
141
  ---
43
142
 
@@ -67,7 +166,7 @@ Use these rules to choose the right component. **Selection** = user picks from o
67
166
  |-------|-----|
68
167
  | 2–5 options, inline (e.g. bold/italic/underline) | **ToggleGroup** `type="multiple"` |
69
168
  | Form context, list of options | **Checkbox** (each option) |
70
- | Many options, need search | **Combobox** `multiSelect` |
169
+ | Many options, need search | **Combobox** with `multiple` (see `nqui-combobox.md`) |
71
170
 
72
171
  ### Actions (trigger, not select)
73
172
 
@@ -108,7 +207,7 @@ Use these rules to choose the right component. **Selection** = user picks from o
108
207
  | Single choice, 5+ options or no space | **Select** |
109
208
  | Single choice, need search | **Combobox** (single) |
110
209
  | Single choice, form, small option set | **RadioGroup** |
111
- | Multiple choice, need search | **Combobox** `multiSelect` |
210
+ | Multiple choice, need search | **Combobox** with `multiple` (see `nqui-combobox.md`) |
112
211
  | One boolean (agree, subscribe) | **Checkbox** |
113
212
  | On/off setting (dark mode, notifications) | **Switch** |
114
213
  | Numeric range (volume, opacity) | **Slider** |
@@ -135,7 +234,7 @@ Use these rules to choose the right component. **Selection** = user picks from o
135
234
  | Rating | [nqui-rating.md](./nqui-rating.md) | Star/score (1–5). |
136
235
  | InputOTP | [nqui-input-otp.md](./nqui-input-otp.md) | OTP/verification. |
137
236
  | Field | [nqui-field.md](./nqui-field.md) | Label + description + error wrapper. |
138
- | Combobox | [nqui-combobox.md](./nqui-combobox.md) | **Searchable** select. Single or `multiSelect`. Use when user types to filter. |
237
+ | Combobox | [nqui-combobox.md](./nqui-combobox.md) | **Searchable** select. Single or `multiple`. Use when user types to filter. |
139
238
  | ColorPicker | [nqui-color-picker.md](./nqui-color-picker.md) | Color selection. OKLCH. |
140
239
  | ColorSlider | [nqui-color-slider.md](./nqui-color-slider.md) | Hue/saturation (used in ColorPicker). |
141
240
  | Label | [nqui-label.md](./nqui-label.md) | Form label. |
@@ -158,7 +257,7 @@ Use these rules to choose the right component. **Selection** = user picks from o
158
257
  | List row (avatar + title + actions) | **Item** |
159
258
  | Show keyboard shortcut | **Kbd** |
160
259
  | Activity blocks (e.g. heatmap) | **Tracker** |
161
- | Glass effect | **FrostedGlass** |
260
+ | Glass effect (blur + tint row; scroll behind) | **FrostedGlass** |
162
261
 
163
262
  ---
164
263
 
@@ -178,7 +277,7 @@ Use these rules to choose the right component. **Selection** = user picks from o
178
277
  | Item | [nqui-item.md](./nqui-item.md) | List row: media + title + description + actions. |
179
278
  | Kbd | [nqui-kbd.md](./nqui-kbd.md) | Keyboard shortcut display. |
180
279
  | Tracker | [nqui-tracker.md](./nqui-tracker.md) | Activity blocks (heatmap). |
181
- | FrostedGlass | [nqui-frosted-glass.md](./nqui-frosted-glass.md) | Glass effect. |
280
+ | FrostedGlass | [nqui-frosted-glass.md](./nqui-frosted-glass.md) | Glass blur; use with second tint row + scroll behind (see doc). |
182
281
  | NquiLogo | [nqui-logo.md](./nqui-logo.md) | Theme-aware logo. |
183
282
 
184
283
  ---
@@ -38,5 +38,6 @@ import { Badge } from "@nqlib/nqui"
38
38
 
39
39
  ## Notes
40
40
 
41
+ - Implementation: **`packages/nqui/src/components/ui/badge.tsx`** (enhanced + core in one module).
41
42
  - ghost/link route to CoreBadge (different styling).
42
43
  - Use `CoreBadge` for plain shadcn badge.
@@ -1,6 +1,6 @@
1
1
  # nqui Button
2
2
 
3
- > Enhanced button with 9 variants, gradients, active scale. Default import is EnhancedButton.
3
+ > Default **Button** is the enhanced variant (gradients, depth, active scale). Implemented in **`ui/button.tsx`** with **`CoreButton`** for the plain primitive.
4
4
 
5
5
  ## Import
6
6
 
@@ -52,5 +52,7 @@ Render as `<a>` or custom element:
52
52
 
53
53
  ## Notes
54
54
 
55
+ - **Shape:** full pill (`rounded-full`) for all sizes.
56
+ - **Bounded label:** string/number children are wrapped so **`truncate`** + **`min-w-0`** apply in narrow layouts. Same pattern is used across **Toggle**, **Tabs**, **Badge**, **Combobox** chips; **Select** value uses **line-clamp**. Custom markup with one long inner node may still need its own `min-w-0 truncate` or a **`title`**.
55
57
  - Active state uses `scale-95`; avoid parent `transform` overrides.
56
58
  - Use `CoreButton` from `@nqlib/nqui` for base shadcn style.
@@ -29,3 +29,11 @@ import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter }
29
29
  <CardContent>Scrollable content</CardContent>
30
30
  </Card>
31
31
  ```
32
+
33
+ ## Notes
34
+
35
+ - **Layout / bounds:** Root **Card**, **CardHeader**, **CardContent**, and **CardFooter** include **`min-w-0`** so nested flex/grid layouts can shrink and children are less likely to spill horizontally. **Card** stays **`overflow-visible`** on purpose so popovers, menus, and focus rings are not clipped—pair with bounded components (buttons, carousel insets, `truncate`, etc.) instead of `overflow-hidden` on the card by default.
36
+ - **stickyHeader:** Use `stickyHeader` prop on Card for scrollable content with sticky header. The header uses `--z-sticky-content` (z-index: 15).
37
+ - **Z-index layering:** Card sticky headers (15) are below page headers (20). If using in a page with sticky header, ensure proper z-index layering.
38
+ - **Height required:** Card needs a defined height (e.g., `h-[400px]` or `h-full`) for sticky behavior to work.
39
+ - **Content scroll:** The CardContent becomes scrollable when Card has `stickyHeader` and a defined height.
@@ -20,3 +20,9 @@ import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext
20
20
  <CarouselNext />
21
21
  </Carousel>
22
22
  ```
23
+
24
+ ## Notes
25
+
26
+ - **Prev/Next** are positioned **inside** the carousel region (`left-2` / `right-2`, or `top-2` / `bottom-2` when vertical) so arrows stay within parents like **Card** instead of using negative offsets that sat outside the viewport (`-left-12` / `-right-12` previously).
27
+ - **Visibility:** Arrows are **dim by default** (`opacity-35`), ramp up on **carousel hover** (`group-hover/carousel`), and go **full opacity** on **button** `:hover`, `:focus-visible`, and `:active` (tap). Disabled controls stay faint.
28
+ - Override with `className` on `CarouselPrevious` / `CarouselNext` if you need edge-aligned or external controls.
@@ -11,9 +11,31 @@ import { Checkbox } from "@nqlib/nqui"
11
11
  ## Basic
12
12
 
13
13
  ```tsx
14
- <Checkbox checked={checked} onCheckedChange={setChecked} />
14
+ const [checked, setChecked] = useState(false)
15
+
16
+ <Checkbox checked={checked} onCheckedChange={setChecked}>Accept terms</Checkbox>
17
+ ```
18
+
19
+ ## With Label
20
+
21
+ Checkbox automatically wraps in a label when children are provided:
22
+
23
+ ```tsx
24
+ <Checkbox>Accept terms</Checkbox>
25
+ ```
26
+
27
+ ## Gap
28
+
29
+ Control the gap between checkbox and label:
30
+
31
+ ```tsx
32
+ <Checkbox gap={2}>Compact gap (8px)</Checkbox>
33
+ <Checkbox gap={3}>Default gap (12px)</Checkbox>
34
+ <Checkbox gap={4}>Loose gap (16px)</Checkbox>
15
35
  ```
16
36
 
37
+ Options: `0`, `1`, `2`, `3`, `4` (maps to Tailwind gap-0 through gap-4).
38
+
17
39
  ## Variants
18
40
 
19
41
  ```tsx
@@ -27,8 +49,23 @@ import { Checkbox } from "@nqlib/nqui"
27
49
  <Checkbox checked="indeterminate" onCheckedChange={...} />
28
50
  ```
29
51
 
52
+ ## Hit area (expanded pointer target)
53
+
54
+ nqui ships [Bazza **hit-area** utilities](https://bazza.dev/craft/2026/hit-area) in library CSS (`hit-area-*`, `hit-area-x-*`, `hit-area-debug`, etc.). They extend the clickable region with a `::before` layer without changing layout.
55
+
56
+ **Enhanced checkbox:** The checkmark is drawn with `::after` on the control root so `::before` stays free for `hit-area-*` on the **same** element. Add a class when you want a larger target (e.g. padded table cells):
57
+
58
+ ```tsx
59
+ <Checkbox className="hit-area-6" checked={rowSelected} onCheckedChange={...} aria-label="Select row" />
60
+ ```
61
+
62
+ **Label hover pill:** `.checkbox-animated-label::before` is on the **label** wrapper only; it does not conflict with `hit-area-*` on the checkbox button.
63
+
64
+ **Without hit-area:** You can still wrap the control in `<span className="relative hit-area-6">` if you prefer the expanded area on a wrapper.
65
+
30
66
  ## Notes
31
67
 
68
+ - Implementation: **`packages/nqui/src/components/ui/checkbox.tsx`** (enhanced + core in one module).
32
69
  - Injects `<style>` at mount. Use client-only guard for SSR.
33
70
  - Square and round share the same animation (pulse + checkmark scale); round has no SVG filters.
34
71
  - Use `CoreCheckbox` for plain Radix checkbox.
@@ -10,10 +10,12 @@ import { ColorSlider } from "@nqlib/nqui"
10
10
 
11
11
  ## Types
12
12
 
13
+ Built on **`Slider`** from `ui/slider` (white thumb + shadow). For **non-controlled** demos, prefer **`defaultValue`** so the thumb stays draggable.
14
+
13
15
  ```tsx
14
- <ColorSlider sliderType="hue" value={[240]} onValueChange={setVal} min={0} max={360} />
15
- <ColorSlider sliderType="saturation" value={[0.5]} onValueChange={setVal} min={0} max={1} step={0.01} />
16
- <ColorSlider sliderType="lightness" value={[0.6]} onValueChange={setVal} min={0} max={1} step={0.01} />
16
+ <ColorSlider sliderType="hue" defaultValue={[240]} onValueChange={setVal} min={0} max={360} />
17
+ <ColorSlider sliderType="saturation" defaultValue={[0.5]} onValueChange={setVal} min={0} max={1} step={0.01} />
18
+ <ColorSlider sliderType="lightness" defaultValue={[0.6]} onValueChange={setVal} min={0} max={1} step={0.01} />
17
19
  ```
18
20
 
19
21
  ## Custom
@@ -1,73 +1,94 @@
1
1
  # nqui Combobox
2
2
 
3
- > **Searchable** select. Single or multi-select. Use when user types to filter options.
3
+ > **Searchable** select (Base UI). User types to filter options. Single or multiple selection.
4
4
 
5
5
  ## When to Use
6
6
 
7
- - **Selection:** Single or multiple
8
- - **Key feature:** User searches/filters options by typing
9
- - **Options:** Many (dozens, hundreds)
7
+ - **Selection:** Single (default) or multiple (`multiple` on root)
8
+ - **Key feature:** Filter list by typing in the input
9
+ - **Options:** Many rows; use **`items`** on `Combobox` + render prop on `ComboboxList` for built-in filtering
10
10
 
11
- **Choose Combobox when:** Options are too many to scroll, or user knows the name. Type to filter. Use Select when options are few and no search needed.
11
+ **Choose Combobox when:** Users need search. Use **Select** when a plain dropdown is enough.
12
12
 
13
13
  ## Import
14
14
 
15
15
  ```tsx
16
16
  import {
17
- Combobox, ComboboxInput, ComboboxContent, ComboboxList,
18
- ComboboxItem, ComboboxEmpty, ComboboxChips, ComboboxChip,
19
- ComboboxTrigger, ComboboxValue, ComboboxCollection, ComboboxGroup,
20
- ComboboxLabel, ComboboxSeparator
17
+ Combobox,
18
+ ComboboxInput,
19
+ ComboboxContent,
20
+ ComboboxList,
21
+ ComboboxItem,
22
+ ComboboxEmpty,
23
+ ComboboxGroup,
24
+ ComboboxLabel,
25
+ ComboboxSeparator,
26
+ ComboboxCollection,
27
+ ComboboxChips,
28
+ ComboboxChip,
29
+ ComboboxChipsInput,
30
+ ComboboxTrigger,
31
+ ComboboxValue,
32
+ ComboboxClear,
33
+ useComboboxAnchor,
21
34
  } from "@nqlib/nqui"
22
35
  ```
23
36
 
24
- ## Basic
37
+ ## Single selection + type-to-filter (recommended)
38
+
39
+ Pass **`items`** to `Combobox` and use a **render function** as the only child of `ComboboxList`. Place **`ComboboxEmpty`** next to `ComboboxList` (both under `ComboboxContent`), not inside `ComboboxList` alongside the function.
25
40
 
26
41
  ```tsx
27
- <Combobox options={items} value={value} onChange={setValue}>
42
+ const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
43
+
44
+ <Combobox items={fruits}>
28
45
  <ComboboxInput placeholder="Search..." />
29
46
  <ComboboxContent>
47
+ <ComboboxEmpty>No results found.</ComboboxEmpty>
30
48
  <ComboboxList>
31
- {items.map((item) => (
32
- <ComboboxItem key={item.value} value={item.value}>{item.label}</ComboboxItem>
33
- ))}
49
+ {(item) => (
50
+ <ComboboxItem key={item} value={item}>
51
+ {item}
52
+ </ComboboxItem>
53
+ )}
34
54
  </ComboboxList>
35
- <ComboboxEmpty>No results</ComboboxEmpty>
36
55
  </ComboboxContent>
37
56
  </Combobox>
38
57
  ```
39
58
 
40
- ## showClear
59
+ ## Static list (no `items` filter)
41
60
 
42
- ```tsx
43
- <Combobox showClear ... />
44
- ```
45
-
46
- ## Multi-select chips
61
+ You can still render `ComboboxItem` children manually when you control filtering yourself.
47
62
 
48
63
  ```tsx
49
- <Combobox multiSelect value={selected} onChange={setSelected}>
50
- <ComboboxChips placeholder="Select...">
51
- {(vals) => vals.map((v) => <ComboboxChip key={v} value={v} onRemove={...} />)}
52
- </ComboboxChips>
53
- ...
64
+ <Combobox>
65
+ <ComboboxInput placeholder="Search..." />
66
+ <ComboboxContent>
67
+ <ComboboxList>
68
+ <ComboboxItem value="a">A</ComboboxItem>
69
+ <ComboboxItem value="b">B</ComboboxItem>
70
+ </ComboboxList>
71
+ </ComboboxContent>
54
72
  </Combobox>
55
73
  ```
56
74
 
57
- ## Groups
75
+ ## Clear button
58
76
 
59
77
  ```tsx
60
- <ComboboxCollection>
61
- <ComboboxGroup>
62
- <ComboboxLabel>Group A</ComboboxLabel>
63
- <ComboboxItem value="a">A</ComboboxItem>
64
- </ComboboxGroup>
65
- <ComboboxSeparator />
66
- ...
67
- </ComboboxCollection>
78
+ <ComboboxInput showClear placeholder="Search..." />
68
79
  ```
69
80
 
81
+ ## Multiple selection
82
+
83
+ Use **`multiple`** on `Combobox` (see [Base UI Combobox](https://base-ui.com/react/components/combobox)). Combine with **`ComboboxChips`**, **`ComboboxChip`**, **`ComboboxChipsInput`** as needed for chip UI.
84
+
85
+ ## Implementation
86
+
87
+ - **Source:** `packages/nqui/src/components/ui/combobox.tsx`
88
+ - **Public API:** exported from `@nqlib/nqui` (same as `CoreCombobox*` aliases in the barrel if you need the unprefixed base re-exports)
89
+ - **Styling:** Input group uses injected CSS once per page (`nqui-combobox-styles-v1`) for trigger depth/shadow; component is `"use client"`.
90
+
70
91
  ## Notes
71
92
 
72
- - Injects CSS at mount; client-only in SSR.
73
- - `useComboboxAnchor` for PopoverAnchor positioning.
93
+ - **`useComboboxAnchor`:** ref for anchoring `ComboboxContent` when using chips / custom layout.
94
+ - **Dropdown items:** spacing/hover treatment aligns with **Select** (`SelectItem`-style density).
@@ -1,6 +1,6 @@
1
1
  # nqui Drawer
2
2
 
3
- > Bottom drawer. DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter.
3
+ > **Vaul** drawer. **DrawerContent** keeps an **inset rounded card** (`before:bg-card`, `before:inset-2`, `before:rounded-xl`) so the panel does not read as a full-bleed slab—surface follows **`card`** tokens in both themes.
4
4
 
5
5
  ## Import
6
6
 
@@ -1,6 +1,8 @@
1
1
  # nqui FrostedGlass
2
2
 
3
- > Frosted glass blur effect. For sticky headers, overlays.
3
+ > Apple-style frosted glass using `backdrop-filter`, extended backdrop, and masks. **Do not use `FrostedGlass` alone** in headers—pair it with a second row that carries the tint and controls stacking.
4
+
5
+ **Background:** [Josh Comeau — `backdrop-filter`](https://www.joshwcomeau.com/css/backdrop-filter/)
4
6
 
5
7
  ## Import
6
8
 
@@ -8,12 +10,88 @@
8
10
  import { FrostedGlass } from "@nqlib/nqui"
9
11
  ```
10
12
 
11
- ## Basic
13
+ ## Props
14
+
15
+ | Prop | Type | Default | Notes |
16
+ |------|------|---------|--------|
17
+ | `blur` | `number` | `16` | Blur radius in pixels (`backdrop-filter`). |
18
+ | `borderRadius` | `number` | `0` | Pixels. `> 0` enables an SVG corner mask; `0` uses a linear gradient mask (flat header edge). |
19
+ | `className` | `string` | — | Extra classes on the backdrop layer (e.g. z-index). |
20
+
21
+ There is **no `opacity` prop**—tint comes from the sibling bar (see below). The backdrop layer uses a very light `bg-background/3` internally so the blur reads clearly.
22
+
23
+ ## Why two layers?
24
+
25
+ `FrostedGlass` renders an **absolute** backdrop layer (`pointer-events-none`, extended height for sampling). Your **toolbar/title row** must be a **sibling** on top with:
26
+
27
+ - `relative z-[var(--z-content)]`
28
+ - A semi-opaque surface, e.g. `bg-background/40` (tint + readability)
29
+ - Optional `border-b`, transitions, flex layout
30
+
31
+ Without that second row, you only get blur with almost no “glass” read and no interactive chrome.
32
+
33
+ ## Z-index (elevation)
34
+
35
+ Use variables from `styles/elevation.css` (never raw `z-10` / `z-50`).
36
+
37
+ | Layer | Typical class | Role |
38
+ |-------|----------------|------|
39
+ | Sticky container | `z-[var(--z-sticky-page)]` or `z-[var(--z-sticky-content)]` | Page header vs sticky **inside** a card/panel. |
40
+ | `FrostedGlass` | `className="z-[var(--z-background)]"` | Blur sits **below** the bar content. |
41
+ | Bar / controls | `relative z-[var(--z-content)]` | Text, buttons, borders. |
42
+
43
+ **Rule:** `--z-sticky-content` (15) &lt; `--z-sticky-page` (20). Use `--z-sticky-page` for app chrome; `--z-sticky-content` for a **Card** with `stickyHeader` so page nav stays above card headers.
44
+
45
+ ## Scroll requirement
46
+
47
+ `backdrop-filter` blurs **what is painted behind** the element. For a sticky header:
48
+
49
+ 1. Put the header and the main content in the **same scroll container** (or ensure content scrolls under the sticky region).
50
+ 2. Content must **move behind** the header while scrolling. If nothing scrolls behind the bar, you will see a flat tint with little or no visible blur.
51
+
52
+ Reference: `AppLayout` wraps header + main in `overflow-y-auto` so the main area scrolls under the sticky header.
53
+
54
+ ## Page sticky header (canonical)
55
+
56
+ Same structure as `packages/nqui/src/components/AppLayout.tsx`:
12
57
 
13
58
  ```tsx
14
- <FrostedGlass blur={16} borderRadius={0} />
59
+ <div className="flex-1 min-h-0 flex flex-col overflow-y-auto overflow-x-hidden">
60
+ <header className="sticky top-0 z-[var(--z-sticky-page)] flex-shrink-0 relative">
61
+ <FrostedGlass blur={16} borderRadius={0} className="z-[var(--z-background)]" />
62
+ <div className="relative z-[var(--z-content)] border-b bg-background/40 flex h-12 items-center gap-2 px-4">
63
+ {/* title, nav, actions */}
64
+ </div>
65
+ </header>
66
+ <main>{/* scrolls behind header */}</main>
67
+ </div>
15
68
  ```
16
69
 
17
- ## Props
70
+ - Outer `header`: `sticky` + `relative` + page-level z-index.
71
+ - `FrostedGlass`: `borderRadius={0}` for a straight top edge; increase if the header has rounded corners.
72
+ - Inner bar: `bg-background/40` (adjust opacity to taste).
73
+
74
+ ## Card with sticky header
75
+
76
+ **Card** `stickyHeader` wires this pattern for you: `FrostedGlass` with `borderRadius={8}` under **CardHeader**, scrollable **CardContent** below. See `ComponentShowcase` and `packages/nqui/src/components/ui/card.tsx`.
77
+
78
+ Use `--z-sticky-content` on the sticky region when the glass sits inside a card, not the full page.
79
+
80
+ ## Troubleshooting
81
+
82
+ | Symptom | Likely cause |
83
+ |---------|----------------|
84
+ | Solid bar, almost no blur | No content scrolling behind the sticky region, or header not in the scrolling ancestor. |
85
+ | Blur but unreadable text | Add / raise `bg-background/40` (or similar) on the **content row**, not only on `FrostedGlass`. |
86
+ | Wrong stacking vs sidebar/modals | Check elevation: floating sidebars use `--z-floating`; modals/popovers sit above—see `elevation.css`. |
87
+ | Rounded header corners | Set `borderRadius` to match; `> 0` switches to the SVG mask path. |
88
+
89
+ ## Vite / Tailwind
90
+
91
+ Consumer apps must import nqui styles **and** add Tailwind `@source` for the library (see **INSTALLATION.md** §2c). Missing sources can strip utilities used around the header (`bg-background/40`, z-index arbitrary values, etc.).
92
+
93
+ ## Related
18
94
 
19
- blur, borderRadius, opacity. Use in sticky header with z-index.
95
+ - **AppLayout** full app shell with frosted page header.
96
+ - **Card** `stickyHeader` — frosted sticky card header.
97
+ - **Select** — popover row uses the same backdrop layering pattern.
@@ -18,13 +18,58 @@ import { RadioGroup, RadioGroupItem } from "@nqlib/nqui"
18
18
 
19
19
  ## Basic
20
20
 
21
+ RadioGroupItem now automatically wraps the radio button with a label when children are provided:
22
+
23
+ ```tsx
24
+ const [value, setValue] = useState("a")
25
+
26
+ <RadioGroup value={value} onValueChange={setValue}>
27
+ <RadioGroupItem value="a">Option A</RadioGroupItem>
28
+ <RadioGroupItem value="b">Option B</RadioGroupItem>
29
+ </RadioGroup>
30
+ ```
31
+
32
+ **Do not** pair an empty `RadioGroupItem` with a separate `Label` in the same row: the item’s outer wrapper is meant to include the text (as children) or stand alone without a sibling label. Putting `Label` beside a control-only item can create a wide empty flex region and push the label to the far edge. Prefer the pattern above; if you must use a separate label, use `CoreRadioGroupItem` from `@nqlib/nqui` (see package exports) or ensure the item is not wrapped in a full-width row that fights the layout.
33
+
34
+ ## With Complex Content
35
+
21
36
  ```tsx
22
37
  <RadioGroup value={value} onValueChange={setValue}>
23
- <RadioGroupItem value="a" /> A
24
- <RadioGroupItem value="b" /> B
38
+ <RadioGroupItem value="email">
39
+ <div>
40
+ <div className="font-medium">Email</div>
41
+ <div className="text-sm text-muted-foreground">Receive notifications via email</div>
42
+ </div>
43
+ </RadioGroupItem>
44
+ <RadioGroupItem value="sms">
45
+ <div>
46
+ <div className="font-medium">SMS</div>
47
+ <div className="text-sm text-muted-foreground">Receive notifications via SMS</div>
48
+ </div>
49
+ </RadioGroupItem>
25
50
  </RadioGroup>
26
51
  ```
27
52
 
53
+ ## Spacing
54
+
55
+ Control the gap between radio items with the `gap` prop on RadioGroup:
56
+
57
+ ```tsx
58
+ <RadioGroup gap={0}>No gap</RadioGroup>
59
+ <RadioGroup gap={1}>4px gap</RadioGroup>
60
+ <RadioGroup gap={2}>Compact (8px)</RadioGroup>
61
+ <RadioGroup gap={3}>Default (12px)</RadioGroup>
62
+ <RadioGroup gap={4}>Loose (16px)</RadioGroup>
63
+ ```
64
+
65
+ Control the gap between radio button and label with the `spacing` prop on RadioGroupItem:
66
+
67
+ ```tsx
68
+ <RadioGroupItem value="a" spacing="compact">Tight spacing (8px)</RadioGroupItem>
69
+ <RadioGroupItem value="b" spacing="default">Default spacing (12px)</RadioGroupItem>
70
+ <RadioGroupItem value="c" spacing="comfortable">Loose spacing (16px)</RadioGroupItem>
71
+ ```
72
+
28
73
  ## Variants
29
74
 
30
75
  ```tsx
@@ -1,6 +1,6 @@
1
1
  # nqui ScrollArea
2
2
 
3
- > Custom scrollbar. fadeMask (enhanced), horizontal scroll.
3
+ > Default export is **EnhancedScrollArea** (fade mask, orientation). Underlying primitive: **`CoreScrollArea`** / `ScrollBar` from **`ui/scroll-area`**. Core scrollbar uses a **thinner** track/thumb (drawer-like).
4
4
 
5
5
  ## Import
6
6
 
@@ -48,6 +48,6 @@ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGr
48
48
 
49
49
  ## Notes
50
50
 
51
- - Injects CSS at mount; client-only in SSR.
51
+ - **Content:** FrostedGlass + popover surface; **items** use relaxed row spacing (hover `accent`, margins) for dropdown parity with **Combobox** list items.
52
52
  - Use `SelectScrollUpButton` / `SelectScrollDownButton` for long lists.
53
- - `CoreSelect*` for plain styling.
53
+ - **`CoreSelect*`** for the same primitives without the enhanced trigger chrome (re-exported from the same `ui/select` module).
@@ -1,6 +1,6 @@
1
1
  # nqui Sheet
2
2
 
3
- > Side panel. SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter. side: top|right|bottom|left.
3
+ > Side panel (Radix **Dialog** pattern). **SheetContent** uses an **inset card** panel: transparent outer shell + `before:bg-card` block inset with **rounded corners** (matches drawer-style “card floating in the viewport”). **No edge borders** on the sheet shell (divider lines removed in favor of the card shape).
4
4
 
5
5
  ## Import
6
6
 
@@ -14,3 +14,16 @@ import { Slider } from "@nqlib/nqui"
14
14
  <Slider value={[50]} onValueChange={([v]) => setVal(v)} />
15
15
  <Slider value={[20, 80]} onValueChange={setRange} />
16
16
  ```
17
+
18
+ ## Sizes
19
+
20
+ ```tsx
21
+ <Slider size="sm" defaultValue={[40]} />
22
+ <Slider size="default" defaultValue={[40]} />
23
+ <Slider size="lg" defaultValue={[40]} />
24
+ ```
25
+
26
+ ## Notes
27
+
28
+ - **Thumb:** white, rounded-full, subtle shadow (aligned with **Switch** thumb language).
29
+ - **`size`:** `sm` | `default` | `lg` (control scale heights).