@nqlib/nqui 0.4.2 → 0.4.4

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 (111) hide show
  1. package/README.md +104 -152
  2. package/dist/button-CJHdCq9I.js +155 -0
  3. package/dist/button-R304rhsj.cjs +1 -0
  4. package/dist/calendar.cjs.js +1 -1
  5. package/dist/calendar.es.js +1 -1
  6. package/dist/carousel-D1FMVglR.cjs +1 -0
  7. package/dist/carousel-U7RZhYZj.js +179 -0
  8. package/dist/carousel.cjs.js +1 -1
  9. package/dist/carousel.es.js +1 -1
  10. package/dist/{command-palette-dEJ9aEk4.js → command-palette-DCtLpM3Q.js} +1 -1
  11. package/dist/{command-palette-BuYcxPCc.cjs → command-palette-MHc03bBf.cjs} +1 -1
  12. package/dist/command.cjs.js +1 -1
  13. package/dist/command.es.js +1 -1
  14. package/dist/components/custom/color-picker.d.ts +1 -1
  15. package/dist/components/custom/color-picker.d.ts.map +1 -1
  16. package/dist/components/custom/color-slider.d.ts +4 -10
  17. package/dist/components/custom/color-slider.d.ts.map +1 -1
  18. package/dist/components/debug/debug-features.d.ts +29 -0
  19. package/dist/components/debug/debug-features.d.ts.map +1 -0
  20. package/dist/components/debug/debug-panel.d.ts.map +1 -1
  21. package/dist/components/index.d.ts +14 -13
  22. package/dist/components/index.d.ts.map +1 -1
  23. package/dist/components/ui/badge.d.ts +15 -4
  24. package/dist/components/ui/badge.d.ts.map +1 -1
  25. package/dist/components/ui/button.d.ts +37 -3
  26. package/dist/components/ui/button.d.ts.map +1 -1
  27. package/dist/components/ui/checkbox.d.ts +12 -1
  28. package/dist/components/ui/checkbox.d.ts.map +1 -1
  29. package/dist/components/ui/combobox.d.ts +2 -1
  30. package/dist/components/ui/combobox.d.ts.map +1 -1
  31. package/dist/components/ui/pagination.d.ts +3 -2
  32. package/dist/components/ui/pagination.d.ts.map +1 -1
  33. package/dist/components/ui/select.d.ts +6 -1
  34. package/dist/components/ui/select.d.ts.map +1 -1
  35. package/dist/components/ui/sidebar.d.ts +1 -1
  36. package/dist/components/ui/sidebar.d.ts.map +1 -1
  37. package/dist/components/ui/slider.d.ts +10 -2
  38. package/dist/components/ui/slider.d.ts.map +1 -1
  39. package/dist/components/ui/sonner.d.ts +18 -2
  40. package/dist/components/ui/sonner.d.ts.map +1 -1
  41. package/dist/components/ui/spinner.d.ts +2 -1
  42. package/dist/components/ui/spinner.d.ts.map +1 -1
  43. package/dist/components/ui/switch.d.ts +15 -2
  44. package/dist/components/ui/switch.d.ts.map +1 -1
  45. package/dist/components/ui/tabs.d.ts +1 -1
  46. package/dist/components/ui/tabs.d.ts.map +1 -1
  47. package/dist/components/ui/toggle.d.ts +1 -1
  48. package/dist/components/ui/toggle.d.ts.map +1 -1
  49. package/dist/{debug-panel-AjzBdMMz.js → debug-panel-CG-vAN0L.js} +3593 -3775
  50. package/dist/debug-panel-DHBfAc1V.cjs +75 -0
  51. package/dist/debug.cjs.js +1 -1
  52. package/dist/debug.es.js +1 -1
  53. package/dist/{drawer-pUXPg3lF.js → drawer-DO26uhym.js} +31 -31
  54. package/dist/drawer-DVarEy65.cjs +1 -0
  55. package/dist/drawer.cjs.js +1 -1
  56. package/dist/drawer.es.js +1 -1
  57. package/dist/{enhanced-calendar-BENbxw7_.js → enhanced-calendar-BGlsSYJd.js} +1 -1
  58. package/dist/{enhanced-calendar-5PA8CeF7.cjs → enhanced-calendar-C7EQIr6i.cjs} +1 -1
  59. package/dist/entries/sonner.d.ts +1 -2
  60. package/dist/entries/sonner.d.ts.map +1 -1
  61. package/dist/lib/wrap-inline-label-text.d.ts +7 -0
  62. package/dist/lib/wrap-inline-label-text.d.ts.map +1 -0
  63. package/dist/nqui.cjs.js +21 -47
  64. package/dist/nqui.es.js +2236 -2381
  65. package/dist/sonner-CpmECDBk.js +179 -0
  66. package/dist/sonner-nE9hIalJ.cjs +48 -0
  67. package/dist/sonner.cjs.js +1 -1
  68. package/dist/sonner.es.js +3 -2
  69. package/dist/styles.css +183 -1
  70. package/docs/components/README.md +8 -7
  71. package/docs/components/nqui-badge.md +1 -0
  72. package/docs/components/nqui-button.md +3 -1
  73. package/docs/components/nqui-card.md +1 -0
  74. package/docs/components/nqui-carousel.md +6 -0
  75. package/docs/components/nqui-checkbox.md +15 -0
  76. package/docs/components/nqui-color-slider.md +5 -3
  77. package/docs/components/nqui-combobox.md +58 -37
  78. package/docs/components/nqui-drawer.md +1 -1
  79. package/docs/components/nqui-scroll-area.md +1 -1
  80. package/docs/components/nqui-select.md +2 -2
  81. package/docs/components/nqui-sheet.md +1 -1
  82. package/docs/components/nqui-slider.md +13 -0
  83. package/docs/components/nqui-spinner.md +6 -1
  84. package/docs/components/nqui-switch.md +23 -1
  85. package/docs/components/nqui-toaster.md +5 -1
  86. package/docs/nqui-skills/SKILL.md +8 -1
  87. package/docs/nqui-skills/design-system.md +16 -3
  88. package/package.json +1 -1
  89. package/scripts/build-styles.js +16 -0
  90. package/scripts/init-debug-css.js +4 -2
  91. package/scripts/verify-build.js +1 -1
  92. package/dist/button-CYFTFDKe.cjs +0 -1
  93. package/dist/button-nJvDl3w8.js +0 -44
  94. package/dist/carousel-DEyyJi49.js +0 -179
  95. package/dist/carousel-Dhhz8m5V.cjs +0 -1
  96. package/dist/components/custom/enhanced-badge.d.ts +0 -33
  97. package/dist/components/custom/enhanced-badge.d.ts.map +0 -1
  98. package/dist/components/custom/enhanced-button.d.ts +0 -39
  99. package/dist/components/custom/enhanced-button.d.ts.map +0 -1
  100. package/dist/components/custom/enhanced-checkbox.d.ts +0 -39
  101. package/dist/components/custom/enhanced-checkbox.d.ts.map +0 -1
  102. package/dist/components/custom/enhanced-combobox.d.ts +0 -35
  103. package/dist/components/custom/enhanced-combobox.d.ts.map +0 -1
  104. package/dist/components/custom/enhanced-select.d.ts +0 -30
  105. package/dist/components/custom/enhanced-select.d.ts.map +0 -1
  106. package/dist/components/custom/enhanced-sonner.d.ts +0 -15
  107. package/dist/components/custom/enhanced-sonner.d.ts.map +0 -1
  108. package/dist/debug-panel-NaOmD68t.cjs +0 -171
  109. package/dist/drawer-Cqq0Ozb2.cjs +0 -1
  110. package/dist/sonner-BtzU00r3.js +0 -248
  111. package/dist/sonner-Dfk26eds.cjs +0 -54
@@ -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,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).
@@ -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.
@@ -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`.
@@ -40,6 +40,8 @@ When designing app UI (toolbars, headers, inline controls):
40
40
 
41
41
  ## Design System Conventions
42
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
+
43
45
  ### Control Sizing
44
46
  - sm = h-6
45
47
  - default = h-7
@@ -57,10 +59,15 @@ Always use CSS variables from elevation.css:
57
59
  - `--z-tooltip` (70)
58
60
 
59
61
  ### Component Naming
60
- - Enhanced vs Core: default is enhanced; use Core* for plain
62
+ - Default exports are the enhanced/polished variants; use **Core\*** for plain primitives
63
+ - Implementations are consolidated under **`ui/`** (not separate `custom/enhanced-*` per component for Button, Badge, Checkbox, Select, Combobox, Sonner)
61
64
  - File names: kebab-case
62
65
  - Component names: PascalCase
63
66
 
67
+ ### Hit area (optional)
68
+
69
+ 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.
70
+
64
71
  ## Key Dependencies
65
72
 
66
73
  Required peer dependencies:
@@ -77,14 +77,15 @@ Font: `--font-sans` (Inter Variable). Leading: `leading-normal` or `text-xs/rela
77
77
  1. **Use the scale** – If the component has a `size` prop, map `sm`→h-6, `default`→h-7, `lg`→h-8.
78
78
  2. **Match padding** – Text controls: px-2–3, py-1.5. Icon-only: p-0 with explicit size.
79
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. For nested layouts, use `calc(outer - offset)`.
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
81
 
82
82
  ## Grouped Controls (ButtonGroup, ToggleGroup)
83
83
 
84
- - **Shared border** – Container: `rounded-md border border-input overflow-hidden`
84
+ - **Shared border** – Container: `rounded-full border border-input overflow-hidden` (pill outer edge)
85
85
  - **Child borders** – Items: `border-0` (container provides the border)
86
86
  - **Dividers** – ToggleGroup: item borders (`border-foreground/20`) between items. Or `ToggleGroupSeparator` when `separator={false}`.
87
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.
88
89
 
89
90
  ## Toggle & ToggleGroup Visual Treatment (Visibility on Any Background)
90
91
 
@@ -108,12 +109,24 @@ Never use flat `bg-muted` only for selected state; always add gradient + shadow
108
109
  | Chart/settings panel | Label + inline controls, `rounded-lg border bg-muted/30 p-3` | Y-axis: Linear/Log; Size: S/M/L |
109
110
  | Standalone | Inline with related UI, not floating alone | Pin, Mute, single Toggle |
110
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
+
111
120
  ## Customization
112
121
 
113
122
  - End users override via `className` or `size` when supported.
114
123
  - Do NOT hardcode heights in consumer code when the component supports `size`.
115
124
  - Prefer semantic sizes over pixel values in component defaults.
116
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
+
117
130
  ## Files to Check for Consistency
118
131
 
119
132
  - `packages/nqui/src/components/ui/button.tsx`
@@ -121,7 +134,7 @@ Never use flat `bg-muted` only for selected state; always add gradient + shadow
121
134
  - `packages/nqui/src/components/ui/toggle-group.tsx`
122
135
  - `packages/nqui/src/components/ui/input.tsx`
123
136
  - `packages/nqui/src/components/ui/select.tsx`
124
- - `packages/nqui/src/components/custom/enhanced-button.tsx`
137
+ - `packages/nqui/src/components/ui/combobox.tsx`
125
138
 
126
139
  ## Anti-Patterns
127
140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nqlib/nqui",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "A React component library with enhanced UI components and developer tools",
5
5
  "type": "module",
6
6
  "main": "./dist/nqui.cjs.js",
@@ -19,6 +19,7 @@ const projectRoot = resolve(__dirname, '..');
19
19
  const indexCssPath = join(projectRoot, 'src', 'index.css');
20
20
  const colorsCssPath = join(projectRoot, 'src', 'styles', 'colors.css');
21
21
  const elevationCssPath = join(projectRoot, 'src', 'styles', 'elevation.css');
22
+ const hitAreaCssPath = join(projectRoot, 'src', 'styles', 'hit-area.css');
22
23
  const outputPath = join(projectRoot, 'dist', 'styles.css');
23
24
 
24
25
  function extractStandaloneCSS() {
@@ -30,6 +31,11 @@ function extractStandaloneCSS() {
30
31
  throw new Error(`Colors CSS file not found: ${colorsCssPath}`);
31
32
  }
32
33
 
34
+ let hitAreaCss = '';
35
+ if (existsSync(hitAreaCssPath)) {
36
+ hitAreaCss = readFileSync(hitAreaCssPath, 'utf-8').trimEnd();
37
+ }
38
+
33
39
  let indexCss = readFileSync(indexCssPath, 'utf-8');
34
40
  let colorsCss = readFileSync(colorsCssPath, 'utf-8');
35
41
  let elevationCss = '';
@@ -110,14 +116,23 @@ function extractStandaloneCSS() {
110
116
  .replace(/@import\s+["']@fontsource-variable\/inter["'];?\s*\n/g, '')
111
117
  .replace(/@import\s+["']\.\/styles\/colors\.css["'];?\s*\n/g, '')
112
118
  .replace(/@import\s+["']\.\/styles\/elevation\.css["'];?\s*\n/g, '')
119
+ .replace(/@import\s+["']\.\/styles\/hit-area\.css["'];?\s*\n/g, '')
113
120
  .replace(/\/\*\s*Import enhanced color system\s*\*\//g, '')
114
121
  .replace(/\/\*\s*Import elevation system\s*\*\//g, '')
122
+ .replace(/\/\*\s*Hit-area utilities \(expanded pointer targets\)\s*\*\/\s*\n/g, '')
115
123
  // Remove @source inline() directives (already extracted above) - must match multiline
116
124
  .replace(/\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/\s*@source\s+inline\([\s\S]*?\)\s*;/g, '')
117
125
  // Remove other @source directives (non-inline ones)
118
126
  .replace(/@source\s+(?!inline\()[^;]+;?\s*\n/g, '')
119
127
  .replace(/@custom-variant\s+[^;]+;?\s*\n/g, '');
120
128
 
129
+ if (hitAreaCss) {
130
+ indexCss = indexCss.replace(
131
+ /@theme inline\s*\{/,
132
+ `/* Hit-area — https://bazza.dev/craft/2026/hit-area */\n${hitAreaCss}\n\n@theme inline {`
133
+ );
134
+ }
135
+
121
136
  // Extract :root and .dark blocks from index.css
122
137
  const indexRootMatch = indexCss.match(/:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
123
138
  const indexDarkMatch = indexCss.match(/\.dark\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
@@ -232,6 +247,7 @@ function extractStandaloneCSS() {
232
247
  * - Light and dark mode support
233
248
  * - Base layer styles
234
249
  * - Utility animations
250
+ * - Hit-area @utility blocks (inlined from src/styles/hit-area.css)
235
251
  * - @source inline() directives for zero-config Tailwind utility generation
236
252
  *
237
253
  * Generated by: npm run build:lib
@@ -114,8 +114,10 @@ function main() {
114
114
  console.log(' 1. Import the CSS in your app entry point:');
115
115
  console.log(` import './${finalTargetPath.replace(process.cwd() + '/', '')}'`);
116
116
  console.log(' 2. Use DebugPanel in your app:');
117
- console.log(' import { DebugPanel } from "nqui"');
118
- console.log(' 3. Add DebugPanel to your root component');
117
+ console.log(' import { DebugPanel } from "@nqlib/nqui"');
118
+ console.log(' // Or tree-shake debug out of production: import { DebugPanel } from "@nqlib/nqui/debug"');
119
+ console.log(' 3. Add <DebugPanel /> to your root layout (panel is inactive until the user opens it).');
120
+ console.log(' Wrapping in process.env.NODE_ENV or import.meta.env.DEV is optional.');
119
121
  console.log('\n✨ Done!');
120
122
  }
121
123
 
@@ -284,7 +284,7 @@ function main() {
284
284
  { name: '@source inline() directives', pattern: /@source\s+inline\(/g },
285
285
  { name: ':root block', pattern: /:root\s*\{/ },
286
286
  { name: '.dark block', pattern: /\.dark\s*\{/ },
287
- { name: 'Viewport lock (html, body, #root)', pattern: /html\s*,\s*\n?\s*body\s*,\s*\n?\s*#root\s*\{[^}]*height:\s*100%[^}]*overflow:\s*hidden/s },
287
+ // Viewport lock (html, body, #root) was intentionally removed from base styles (see CHANGELOG 0.3.3); use AppLayout for opt-in lock.
288
288
  ];
289
289
 
290
290
  criticalPatterns.forEach(({ name, pattern }) => {
@@ -1 +0,0 @@
1
- "use strict";const u=require("react/jsx-runtime"),d=require("react"),l=require("@radix-ui/react-slot"),f=require("class-variance-authority"),g=require("./utils-IjLH3w2e.cjs");function b(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const t in e)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:()=>e[t]})}}return r.default=e,Object.freeze(r)}const v=b(d),o=f.cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-7 min-w-7 px-3",sm:"h-6 min-w-6 rounded-[min(var(--radius-md),8px)] px-2 text-xs",lg:"h-8 min-w-8 px-4",icon:"h-7 w-7 p-0"}},defaultVariants:{variant:"default",size:"default"}}),i=v.forwardRef(({className:e,variant:r,size:t,asChild:n=!1,...s},a)=>{const c=n?l.Slot:"button";return u.jsx(c,{className:g.cn(o({variant:r,size:t,className:e})),ref:a,...s})});i.displayName="Button";exports.Button=i;exports.buttonVariants=o;
@@ -1,44 +0,0 @@
1
- import { jsx as s } from "react/jsx-runtime";
2
- import * as a from "react";
3
- import { Slot as d } from "@radix-ui/react-slot";
4
- import { cva as c } from "class-variance-authority";
5
- import { c as u } from "./utils-B6yFEsav.js";
6
- const f = c(
7
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
- {
9
- variants: {
10
- variant: {
11
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
- destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
13
- outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
14
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
- ghost: "hover:bg-accent hover:text-accent-foreground",
16
- link: "text-primary underline-offset-4 hover:underline"
17
- },
18
- size: {
19
- default: "h-7 min-w-7 px-3",
20
- sm: "h-6 min-w-6 rounded-[min(var(--radius-md),8px)] px-2 text-xs",
21
- lg: "h-8 min-w-8 px-4",
22
- icon: "h-7 w-7 p-0"
23
- }
24
- },
25
- defaultVariants: {
26
- variant: "default",
27
- size: "default"
28
- }
29
- }
30
- ), m = a.forwardRef(
31
- ({ className: e, variant: r, size: t, asChild: o = !1, ...n }, i) => /* @__PURE__ */ s(
32
- o ? d : "button",
33
- {
34
- className: u(f({ variant: r, size: t, className: e })),
35
- ref: i,
36
- ...n
37
- }
38
- )
39
- );
40
- m.displayName = "Button";
41
- export {
42
- m as B,
43
- f as b
44
- };
@@ -1,179 +0,0 @@
1
- import { jsx as a, jsxs as p } from "react/jsx-runtime";
2
- import * as l from "react";
3
- import z from "embla-carousel-react";
4
- import { K as C } from "./keyboard-pkY42Y3a.js";
5
- import { c as d } from "./utils-B6yFEsav.js";
6
- import { B as v } from "./button-nJvDl3w8.js";
7
- import { HugeiconsIcon as N } from "@hugeicons/react";
8
- import { ArrowLeft01Icon as I, ArrowRight01Icon as R } from "@hugeicons/core-free-icons";
9
- const b = l.createContext(null);
10
- function m() {
11
- const e = l.useContext(b);
12
- if (!e)
13
- throw new Error("useCarousel must be used within a <Carousel />");
14
- return e;
15
- }
16
- function W({
17
- orientation: e = "horizontal",
18
- opts: t,
19
- setApi: r,
20
- plugins: s,
21
- className: c,
22
- children: i,
23
- ...u
24
- }) {
25
- const [w, o] = z(
26
- {
27
- ...t,
28
- axis: e === "horizontal" ? "x" : "y"
29
- },
30
- s
31
- ), [k, y] = l.useState(!1), [S, P] = l.useState(!1), f = l.useCallback((n) => {
32
- n && (y(n.canScrollPrev()), P(n.canScrollNext()));
33
- }, []), h = l.useCallback(() => {
34
- o?.scrollPrev();
35
- }, [o]), x = l.useCallback(() => {
36
- o?.scrollNext();
37
- }, [o]), g = l.useCallback(
38
- (n) => {
39
- n.key === C.ArrowLeft ? (n.preventDefault(), h()) : n.key === C.ArrowRight && (n.preventDefault(), x());
40
- },
41
- [h, x]
42
- );
43
- return l.useEffect(() => {
44
- !o || !r || r(o);
45
- }, [o, r]), l.useEffect(() => {
46
- if (o)
47
- return f(o), o.on("reInit", f), o.on("select", f), () => {
48
- o?.off("select", f);
49
- };
50
- }, [o, f]), /* @__PURE__ */ a(
51
- b.Provider,
52
- {
53
- value: {
54
- carouselRef: w,
55
- api: o,
56
- opts: t,
57
- orientation: e || (t?.axis === "y" ? "vertical" : "horizontal"),
58
- scrollPrev: h,
59
- scrollNext: x,
60
- canScrollPrev: k,
61
- canScrollNext: S
62
- },
63
- children: /* @__PURE__ */ a(
64
- "div",
65
- {
66
- onKeyDownCapture: g,
67
- className: d("relative", c),
68
- role: "region",
69
- "aria-roledescription": "carousel",
70
- "data-slot": "carousel",
71
- ...u,
72
- children: i
73
- }
74
- )
75
- }
76
- );
77
- }
78
- function H({ className: e, ...t }) {
79
- const { carouselRef: r, orientation: s } = m();
80
- return /* @__PURE__ */ a(
81
- "div",
82
- {
83
- ref: r,
84
- className: "overflow-hidden",
85
- "data-slot": "carousel-content",
86
- children: /* @__PURE__ */ a(
87
- "div",
88
- {
89
- className: d(
90
- "flex",
91
- s === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
92
- e
93
- ),
94
- ...t
95
- }
96
- )
97
- }
98
- );
99
- }
100
- function q({ className: e, ...t }) {
101
- const { orientation: r } = m();
102
- return /* @__PURE__ */ a(
103
- "div",
104
- {
105
- role: "group",
106
- "aria-roledescription": "slide",
107
- "data-slot": "carousel-item",
108
- className: d(
109
- "min-w-0 shrink-0 grow-0 basis-full",
110
- r === "horizontal" ? "pl-4" : "pt-4",
111
- e
112
- ),
113
- ...t
114
- }
115
- );
116
- }
117
- function F({
118
- className: e,
119
- variant: t = "outline",
120
- size: r = "icon",
121
- ...s
122
- }) {
123
- const { orientation: c, scrollPrev: i, canScrollPrev: u } = m();
124
- return /* @__PURE__ */ p(
125
- v,
126
- {
127
- "data-slot": "carousel-previous",
128
- variant: t,
129
- size: r,
130
- className: d(
131
- "rounded-full absolute touch-manipulation",
132
- c === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
133
- e
134
- ),
135
- disabled: !u,
136
- onClick: i,
137
- ...s,
138
- children: [
139
- /* @__PURE__ */ a(N, { icon: I, strokeWidth: 2 }),
140
- /* @__PURE__ */ a("span", { className: "sr-only", children: "Previous slide" })
141
- ]
142
- }
143
- );
144
- }
145
- function G({
146
- className: e,
147
- variant: t = "outline",
148
- size: r = "icon",
149
- ...s
150
- }) {
151
- const { orientation: c, scrollNext: i, canScrollNext: u } = m();
152
- return /* @__PURE__ */ p(
153
- v,
154
- {
155
- "data-slot": "carousel-next",
156
- variant: t,
157
- size: r,
158
- className: d(
159
- "rounded-full absolute touch-manipulation",
160
- c === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
161
- e
162
- ),
163
- disabled: !u,
164
- onClick: i,
165
- ...s,
166
- children: [
167
- /* @__PURE__ */ a(N, { icon: R, strokeWidth: 2 }),
168
- /* @__PURE__ */ a("span", { className: "sr-only", children: "Next slide" })
169
- ]
170
- }
171
- );
172
- }
173
- export {
174
- W as C,
175
- H as a,
176
- q as b,
177
- F as c,
178
- G as d
179
- };