@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.
- package/INSTALLATION.md +234 -0
- package/README.md +109 -151
- package/dist/button-CJHdCq9I.js +155 -0
- package/dist/button-R304rhsj.cjs +1 -0
- package/dist/calendar.cjs.js +1 -1
- package/dist/calendar.es.js +1 -1
- package/dist/carousel-D1FMVglR.cjs +1 -0
- package/dist/carousel-U7RZhYZj.js +179 -0
- package/dist/carousel.cjs.js +1 -1
- package/dist/carousel.es.js +1 -1
- package/dist/command-palette-DCtLpM3Q.js +694 -0
- package/dist/command-palette-MHc03bBf.cjs +5 -0
- package/dist/command.cjs.js +1 -1
- package/dist/command.es.js +1 -1
- package/dist/components/custom/color-picker.d.ts +1 -1
- package/dist/components/custom/color-picker.d.ts.map +1 -1
- package/dist/components/custom/color-slider.d.ts +4 -10
- package/dist/components/custom/color-slider.d.ts.map +1 -1
- package/dist/components/custom/enhanced-radio-group.d.ts +13 -4
- package/dist/components/custom/enhanced-radio-group.d.ts.map +1 -1
- package/dist/components/custom/enhanced-tabs.d.ts.map +1 -1
- package/dist/components/debug/debug-features.d.ts +29 -0
- package/dist/components/debug/debug-features.d.ts.map +1 -0
- package/dist/components/debug/debug-panel.d.ts.map +1 -1
- package/dist/components/error-boundary.d.ts +20 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/index.d.ts +103 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +16 -5
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/button.d.ts +38 -4
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/checkbox.d.ts +16 -2
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/combobox.d.ts +2 -1
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/frosted-glass.d.ts.map +1 -1
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/input-group.d.ts.map +1 -1
- package/dist/components/ui/pagination.d.ts +3 -2
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/radio-group.d.ts +3 -1
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/select.d.ts +6 -1
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/sidebar.d.ts +1 -1
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/slider.d.ts +10 -2
- package/dist/components/ui/slider.d.ts.map +1 -1
- package/dist/components/ui/sonner.d.ts +18 -2
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/spinner.d.ts +2 -1
- package/dist/components/ui/spinner.d.ts.map +1 -1
- package/dist/components/ui/switch.d.ts +15 -2
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -1
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/ui/toggle.d.ts +1 -1
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/debug-panel-CNKk-No5.cjs +75 -0
- package/dist/debug-panel-pg39-6xw.js +9011 -0
- package/dist/debug.cjs.js +1 -0
- package/dist/debug.es.js +7 -0
- package/dist/{drawer-CU4lkcz7.js → drawer-DO26uhym.js} +31 -31
- package/dist/drawer-DVarEy65.cjs +1 -0
- package/dist/drawer.cjs.js +1 -1
- package/dist/drawer.es.js +1 -1
- package/dist/{enhanced-calendar-BENbxw7_.js → enhanced-calendar-BGlsSYJd.js} +1 -1
- package/dist/{enhanced-calendar-5PA8CeF7.cjs → enhanced-calendar-C7EQIr6i.cjs} +1 -1
- package/dist/entries/debug.d.ts +14 -0
- package/dist/entries/debug.d.ts.map +1 -0
- package/dist/entries/sonner.d.ts +1 -2
- package/dist/entries/sonner.d.ts.map +1 -1
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-scroll-spy.d.ts.map +1 -1
- package/dist/index-CI756mSv.cjs +41 -0
- package/dist/index-CgfzsUO6.js +1069 -0
- package/dist/index.d.ts +2 -98
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/index.d.ts +9 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/wrap-inline-label-text.d.ts +7 -0
- package/dist/lib/wrap-inline-label-text.d.ts.map +1 -0
- package/dist/nqui.cjs.js +49 -245
- package/dist/nqui.es.js +7402 -16735
- package/dist/sonner-CpmECDBk.js +179 -0
- package/dist/sonner-nE9hIalJ.cjs +48 -0
- package/dist/sonner.cjs.js +1 -1
- package/dist/sonner.es.js +3 -2
- package/dist/styles.css +237 -10
- package/docs/components/README.md +109 -10
- package/docs/components/nqui-badge.md +1 -0
- package/docs/components/nqui-button.md +3 -1
- package/docs/components/nqui-card.md +8 -0
- package/docs/components/nqui-carousel.md +6 -0
- package/docs/components/nqui-checkbox.md +38 -1
- package/docs/components/nqui-color-slider.md +5 -3
- package/docs/components/nqui-combobox.md +58 -37
- package/docs/components/nqui-drawer.md +1 -1
- package/docs/components/nqui-frosted-glass.md +83 -5
- package/docs/components/nqui-radio-group.md +47 -2
- package/docs/components/nqui-scroll-area.md +1 -1
- package/docs/components/nqui-select.md +2 -2
- package/docs/components/nqui-sheet.md +1 -1
- package/docs/components/nqui-slider.md +13 -0
- package/docs/components/nqui-spinner.md +6 -1
- package/docs/components/nqui-switch.md +23 -1
- package/docs/components/nqui-tabs.md +11 -1
- package/docs/components/nqui-toaster.md +5 -1
- package/docs/internal-notes/PUBLISHING.md +46 -4
- package/docs/nqui-skills/SKILL.md +106 -0
- package/docs/nqui-skills/design-system.md +143 -0
- package/docs/nqui-skills/rules/composition.md +183 -0
- package/docs/nqui-skills/rules/forms.md +190 -0
- package/docs/nqui-skills/rules/icons.md +158 -0
- package/docs/nqui-skills/rules/styling.md +192 -0
- package/package.json +23 -12
- package/scripts/build-styles.js +16 -0
- package/scripts/cli.js +1 -0
- package/scripts/download-skills.js +91 -0
- package/scripts/examples/nextjs-layout-sidebar.tsx +100 -0
- package/scripts/examples/nextjs-page-sidebar.tsx +81 -0
- package/scripts/examples/vite-app.tsx +135 -0
- package/scripts/examples/vite-main.tsx +17 -0
- package/scripts/examples.js +92 -6
- package/scripts/generate-docs.js +169 -0
- package/scripts/init-css.js +34 -14
- package/scripts/init-cursor.js +8 -0
- package/scripts/init-debug-css.js +4 -2
- package/scripts/post-install.js +41 -9
- package/scripts/publish-npmjs.js +17 -3
- package/scripts/resolve-target-dir.js +20 -1
- package/scripts/setup-helper.js +13 -1
- package/scripts/verify-build.js +1 -1
- package/scripts/wizard.js +12 -7
- package/dist/button-CYFTFDKe.cjs +0 -1
- package/dist/button-nJvDl3w8.js +0 -44
- package/dist/carousel-DEyyJi49.js +0 -179
- package/dist/carousel-Dhhz8m5V.cjs +0 -1
- package/dist/command-palette-UHk8zZOg.cjs +0 -45
- package/dist/command-palette-d-TrdBsM.js +0 -1778
- package/dist/components/custom/enhanced-badge.d.ts +0 -33
- package/dist/components/custom/enhanced-badge.d.ts.map +0 -1
- package/dist/components/custom/enhanced-button.d.ts +0 -34
- package/dist/components/custom/enhanced-button.d.ts.map +0 -1
- package/dist/components/custom/enhanced-checkbox.d.ts +0 -28
- package/dist/components/custom/enhanced-checkbox.d.ts.map +0 -1
- package/dist/components/custom/enhanced-combobox.d.ts +0 -35
- package/dist/components/custom/enhanced-combobox.d.ts.map +0 -1
- package/dist/components/custom/enhanced-select.d.ts +0 -30
- package/dist/components/custom/enhanced-select.d.ts.map +0 -1
- package/dist/components/custom/enhanced-sonner.d.ts +0 -16
- package/dist/components/custom/enhanced-sonner.d.ts.map +0 -1
- package/dist/drawer-BcIxWRN8.cjs +0 -1
- package/dist/sonner-Co6YpYVs.js +0 -546
- package/dist/sonner-DbQhVp8m.cjs +0 -330
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nqui Spinner
|
|
2
2
|
|
|
3
|
-
>
|
|
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.
|
|
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
|
|
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).
|
|
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">` |
|