@nqlib/nqui 0.4.8 → 0.5.1

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 (63) hide show
  1. package/dist/{button-CJHdCq9I.js → button-BrroJ39H.js} +35 -34
  2. package/dist/button-CrLihxcE.cjs +1 -0
  3. package/dist/calendar.cjs.js +1 -1
  4. package/dist/calendar.es.js +1 -1
  5. package/dist/{carousel-U7RZhYZj.js → carousel-C976t5jo.js} +1 -1
  6. package/dist/{carousel-D1FMVglR.cjs → carousel-VldrniuT.cjs} +1 -1
  7. package/dist/carousel.cjs.js +1 -1
  8. package/dist/carousel.es.js +1 -1
  9. package/dist/{command-palette-D_SFxXyQ.js → command-palette-2V1VSlOM.js} +1 -1
  10. package/dist/{command-palette-DSQYbRiH.cjs → command-palette-4Sv4QjZ2.cjs} +1 -1
  11. package/dist/command.cjs.js +1 -1
  12. package/dist/command.es.js +1 -1
  13. package/dist/components/custom/enhanced-tabs.d.ts +1 -0
  14. package/dist/components/custom/enhanced-tabs.d.ts.map +1 -1
  15. package/dist/components/ui/button.d.ts +4 -0
  16. package/dist/components/ui/button.d.ts.map +1 -1
  17. package/dist/components/ui/card.d.ts +1 -0
  18. package/dist/components/ui/card.d.ts.map +1 -1
  19. package/dist/components/ui/sonner.d.ts.map +1 -1
  20. package/dist/components/ui/tabs.d.ts +6 -4
  21. package/dist/components/ui/tabs.d.ts.map +1 -1
  22. package/dist/{debug-panel-CS_wuYVH.js → debug-panel-BnZDhlaR.js} +1621 -1749
  23. package/dist/{debug-panel-nDTrIh9s.cjs → debug-panel-R2ZLkucO.cjs} +10 -10
  24. package/dist/debug.cjs.js +1 -1
  25. package/dist/debug.es.js +1 -1
  26. package/dist/{enhanced-calendar-C7EQIr6i.cjs → enhanced-calendar-BEIfybRx.cjs} +1 -1
  27. package/dist/{enhanced-calendar-BGlsSYJd.js → enhanced-calendar-Cj-4xhqf.js} +1 -1
  28. package/dist/hooks/use-resolved-theme.d.ts +2 -4
  29. package/dist/hooks/use-resolved-theme.d.ts.map +1 -1
  30. package/dist/nqui.cjs.js +3 -23
  31. package/dist/nqui.es.js +605 -627
  32. package/dist/{sonner-CpmECDBk.js → sonner-C1ndgVQY.js} +24 -24
  33. package/dist/sonner-CP8np0BP.cjs +48 -0
  34. package/dist/sonner.cjs.js +1 -1
  35. package/dist/sonner.es.js +1 -1
  36. package/dist/styles.css +79 -84
  37. package/docs/components/README.md +11 -2
  38. package/docs/components/nqui-scroll-area.md +74 -0
  39. package/docs/components/nqui-tooltip.md +17 -2
  40. package/docs/nqui-skills/COMPONENTS_INDEX.md +48 -0
  41. package/docs/nqui-skills/HUMAN_GUIDE.md +21 -0
  42. package/docs/nqui-skills/README.md +20 -0
  43. package/docs/nqui-skills/SKILL.md +36 -89
  44. package/docs/nqui-skills/nqui-bundle-size-best-practices/SKILL.md +78 -0
  45. package/docs/nqui-skills/nqui-components/SKILL.md +101 -0
  46. package/docs/nqui-skills/{design-system.md → nqui-design-system/SKILL.md} +53 -25
  47. package/docs/nqui-skills/nqui-install/SKILL.md +65 -0
  48. package/docs/nqui-skills/nqui-local-published-toggle/SKILL.md +132 -0
  49. package/docs/nqui-skills/nqui-local-published-toggle/scripts/toggle-nqui.js +203 -0
  50. package/docs/nqui-skills/nqui-shadcn/SKILL.md +164 -0
  51. package/docs/nqui-skills/{rules → nqui-shadcn/rules}/forms.md +3 -3
  52. package/docs/nqui-skills/{rules → nqui-shadcn/rules}/styling.md +1 -1
  53. package/package.json +1 -1
  54. package/scripts/download-skills.js +28 -8
  55. package/scripts/examples/nextjs-layout-sidebar.tsx +3 -2
  56. package/scripts/examples/nextjs-layout.tsx +3 -2
  57. package/scripts/examples/vite-main.tsx +7 -1
  58. package/scripts/init-cursor.js +4 -3
  59. package/scripts/skill-templates.js +16 -6
  60. package/dist/button-R304rhsj.cjs +0 -1
  61. package/dist/sonner-nE9hIalJ.cjs +0 -48
  62. /package/docs/nqui-skills/{rules → nqui-shadcn/rules}/composition.md +0 -0
  63. /package/docs/nqui-skills/{rules → nqui-shadcn/rules}/icons.md +0 -0
@@ -1,36 +1,36 @@
1
1
  import { jsx as a } from "react/jsx-runtime";
2
- import * as i from "react";
2
+ import * as c from "react";
3
3
  import { Toaster as T } from "sonner";
4
- import { HugeiconsIcon as s } from "@hugeicons/react";
4
+ import { HugeiconsIcon as i } from "@hugeicons/react";
5
5
  import { Loading03Icon as S, MultiplicationSignCircleIcon as I, Alert02Icon as E, InformationCircleIcon as C, CheckmarkCircle02Icon as N } from "@hugeicons/core-free-icons";
6
- var z = (t, n, p, c, l, r, u, f) => {
6
+ var z = (t, n, s, l, d, r, u, f) => {
7
7
  let e = document.documentElement, g = ["light", "dark"];
8
- function d(o) {
9
- (Array.isArray(t) ? t : [t]).forEach((m) => {
10
- let v = m === "class", k = v && r ? l.map((x) => r[x] || x) : l;
11
- v ? (e.classList.remove(...k), e.classList.add(r && r[o] ? r[o] : o)) : e.setAttribute(m, o);
8
+ function m(o) {
9
+ (Array.isArray(t) ? t : [t]).forEach((p) => {
10
+ let v = p === "class", y = v && r ? d.map((x) => r[x] || x) : d;
11
+ v ? (e.classList.remove(...y), e.classList.add(r && r[o] ? r[o] : o)) : e.setAttribute(p, o);
12
12
  }), w(o);
13
13
  }
14
14
  function w(o) {
15
15
  f && g.includes(o) && (e.style.colorScheme = o);
16
16
  }
17
- function y() {
17
+ function k() {
18
18
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
19
19
  }
20
- if (c) d(c);
20
+ if (l) m(l);
21
21
  else try {
22
- let o = localStorage.getItem(n) || p, m = u && o === "system" ? y() : o;
23
- d(m);
22
+ let o = localStorage.getItem(n) || s, p = u && o === "system" ? k() : o;
23
+ m(p);
24
24
  } catch {
25
25
  }
26
- }, W = i.createContext(void 0), A = { setTheme: (t) => {
26
+ }, W = c.createContext(void 0), A = { setTheme: (t) => {
27
27
  }, themes: [] }, L = () => {
28
28
  var t;
29
- return (t = i.useContext(W)) != null ? t : A;
29
+ return (t = c.useContext(W)) != null ? t : A;
30
30
  };
31
- i.memo(({ forcedTheme: t, storageKey: n, attribute: p, enableSystem: c, enableColorScheme: l, defaultTheme: r, value: u, themes: f, nonce: e, scriptProps: g }) => {
32
- let d = JSON.stringify([p, n, r, t, f, u, c, l]).slice(1, -1);
33
- return i.createElement("script", { ...g, suppressHydrationWarning: !0, nonce: typeof window > "u" ? e : "", dangerouslySetInnerHTML: { __html: `(${z.toString()})(${d})` } });
31
+ c.memo(({ forcedTheme: t, storageKey: n, attribute: s, enableSystem: l, enableColorScheme: d, defaultTheme: r, value: u, themes: f, nonce: e, scriptProps: g }) => {
32
+ let m = JSON.stringify([s, n, r, t, f, u, l, d]).slice(1, -1);
33
+ return c.createElement("script", { ...g, suppressHydrationWarning: !0, nonce: typeof window > "u" ? e : "", dangerouslySetInnerHTML: { __html: `(${z.toString()})(${m})` } });
34
34
  });
35
35
  const b = "nqui-toast-styles-v2";
36
36
  function M() {
@@ -86,17 +86,17 @@ function M() {
86
86
  `, document.head.appendChild(t);
87
87
  }
88
88
  const h = ({ ...t }) => {
89
- const { theme: n = "system" } = L();
90
- return i.useEffect(() => {
89
+ const { theme: n = "light" } = L(), s = n === "dark" ? "dark" : "light";
90
+ return c.useEffect(() => {
91
91
  M();
92
92
  }, []), /* @__PURE__ */ a(
93
93
  T,
94
94
  {
95
- theme: n,
95
+ theme: s,
96
96
  className: "toaster group",
97
97
  icons: {
98
98
  success: /* @__PURE__ */ a(
99
- s,
99
+ i,
100
100
  {
101
101
  icon: N,
102
102
  strokeWidth: 2,
@@ -104,7 +104,7 @@ const h = ({ ...t }) => {
104
104
  }
105
105
  ),
106
106
  info: /* @__PURE__ */ a(
107
- s,
107
+ i,
108
108
  {
109
109
  icon: C,
110
110
  strokeWidth: 2,
@@ -112,7 +112,7 @@ const h = ({ ...t }) => {
112
112
  }
113
113
  ),
114
114
  warning: /* @__PURE__ */ a(
115
- s,
115
+ i,
116
116
  {
117
117
  icon: E,
118
118
  strokeWidth: 2,
@@ -120,7 +120,7 @@ const h = ({ ...t }) => {
120
120
  }
121
121
  ),
122
122
  error: /* @__PURE__ */ a(
123
- s,
123
+ i,
124
124
  {
125
125
  icon: I,
126
126
  strokeWidth: 2,
@@ -128,7 +128,7 @@ const h = ({ ...t }) => {
128
128
  }
129
129
  ),
130
130
  loading: /* @__PURE__ */ a(
131
- s,
131
+ i,
132
132
  {
133
133
  icon: S,
134
134
  strokeWidth: 2,
@@ -0,0 +1,48 @@
1
+ "use strict";const c=require("react/jsx-runtime"),T=require("react"),j=require("sonner"),i=require("@hugeicons/react"),l=require("@hugeicons/core-free-icons");function E(t){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const o in t)if(o!=="default"){const n=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(r,o,n.get?n:{enumerable:!0,get:()=>t[o]})}}return r.default=t,Object.freeze(r)}const d=E(T);var N=(t,r,o,n,u,a,f,h)=>{let s=document.documentElement,x=["light","dark"];function m(e){(Array.isArray(t)?t:[t]).forEach(g=>{let v=g==="class",S=v&&a?u.map(b=>a[b]||b):u;v?(s.classList.remove(...S),s.classList.add(a&&a[e]?a[e]:e)):s.setAttribute(g,e)}),y(e)}function y(e){h&&x.includes(e)&&(s.style.colorScheme=e)}function I(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}if(n)m(n);else try{let e=localStorage.getItem(r)||o,g=f&&e==="system"?I():e;m(g)}catch{}},C=d.createContext(void 0),O={setTheme:t=>{},themes:[]},k=()=>{var t;return(t=d.useContext(C))!=null?t:O};d.memo(({forcedTheme:t,storageKey:r,attribute:o,enableSystem:n,enableColorScheme:u,defaultTheme:a,value:f,themes:h,nonce:s,scriptProps:x})=>{let m=JSON.stringify([o,r,a,t,h,f,n,u]).slice(1,-1);return d.createElement("script",{...x,suppressHydrationWarning:!0,nonce:typeof window>"u"?s:"",dangerouslySetInnerHTML:{__html:`(${N.toString()})(${m})`}})});const w="nqui-toast-styles-v2";function z(){if(typeof document>"u"||document.getElementById(w))return;const t=document.createElement("style");t.id=w,t.textContent=`
2
+ /* Pill toast — inverted surface: dark-on-light app, light-on-dark app */
3
+ [data-sonner-toast] .cn-toast {
4
+ border: 1px solid color-mix(in oklch, var(--normal-text) 18%, transparent) !important;
5
+ border-radius: 9999px !important;
6
+ box-shadow:
7
+ 0 1px 0 0 color-mix(in oklch, var(--normal-text) 12%, transparent),
8
+ 0 1px 2px 0 oklch(0.15 0 0 / 0.08);
9
+ transition: all 200ms ease-in-out !important;
10
+ }
11
+
12
+ .dark [data-sonner-toast] .cn-toast {
13
+ box-shadow:
14
+ 0 1px 0 0 color-mix(in oklch, var(--normal-text) 14%, transparent),
15
+ 0 1px 2px 0 oklch(0.15 0 0 / 0.12);
16
+ }
17
+
18
+ [data-sonner-toast] .cn-toast.toast-success {
19
+ border-left-width: 3px !important;
20
+ border-left-color: var(--success-500) !important;
21
+ }
22
+
23
+ [data-sonner-toast] .cn-toast.toast-error {
24
+ border-left-width: 3px !important;
25
+ border-left-color: var(--danger-500) !important;
26
+ }
27
+
28
+ [data-sonner-toast] .cn-toast.toast-warning {
29
+ border-left-width: 3px !important;
30
+ border-left-color: var(--warning-500) !important;
31
+ }
32
+
33
+ [data-sonner-toast] .cn-toast.toast-info {
34
+ border-left-width: 3px !important;
35
+ border-left-color: var(--info-500) !important;
36
+ }
37
+
38
+ [data-sonner-toast] .cn-toast.toast-loading {
39
+ border-left-width: 3px !important;
40
+ border-left-color: var(--primary-500) !important;
41
+ }
42
+
43
+ .cn-toast .toast-icon-success { color: var(--success); }
44
+ .cn-toast .toast-icon-error { color: var(--destructive); }
45
+ .cn-toast .toast-icon-warning { color: var(--warning); }
46
+ .cn-toast .toast-icon-info { color: var(--info); }
47
+ .cn-toast .toast-icon-loading { color: var(--primary); }
48
+ `,document.head.appendChild(t)}const p=({...t})=>{const{theme:r="light"}=k(),o=r==="dark"?"dark":"light";return d.useEffect(()=>{z()},[]),c.jsx(j.Toaster,{theme:o,className:"toaster group",icons:{success:c.jsx(i.HugeiconsIcon,{icon:l.CheckmarkCircle02Icon,strokeWidth:2,className:"size-4 text-success toast-icon-success"}),info:c.jsx(i.HugeiconsIcon,{icon:l.InformationCircleIcon,strokeWidth:2,className:"size-4 text-info toast-icon-info"}),warning:c.jsx(i.HugeiconsIcon,{icon:l.Alert02Icon,strokeWidth:2,className:"size-4 text-warning toast-icon-warning"}),error:c.jsx(i.HugeiconsIcon,{icon:l.MultiplicationSignCircleIcon,strokeWidth:2,className:"size-4 text-destructive toast-icon-error"}),loading:c.jsx(i.HugeiconsIcon,{icon:l.Loading03Icon,strokeWidth:2,className:"size-4 text-primary toast-icon-loading animate-spin"})},style:{"--normal-bg":"var(--foreground)","--normal-text":"var(--background)","--normal-border":"color-mix(in oklch, var(--background) 25%, transparent)","--border-radius":"9999px","--success-bg":"var(--success)","--success-text":"var(--success-foreground)","--success-border":"var(--success-400)","--error-bg":"var(--destructive)","--error-text":"var(--destructive-foreground)","--error-border":"var(--danger-400)","--warning-bg":"var(--warning)","--warning-text":"var(--warning-foreground)","--warning-border":"var(--warning-400)","--info-bg":"var(--info)","--info-text":"var(--info-foreground)","--info-border":"var(--info-400)"},toastOptions:{classNames:{toast:"cn-toast",success:"toast-success",error:"toast-error",warning:"toast-warning",info:"toast-info",loading:"toast-loading"}},...t})};p.displayName="Toaster";const H=p,_=p;exports.EnhancedSonner=_;exports.EnhancedToaster=H;exports.Toaster=p;exports.z=k;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sonner-nE9hIalJ.cjs");exports.CoreToaster=e.Toaster;exports.EnhancedSonner=e.EnhancedSonner;exports.EnhancedToaster=e.EnhancedToaster;exports.Toaster=e.Toaster;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sonner-CP8np0BP.cjs");exports.CoreToaster=e.Toaster;exports.EnhancedSonner=e.EnhancedSonner;exports.EnhancedToaster=e.EnhancedToaster;exports.Toaster=e.Toaster;
package/dist/sonner.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import { T as o, E as r, a as s, T as n } from "./sonner-CpmECDBk.js";
1
+ import { T as o, E as r, a as s, T as n } from "./sonner-C1ndgVQY.js";
2
2
  export {
3
3
  o as CoreToaster,
4
4
  r as EnhancedSonner,
package/dist/styles.css CHANGED
@@ -37,13 +37,13 @@
37
37
  @source inline("border border-primary border-secondary border-destructive border-success border-warning border-info border-border border-input border-ring rounded-md rounded-lg rounded-full");
38
38
 
39
39
  /* Background color utilities */
40
- @source inline("bg-primary bg-secondary bg-destructive bg-success bg-warning bg-info bg-background bg-accent bg-transparent bg-none bg-muted bg-input bg-input/20 dark:bg-input/30 mid:bg-input/30");
40
+ @source inline("bg-primary bg-secondary bg-destructive bg-success bg-warning bg-info bg-background bg-accent bg-transparent bg-none bg-muted bg-input bg-input/20 dark:bg-input/30 light:bg-input/30");
41
41
 
42
42
  /* Text color utilities */
43
43
  @source inline("text-primary-foreground text-secondary-foreground text-destructive-foreground text-success-foreground text-warning-foreground text-info-foreground text-accent-foreground text-muted-foreground text-foreground");
44
44
 
45
45
  /* Hover state utilities */
46
- @source inline("hover:opacity-100 hover:bg-primary/90 hover:bg-secondary/90 hover:bg-destructive/90 hover:bg-success/90 hover:bg-warning/90 hover:bg-info/90 hover:bg-accent hover:bg-muted hover:border-primary/90 hover:border-secondary/90 hover:border-destructive/90 hover:border-success/90 hover:border-warning/90 hover:border-info/90 hover:border-border hover:text-accent-foreground hover:text-muted-foreground hover:underline dark:hover:bg-muted/50 mid:hover:bg-muted/50");
46
+ @source inline("hover:opacity-100 hover:bg-primary/90 hover:bg-secondary/90 hover:bg-destructive/90 hover:bg-success/90 hover:bg-warning/90 hover:bg-info/90 hover:bg-accent hover:bg-muted hover:border-primary/90 hover:border-secondary/90 hover:border-destructive/90 hover:border-success/90 hover:border-warning/90 hover:border-info/90 hover:border-border hover:text-accent-foreground hover:text-muted-foreground hover:underline dark:hover:bg-muted/50 light:hover:bg-muted/50");
47
47
 
48
48
  /* Focus state utilities */
49
49
  @source inline("focus:bg-primary/80 focus:bg-secondary/80 focus:bg-destructive/80 focus:bg-success/80 focus:bg-warning/80 focus:bg-info/80 focus:border-primary/80 focus:border-secondary/80 focus:border-destructive/80 focus:border-success/80 focus:border-warning/80 focus:border-info/80 focus:border-border focus:outline-0 focus-visible:outline-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]");
@@ -64,7 +64,7 @@
64
64
  @source inline("[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&>svg]:size-2.5");
65
65
 
66
66
  /* Group & State utilities */
67
- @source inline("group/badge has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 mid:aria-invalid:ring-destructive/40 aria-invalid:border-destructive");
67
+ @source inline("group/badge has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 light:aria-invalid:ring-destructive/40 aria-invalid:border-destructive");
68
68
 
69
69
  /* Z-index elevation utilities */
70
70
  @source inline("z-[var(--z-debug)] z-[var(--z-sticky-page)] z-[var(--z-background)] z-[var(--z-content)] z-[var(--z-popover)] z-[var(--z-modal-backdrop)] z-[var(--z-modal)] z-[var(--z-tooltip)] z-[var(--z-sticky-content)] z-[var(--z-floating)]");
@@ -75,7 +75,7 @@
75
75
 
76
76
 
77
77
  /* Custom dark mode variant - matches when .dark class is on element or ancestor */
78
- /* Mid theme (warm paper) matches when .mid class is on element or ancestor; palette from mind-canvas */
78
+ /* Non-dark themes — .light (warm paper) and .mid (dimmer neutral); both use light: utilities */
79
79
  /* Hit-area — https://bazza.dev/craft/2026/hit-area */
80
80
  /* Bazza hit-area utilities — https://bazza.dev/craft/2026/hit-area */
81
81
 
@@ -784,10 +784,14 @@
784
784
  nqui Additional System Variables
785
785
  ============================================ */
786
786
 
787
- /* ============================================
787
+ /*
788
+ * Non-dark surfaces: /* ============================================
788
789
  nqui Color System & Design Tokens
789
790
  ============================================ */
790
791
 
792
+ :root + html.light = warm paper; html.mid = separate token block (showcase / comparison).
793
+ * Dark = .dark. Showcase app uses next-themes classes light | mid | dark.
794
+ */
791
795
  :root {
792
796
  /* ============================================
793
797
  Z-Index Elevation Scale
@@ -948,21 +952,16 @@
948
952
  --z-debug: 9999;
949
953
 
950
954
  /* Color system variables from colors.css */
951
- /* ============================================
952
- Primary Color Scale - Light Mode
953
- Blue Theme (Hue 240)
954
- ============================================ */
955
-
956
- /* Primary Color Scale (Light Mode) - Blue Theme (Hue 240) */
957
- --primary-100: oklch(0.95 0.08 240);
958
- --primary-200: oklch(0.90 0.10 240);
959
- --primary-300: oklch(0.85 0.12 240);
960
- --primary-400: oklch(0.80 0.14 240);
961
- --primary-500: oklch(0.60 0.25 240); /* Main primary - darker for light mode */
962
- --primary-600: oklch(0.50 0.28 240);
955
+ /* Primary scale — midpoint between classic light and dark (blue hue 240) */
956
+ --primary-100: oklch(0.675 0.115 240);
957
+ --primary-200: oklch(0.675 0.14 240);
958
+ --primary-300: oklch(0.675 0.16 240);
959
+ --primary-400: oklch(0.675 0.18 240);
960
+ --primary-500: oklch(0.625 0.225 240);
961
+ --primary-600: oklch(0.6 0.25 240);
963
962
 
964
963
  /* ============================================
965
- Semantic Color Scales - Light Mode
964
+ Semantic Color Scales classic light (success / warning / danger / info)
966
965
  ============================================ */
967
966
 
968
967
  /* Success Color Scale (Light Mode) - Green (Hue 142) */
@@ -998,16 +997,14 @@
998
997
  --info-600: oklch(0.50 0.18 200);
999
998
 
1000
999
  /* ============================================
1001
- System Color Mappings - Light Mode
1000
+ System Color Mappings system default uses primary scale + light semantic scales
1002
1001
  ============================================ */
1003
1002
 
1004
- /* Primary Colors - Mapped to color scales */
1005
1003
  --primary: var(--primary-500);
1006
- --primary-foreground: oklch(0.98 0 0); /* Light text for contrast */
1004
+ --primary-foreground: oklch(0.98 0 0);
1007
1005
  --primary-hover: var(--primary-400);
1008
- --ring: var(--primary-500); /* Focus ring uses primary color */
1006
+ --ring: var(--primary-500);
1009
1007
 
1010
- /* Semantic Colors - Mapped to color scales */
1011
1008
  --success: var(--success-500);
1012
1009
  --success-foreground: oklch(0.98 0 0);
1013
1010
 
@@ -1021,21 +1018,21 @@
1021
1018
  --info-foreground: oklch(0.98 0 0);
1022
1019
 
1023
1020
  /* Additional variables from index.css */
1024
- /* System UI colors (unique to index.css) */
1025
- --background: oklch(1 0 0);
1026
- --foreground: oklch(0.145 0 0);
1027
- --card: oklch(0.98 0 0);
1028
- --card-foreground: oklch(0.145 0 0);
1029
- --popover: oklch(1 0 0);
1030
- --popover-foreground: oklch(0.145 0 0);
1031
- --secondary: oklch(0.967 0.001 286.375);
1032
- --secondary-foreground: oklch(0.21 0.006 285.885);
1033
- --muted: oklch(0.95 0 0);
1034
- --muted-foreground: oklch(0.556 0 0);
1035
- --accent: oklch(0.96 0 0);
1036
- --accent-foreground: oklch(0.205 0 0);
1037
- --border: oklch(0.922 0 0);
1038
- --input: oklch(0.922 0 0);
1021
+ /* Light / system UI warm paper */
1022
+ --background: oklch(0.985 0.002 78);
1023
+ --foreground: oklch(0.2416 0.0219 57);
1024
+ --card: oklch(0.9792 0.0042 78.3);
1025
+ --card-foreground: oklch(0.2416 0.0219 57);
1026
+ --popover: oklch(0.9792 0.0042 78.3);
1027
+ --popover-foreground: oklch(0.2416 0.0219 57);
1028
+ --secondary: oklch(0.927 0.009 73.15);
1029
+ --secondary-foreground: oklch(0.3067 0.0309 56.81);
1030
+ --muted: oklch(0.9111 0.0082 73.15);
1031
+ --muted-foreground: oklch(0.5576 0.0222 57.81);
1032
+ --accent: oklch(0.935 0.011 73.15);
1033
+ --accent-foreground: oklch(0.2416 0.0219 57);
1034
+ --border: oklch(0.89 0.0137 73.1);
1035
+ --input: oklch(0.89 0.0137 73.1);
1039
1036
 
1040
1037
  /* Chart colors */
1041
1038
  --chart-1: oklch(0.809 0.105 251.813);
@@ -1048,14 +1045,49 @@
1048
1045
  --radius: 0.45rem;
1049
1046
 
1050
1047
  /* Sidebar */
1051
- --sidebar: oklch(0.98 0 0);
1052
- --sidebar-foreground: oklch(0.145 0 0);
1048
+ --sidebar: oklch(0.9792 0.0042 78.3);
1049
+ --sidebar-foreground: oklch(0.2416 0.0219 57);
1053
1050
  --sidebar-primary: var(--primary);
1054
1051
  --sidebar-primary-foreground: var(--primary-foreground);
1055
- --sidebar-accent: oklch(0.96 0 0);
1056
- --sidebar-accent-foreground: oklch(0.205 0 0);
1057
- --sidebar-border: oklch(0.922 0 0);
1058
- --sidebar-ring: oklch(0.708 0 0);
1052
+ --sidebar-accent: oklch(0.96 0.004 75);
1053
+ --sidebar-accent-foreground: oklch(0.205 0.015 55);
1054
+ --sidebar-border: oklch(0.89 0.0137 73.1);
1055
+ --sidebar-ring: var(--ring);
1056
+ }
1057
+
1058
+ /* Mid — between warm paper and dark (neutral, slightly dimmer than light) */
1059
+ .mid {
1060
+ --background: oklch(0.915 0.01 78);
1061
+ --foreground: oklch(0.28 0.022 57);
1062
+ --card: oklch(0.895 0.012 78);
1063
+ --card-foreground: oklch(0.28 0.022 57);
1064
+ --popover: oklch(0.895 0.012 78);
1065
+ --popover-foreground: oklch(0.28 0.022 57);
1066
+ --secondary: oklch(0.88 0.012 73);
1067
+ --secondary-foreground: oklch(0.32 0.028 57);
1068
+ --muted: oklch(0.855 0.012 73);
1069
+ --muted-foreground: oklch(0.48 0.024 58);
1070
+ --accent: oklch(0.87 0.014 73);
1071
+ --accent-foreground: oklch(0.28 0.022 57);
1072
+ --border: oklch(0.82 0.016 73);
1073
+ --input: oklch(0.82 0.016 73);
1074
+
1075
+ --chart-1: oklch(0.809 0.105 251.813);
1076
+ --chart-2: oklch(0.623 0.214 259.815);
1077
+ --chart-3: oklch(0.546 0.245 262.881);
1078
+ --chart-4: oklch(0.488 0.243 264.376);
1079
+ --chart-5: oklch(0.424 0.199 265.638);
1080
+
1081
+ --radius: 0.45rem;
1082
+
1083
+ --sidebar: oklch(0.9 0.01 78);
1084
+ --sidebar-foreground: oklch(0.28 0.022 57);
1085
+ --sidebar-primary: var(--primary);
1086
+ --sidebar-primary-foreground: var(--primary-foreground);
1087
+ --sidebar-accent: oklch(0.875 0.012 75);
1088
+ --sidebar-accent-foreground: oklch(0.26 0.018 55);
1089
+ --sidebar-border: oklch(0.82 0.016 73);
1090
+ --sidebar-ring: var(--ring);
1059
1091
  }
1060
1092
 
1061
1093
  .dark {
@@ -1168,43 +1200,6 @@
1168
1200
  }
1169
1201
 
1170
1202
  /*
1171
- * Mid theme light-mode layout (same token roles as :root), with warm paper chroma
1172
- * (mind-canvas–inspired oklch). Use class="mid" on <html> or an ancestor; do not
1173
- * combine with .dark on the same root. Destructive/success/warning/info come from
1174
- * colors.css like default light.
1175
- */
1176
- .mid {
1177
- /* Surfaces: parallel to :root — slightly lower L and mild hue vs neutral light */
1178
- --background: oklch(0.9574 0.0081 73.16);
1179
- --foreground: oklch(0.2416 0.0219 57);
1180
- --card: oklch(0.9792 0.0042 78.3);
1181
- --card-foreground: oklch(0.2416 0.0219 57);
1182
- --popover: oklch(0.9792 0.0042 78.3);
1183
- --popover-foreground: oklch(0.2416 0.0219 57);
1184
- --secondary: oklch(0.927 0.009 73.15);
1185
- --secondary-foreground: oklch(0.3067 0.0309 56.81);
1186
- --muted: oklch(0.9111 0.0082 73.15);
1187
- --muted-foreground: oklch(0.5576 0.0222 57.81);
1188
- /* Hover/focus wash (menus, ghost buttons) — high L like :root accent, not a filled control */
1189
- --accent: oklch(0.935 0.011 73.15);
1190
- --accent-foreground: oklch(0.2416 0.0219 57);
1191
- --border: oklch(0.89 0.0137 73.1);
1192
- --input: oklch(0.89 0.0137 73.1);
1193
-
1194
- /* Charts: same as light (:root) */
1195
- --chart-1: oklch(0.809 0.105 251.813);
1196
- --chart-2: oklch(0.623 0.214 259.815);
1197
- --chart-3: oklch(0.546 0.245 262.881);
1198
- --chart-4: oklch(0.488 0.243 264.376);
1199
- --chart-5: oklch(0.424 0.199 265.638);
1200
-
1201
- /* Sidebar: same structure as :root (light) — primary via semantic tokens, ring follows focus */
1202
- --sidebar: oklch(0.985 0.002 78);
1203
- --sidebar-foreground: oklch(0.2416 0.0219 57);
1204
- --sidebar-primary: var(--primary);
1205
- --sidebar-primary-foreground: var(--primary-foreground);
1206
- --sidebar-accent: oklch(0.96 0.004 75);
1207
- --sidebar-accent-foreground: oklch(0.205 0.015 55);
1208
- --sidebar-border: oklch(0.89 0.0137 73.1);
1209
- --sidebar-ring: var(--ring);
1210
- }
1203
+ * html.lightnext-themes; surface tokens match :root (warm paper).
1204
+ * html.mid — next-themes; surfaces use .mid block above (distinct from light).
1205
+ */
@@ -12,6 +12,15 @@ Implementation guides for each component. **AI Skill:** Optimized for AI/LLM con
12
12
 
13
13
  ---
14
14
 
15
+ ## Start here (humans)
16
+
17
+ 1. **Know your task?** Open **`docs/nqui-skills/HUMAN_GUIDE.md`** (in the package) or **`node_modules/@nqlib/nqui/docs/nqui-skills/HUMAN_GUIDE.md`** after install — maps *forms, toolbar, dashboard, empty/loading, …* → which doc to open first.
18
+ 2. **Pick one component** → **`nqui-<kebab-case>.md`** in this folder (e.g. `nqui-combobox.md`). Don’t load every file.
19
+ 3. **Need “which component?” tables** → scroll to **When to Use (Selection & Action Components)** and **Shared Conventions** below; skip **Prerequisites** until you wire build/CSS.
20
+ 4. **Deep layout / scroll / sticky** → **Layout & Scroll Patterns** (implementation-heavy; skip if you’re only choosing a component).
21
+
22
+ ---
23
+
15
24
  ## Prerequisites
16
25
 
17
26
  | Dependency | Version | Notes |
@@ -28,7 +37,7 @@ Implementation guides for each component. **AI Skill:** Optimized for AI/LLM con
28
37
 
29
38
  ## Shared Conventions (AI Implementation Rules)
30
39
 
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`.
40
+ - **Control sizes:** `sm`=h-6, `default`=h-7, `lg`=h-8. Button, Toggle, ToggleGroup, Input, Select, Switch, Slider use this scale. See `docs/nqui-skills/nqui-design-system/SKILL.md` (package) or `.cursor/skills/nqui-design-system/SKILL.md` (monorepo copy).
32
41
  - **CSS vars required:** nqui uses `--primary`, `--background`, `--foreground`, etc. Run `npx @nqlib/nqui init-css`.
33
42
  - **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
43
  - **Grouped controls:** ButtonGroup, ToggleGroup share border; outer container uses **pill** radius (`rounded-full`). ToggleGroup uses item dividers (`border-foreground/20`) or `ToggleGroupSeparator`.
@@ -38,7 +47,7 @@ Implementation guides for each component. **AI Skill:** Optimized for AI/LLM con
38
47
  - **SidebarProvider:** Must wrap entire layout (sidebar + content).
39
48
  - **Z-index:** Use CSS vars from `styles/elevation.css` (e.g. `z-[var(--z-modal)]`). Never hardcode `z-10`, `z-50`, etc.
40
49
  - **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.
50
+ - **Bounded content:** Chips, pills, and inline controls should stay inside their box (ellipsis / line-clamp / scroll-by-design). See **Bounded content** in `docs/nqui-skills/nqui-design-system/SKILL.md`. Portals and tooltips are explicit exceptions.
42
51
 
43
52
  ---
44
53
 
@@ -30,3 +30,77 @@ import { ScrollArea, ScrollBar } from "@nqlib/nqui"
30
30
  ```tsx
31
31
  <ScrollArea fadeMask>...</ScrollArea>
32
32
  ```
33
+
34
+ ## `viewportRef`
35
+
36
+ Pass **`viewportRef`** when you need a ref to the scrollable viewport (programmatic `scrollTop`, `ResizeObserver`, etc.).
37
+
38
+ ```tsx
39
+ const viewportRef = useRef<HTMLDivElement>(null);
40
+
41
+ <ScrollArea viewportRef={viewportRef} className="min-h-0 flex-1">
42
+
43
+ </ScrollArea>
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Layout pitfalls (read before shipping)
49
+
50
+ Radix Scroll Area wraps content in a **viewport** plus an **inner wrapper** whose default layout can interact badly with **flex**, **wide content**, and **`pre` / monospace**. Follow the checks below to avoid: **no vertical scroll**, **content growing off-screen to the right**, or **nested scroll bugs**.
51
+
52
+ ### 1. Flex / resizable panels — height chain
53
+
54
+ In a **flex column**, children default to `min-height: auto`, so a `flex-1` scroll region can **expand with content** and **never scroll**.
55
+
56
+ - Put **`min-h-0`** (and usually **`min-w-0`**) on **every** flex ancestor between the root and `ScrollArea`.
57
+ - On **`ScrollArea` root**, use something like **`min-h-0 flex-1`** (and **`h-full`** only when the parent’s height is well-defined).
58
+
59
+ ```tsx
60
+ <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
61
+ <ScrollArea className="min-h-0 flex-1" viewportRef={viewportRef}>
62
+
63
+ </ScrollArea>
64
+ </div>
65
+ ```
66
+
67
+ ### 2. Prose, chat, markdown — width and wrapping
68
+
69
+ The viewport’s **inner wrapper** (Radix) often behaves like a **shrink-to-fit** width. Long **URLs**, **unbroken strings**, or **table layout** can make the inner column **wider than the panel**, so users lose content off the **right** even though vertical scroll “works”.
70
+
71
+ **Do:**
72
+
73
+ - On **`ScrollArea` root** (or the viewport via arbitrary selectors), ensure:
74
+ - **`overflow-x: hidden`** on the **viewport** so the column does not pan sideways at the transcript level.
75
+ - On the **inner content wrapper** (Radix’s direct child of the viewport): **`display: block`**, **`min-width: 0`**, **`max-width: 100%`**, **`width: 100%`**, and **`break-words`** / **`overflow-wrap: anywhere`** so body text **wraps downward** instead of widening the scroll area.
76
+
77
+ **Avoid:**
78
+
79
+ - Blindly forcing **`display: block`** on the inner wrapper **without** `min-w-0` / `max-w-full` / wrapping — that can **break vertical scroll measurement** in some trees. Prefer a **documented, tested** combination (height + width) for your shell (e.g. chat transcript vs. simple list).
80
+
81
+ **Consumer pattern (example):** apps may ship a shared class for “chat transcript” `ScrollArea` that sets viewport `overflow-x-hidden` and targets `[data-radix-scroll-area-viewport] > div` with block + `min-w-0` + `max-w-full` + `break-words`. Reuse one class per product so behavior stays consistent.
82
+
83
+ ### 3. `<pre>`, fenced code, `white-space: pre`
84
+
85
+ **Do not** rely on `ScrollArea` as the only clip for **fenced code** or **`white-space: pre`** blocks. Radix’s structure often **fails to clip** tall/wide `pre` content predictably.
86
+
87
+ **Prefer:** a plain element with **`max-height`**, **`overflow-auto`**, and optional thin scrollbar styling (e.g. a utility class in app CSS). Keep **one** main column `ScrollArea` for the page/panel; use **native overflow** inside message bodies for code.
88
+
89
+ ### 4. Nested `ScrollArea`
90
+
91
+ Avoid **nested** `ScrollArea` for small regions (reasoning previews, inline panels). Prefer **one** outer scroll for the column + **native `overflow-auto`** for inner blocks. Nested Radix scroll roots multiply width/height bugs.
92
+
93
+ ### 5. Sidebar lists vs. chat transcript
94
+
95
+ A pattern that fixes **wide titles** in a list (e.g. forcing inner `display: block`) may **break** vertical scrolling in a **chat transcript**. Do not reuse one global class for both without verifying **both** behaviors; split constants (e.g. “sidebar list” vs. “chat transcript”) when needed.
96
+
97
+ ---
98
+
99
+ ## Checklist (copy for PRs)
100
+
101
+ - [ ] Flex ancestors use **`min-h-0`** where the scroll area must shrink.
102
+ - [ ] `ScrollArea` root uses **`min-h-0`** (+ **`flex-1`** / **`h-full`** as appropriate).
103
+ - [ ] Long **text** wraps: **`min-w-0`**, **`max-w-full`**, **`break-words`** / **`overflow-wrap: anywhere`**, viewport **`overflow-x-hidden`** if prose lives inside.
104
+ - [ ] **Code / `pre`**: **`max-height` + native `overflow-auto`**, not nested `ScrollArea`.
105
+ - [ ] **No unnecessary nested** `ScrollArea` for small inner regions.
106
+ - [ ] **`ScrollBar`** included where a visible scrollbar is desired (see Basic example).
@@ -1,6 +1,6 @@
1
1
  # nqui Tooltip
2
2
 
3
- > Hover tooltip. Wrap app in TooltipProvider.
3
+ > Supplemental hint on **hover or focus** — not a substitute for visible labels or critical instructions.
4
4
 
5
5
  ## Import
6
6
 
@@ -21,4 +21,19 @@ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "@nqlib
21
21
 
22
22
  ## Provider
23
23
 
24
- Place once at app root (or layout).
24
+ Place **`TooltipProvider`** once at app root (or layout) so delays and portals work consistently.
25
+
26
+ ## UX & accessibility
27
+
28
+ - **Do not** put **required** information only in a tooltip. Users who don’t hover (keyboard, touch, screen reader) may miss it. Repeat critical copy in the visible UI or **Dialog** / **Sheet** / inline help.
29
+ - **Keyboard:** Triggers should be focusable; tooltips typically show on **focus** as well as hover (Radix Tooltip behavior). Don’t rely on hover-only for anything mandatory.
30
+ - **Touch:** There is **no hover** on phones. Treat tooltip content as **nice-to-have**; use **Popover** or inline text for essential mobile copy.
31
+ - **Screen readers:** Tooltip content is exposed when the trigger is focused; keep text **short** and **non-semantic** for critical structure (don’t use tooltips as the only form label).
32
+
33
+ ## When to use something else
34
+
35
+ | Need | Use instead |
36
+ |------|-------------|
37
+ | Rich content, tap to open | **Popover** (`nqui-popover.md`) |
38
+ | Long preview on hover | **HoverCard** (`nqui-hover-card.md`) |
39
+ | Blocking message | **Alert** or **Dialog** |
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: nqui-component-docs-index
3
+ description: Token-efficient map for nqui per-component docs. Use BEFORE opening README or multiple nqui-*.md files. Tells which single file to load for a component.
4
+ ---
5
+
6
+ # Component docs — load order (save tokens)
7
+
8
+ **Do not** bulk-read every file under `components/`. **Do not** load all of `README.md` unless you need full “when to use” tables.
9
+
10
+ 1. **Pick one doc:** `nqui-<kebab-case>.md` for the component you are implementing (e.g. `nqui-toggle-group.md`). In this repo use **`docs/components/`**; after `init-skills` use **`.cursor/nqui-skills/components/`**.
11
+ 2. **Unsure which component?** Skim **`README.md`** in that folder only for **“When to Use”** and the category table — or use **[HUMAN_GUIDE.md](./HUMAN_GUIDE.md)** (task → docs) or the **area → filenames** list below.
12
+ 3. **Deep selection logic** (toolbar vs form, ToggleGroup vs RadioGroup): **`nqui-components/SKILL.md`** and **`nqui-design-system/SKILL.md`**.
13
+
14
+ ## Paths
15
+
16
+ | Where | Per-component docs | Routing / human wayfinding |
17
+ |-------|--------------------|----------------------------|
18
+ | nqui repo / package source | `docs/components/nqui-<kebab>.md` | `docs/nqui-skills/COMPONENTS_INDEX.md` · `HUMAN_GUIDE.md` |
19
+ | Consumer after `init-skills` | `.cursor/nqui-skills/components/nqui-<kebab>.md` | `.cursor/nqui-skills/COMPONENTS_INDEX.md` · `HUMAN_GUIDE.md` |
20
+ | Installed only (no init-skills) | `node_modules/@nqlib/nqui/docs/components/nqui-<kebab>.md` | `node_modules/@nqlib/nqui/docs/nqui-skills/COMPONENTS_INDEX.md` · `HUMAN_GUIDE.md` |
21
+
22
+ Filenames always: `nqui-<kebab>.md`.
23
+
24
+ ## Area → doc files (open **one**)
25
+
26
+ Suffix every name with `.md` in the components folder you are using (`docs/components/` or `.cursor/nqui-skills/components/`).
27
+
28
+ **Actions & selection:** `nqui-button` · `nqui-button-group` · `nqui-toggle` · `nqui-toggle-group`
29
+
30
+ **Forms & inputs:** `nqui-input` · `nqui-textarea` · `nqui-select` · `nqui-native-select` · `nqui-checkbox` · `nqui-radio-group` · `nqui-switch` · `nqui-slider` · `nqui-rating` · `nqui-input-otp` · `nqui-field` · `nqui-combobox` · `nqui-color-picker` · `nqui-color-slider` · `nqui-label` · `nqui-input-group`
31
+
32
+ **Display:** `nqui-badge` · `nqui-avatar` · `nqui-alert` · `nqui-empty` · `nqui-skeleton` · `nqui-spinner` · `nqui-progress` · `nqui-separator` · `nqui-card` · `nqui-item` · `nqui-kbd` · `nqui-tracker` · `nqui-frosted-glass` · `nqui-logo`
33
+
34
+ **Data:** `nqui-table` · `nqui-data-table`
35
+
36
+ **Code / embed:** `nqui-code-block` · `nqui-snippet` · `nqui-code-editor` · `nqui-sandbox`
37
+
38
+ **Navigation:** `nqui-breadcrumb` · `nqui-tabs` · `nqui-dropdown-menu` · `nqui-context-menu` · `nqui-menubar` · `nqui-navigation-menu` · `nqui-pagination` · `nqui-command` · `nqui-command-palette` · `nqui-table-of-contents`
39
+
40
+ **Overlays:** `nqui-dialog` · `nqui-alert-dialog` · `nqui-drawer` · `nqui-sheet` · `nqui-popover` · `nqui-hover-card` · `nqui-tooltip` · `nqui-toaster`
41
+
42
+ **Layout:** `nqui-accordion` · `nqui-collapsible` · `nqui-scroll-area` · `nqui-aspect-ratio` · `nqui-carousel` · `nqui-resizable` · `nqui-sidebar` · `nqui-sortable`
43
+
44
+ **Advanced:** `nqui-calendar`
45
+
46
+ ---
47
+
48
+ Full index + long-form guidance: `README.md` next to those files (large — use sections, not whole-file dump).
@@ -0,0 +1,21 @@
1
+ # nqui — task → docs (for designers & humans)
2
+
3
+ Use this file for **wayfinding** before diving into `README.md` (long) or dozens of `nqui-*.md` files. Paths below are from the **nqui package**; after `npx @nqlib/nqui init-skills`, the same files live under **`.cursor/nqui-skills/components/`**.
4
+
5
+ | I’m building… | Start here | Then open |
6
+ |---------------|------------|-----------|
7
+ | **Forms** (login, settings, checkout) | `docs/components/README.md` → Forms sections | One of `nqui-field.md`, `nqui-input.md`, `nqui-select.md`, `nqui-combobox.md`, … |
8
+ | **Toolbar / editor** (bold, view mode, align) | `nqui-skills/nqui-components/SKILL.md` (ToggleGroup rules) | `nqui-toggle-group.md`, `nqui-button-group.md` |
9
+ | **“Choose one” in a form** (stacked options, survey) | **RadioGroup** may be correct — see `nqui-components/SKILL.md` vs ToggleGroup | `nqui-radio-group.md` |
10
+ | **Dashboard / cards / lists** | `README.md` → Display | `nqui-card.md`, `nqui-item.md`, `nqui-table.md` or `nqui-data-table.md` |
11
+ | **Empty / loading / error** | Skeleton + Empty + Alert | `nqui-skeleton.md`, `nqui-empty.md`, `nqui-alert.md` |
12
+ | **Confirm destructive action** | Modal pattern | `nqui-alert-dialog.md` |
13
+ | **Navigation** | `README.md` → Navigation | `nqui-tabs.md`, `nqui-breadcrumb.md`, `nqui-sidebar.md`, … |
14
+ | **Touch-heavy or mobile** | Prefer **default/lg** controls; avoid **only** hover for critical info | `nqui-tooltip.md` (hover limits), `nqui-components/SKILL.md` (hit area) |
15
+
16
+ **Cross-cutting UX**
17
+
18
+ - **Accessibility:** Prefer components’ built-in Radix semantics; **don’t** put required instructions only in **Tooltip** (see `nqui-tooltip.md`). See `nqui-shadcn/SKILL.md` + rules for forms and dialogs.
19
+ - **States:** **Loading** → Skeleton / Spinner; **empty** → Empty; **error** → Field + Alert + validation patterns in `nqui-shadcn/rules/forms.md`.
20
+
21
+ **Agents:** For token-efficient routing, use **`COMPONENTS_INDEX.md`** first, then **one** `nqui-<name>.md`.