@qeetrix/react 0.0.1 → 0.0.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 (263) hide show
  1. package/CHANGELOG.md +288 -0
  2. package/README.md +240 -4
  3. package/dist/accordion/accordion.d.ts +42 -0
  4. package/dist/accordion/accordion.js +72 -0
  5. package/dist/accordion/index.d.ts +2 -0
  6. package/dist/accordion/index.js +1 -0
  7. package/dist/avatar/avatar.d.ts +21 -0
  8. package/dist/avatar/avatar.js +43 -0
  9. package/dist/avatar/index.d.ts +2 -0
  10. package/dist/avatar/index.js +1 -0
  11. package/dist/badge/badge.d.ts +14 -0
  12. package/dist/badge/badge.js +25 -0
  13. package/dist/badge/index.d.ts +2 -0
  14. package/dist/badge/index.js +1 -0
  15. package/dist/box/box.d.ts +27 -0
  16. package/dist/box/box.js +21 -0
  17. package/dist/box/index.d.ts +2 -0
  18. package/dist/box/index.js +1 -0
  19. package/dist/button/button.d.ts +50 -0
  20. package/dist/button/button.js +78 -0
  21. package/dist/button/index.d.ts +2 -0
  22. package/dist/button/index.js +1 -0
  23. package/dist/card/card.d.ts +30 -0
  24. package/dist/card/card.js +56 -0
  25. package/dist/card/index.d.ts +2 -0
  26. package/dist/card/index.js +1 -0
  27. package/dist/checkbox/checkbox.d.ts +33 -0
  28. package/dist/checkbox/checkbox.js +61 -0
  29. package/dist/checkbox/index.d.ts +2 -0
  30. package/dist/checkbox/index.js +1 -0
  31. package/dist/css/accordion.css +73 -0
  32. package/dist/css/avatar.css +50 -0
  33. package/dist/css/badge.css +59 -0
  34. package/dist/css/box.css +163 -0
  35. package/dist/css/button.css +243 -0
  36. package/dist/css/card.css +75 -0
  37. package/dist/css/checkbox.css +67 -0
  38. package/dist/css/dialog.css +128 -0
  39. package/dist/css/drawer.css +129 -0
  40. package/dist/css/experimental/alert.css +89 -0
  41. package/dist/css/experimental/app-shell.css +258 -0
  42. package/dist/css/experimental/breadcrumb.css +67 -0
  43. package/dist/css/experimental/copy-button.css +13 -0
  44. package/dist/css/experimental/empty-state.css +63 -0
  45. package/dist/css/experimental/otp-input.css +84 -0
  46. package/dist/css/experimental/page-header.css +110 -0
  47. package/dist/css/experimental/password-input.css +21 -0
  48. package/dist/css/experimental/sidebar.css +234 -0
  49. package/dist/css/experimental/stat.css +115 -0
  50. package/dist/css/experimental/top-bar.css +299 -0
  51. package/dist/css/field.css +121 -0
  52. package/dist/css/fieldset.css +37 -0
  53. package/dist/css/grid.css +86 -0
  54. package/dist/css/heading.css +93 -0
  55. package/dist/css/inline.css +85 -0
  56. package/dist/css/input.css +108 -0
  57. package/dist/css/link.css +45 -0
  58. package/dist/css/menu.css +166 -0
  59. package/dist/css/popover.css +72 -0
  60. package/dist/css/progress.css +102 -0
  61. package/dist/css/radio.css +54 -0
  62. package/dist/css/scroll-area.css +70 -0
  63. package/dist/css/select.css +189 -0
  64. package/dist/css/separator.css +35 -0
  65. package/dist/css/skeleton.css +39 -0
  66. package/dist/css/skip-link.css +33 -0
  67. package/dist/css/slider.css +108 -0
  68. package/dist/css/spinner.css +55 -0
  69. package/dist/css/stack.css +76 -0
  70. package/dist/css/switch.css +112 -0
  71. package/dist/css/table.css +68 -0
  72. package/dist/css/tabs.css +123 -0
  73. package/dist/css/text-field.css +4 -0
  74. package/dist/css/text.css +106 -0
  75. package/dist/css/toast.css +115 -0
  76. package/dist/css/toggle.css +125 -0
  77. package/dist/css/tooltip.css +47 -0
  78. package/dist/css/visually-hidden.css +15 -0
  79. package/dist/dialog/dialog.d.ts +54 -0
  80. package/dist/dialog/dialog.js +121 -0
  81. package/dist/dialog/index.d.ts +2 -0
  82. package/dist/dialog/index.js +1 -0
  83. package/dist/drawer/drawer.d.ts +49 -0
  84. package/dist/drawer/drawer.js +102 -0
  85. package/dist/drawer/index.d.ts +2 -0
  86. package/dist/drawer/index.js +1 -0
  87. package/dist/experimental/alert/alert.d.ts +29 -0
  88. package/dist/experimental/alert/alert.js +72 -0
  89. package/dist/experimental/alert/index.d.ts +2 -0
  90. package/dist/experimental/app-shell/app-shell.d.ts +60 -0
  91. package/dist/experimental/app-shell/app-shell.js +224 -0
  92. package/dist/experimental/app-shell/index.d.ts +2 -0
  93. package/dist/experimental/breadcrumb/breadcrumb.d.ts +27 -0
  94. package/dist/experimental/breadcrumb/breadcrumb.js +48 -0
  95. package/dist/experimental/breadcrumb/index.d.ts +2 -0
  96. package/dist/experimental/copy-button/copy-button.d.ts +27 -0
  97. package/dist/experimental/copy-button/copy-button.js +61 -0
  98. package/dist/experimental/copy-button/index.d.ts +2 -0
  99. package/dist/experimental/empty-state/data-state.d.ts +23 -0
  100. package/dist/experimental/empty-state/data-state.js +40 -0
  101. package/dist/experimental/empty-state/empty-state.d.ts +26 -0
  102. package/dist/experimental/empty-state/empty-state.js +46 -0
  103. package/dist/experimental/empty-state/index.d.ts +3 -0
  104. package/dist/experimental/index.d.ts +24 -0
  105. package/dist/experimental/index.js +13 -0
  106. package/dist/experimental/otp-input/index.d.ts +2 -0
  107. package/dist/experimental/otp-input/otp-input.d.ts +27 -0
  108. package/dist/experimental/otp-input/otp-input.js +39 -0
  109. package/dist/experimental/page-header/index.d.ts +2 -0
  110. package/dist/experimental/page-header/page-header.d.ts +32 -0
  111. package/dist/experimental/page-header/page-header.js +73 -0
  112. package/dist/experimental/password-input/index.d.ts +2 -0
  113. package/dist/experimental/password-input/password-input.d.ts +26 -0
  114. package/dist/experimental/password-input/password-input.js +103 -0
  115. package/dist/experimental/sidebar/index.d.ts +2 -0
  116. package/dist/experimental/sidebar/sidebar.d.ts +64 -0
  117. package/dist/experimental/sidebar/sidebar.js +200 -0
  118. package/dist/experimental/stat/index.d.ts +2 -0
  119. package/dist/experimental/stat/stat.d.ts +31 -0
  120. package/dist/experimental/stat/stat.js +90 -0
  121. package/dist/experimental/top-bar/index.d.ts +2 -0
  122. package/dist/experimental/top-bar/top-bar.d.ts +60 -0
  123. package/dist/experimental/top-bar/top-bar.js +162 -0
  124. package/dist/field/choice.d.ts +21 -0
  125. package/dist/field/choice.js +67 -0
  126. package/dist/field/field.d.ts +31 -0
  127. package/dist/field/field.js +78 -0
  128. package/dist/field/index.d.ts +3 -0
  129. package/dist/field/index.js +1 -0
  130. package/dist/fieldset/fieldset.d.ts +19 -0
  131. package/dist/fieldset/fieldset.js +29 -0
  132. package/dist/fieldset/index.d.ts +2 -0
  133. package/dist/fieldset/index.js +1 -0
  134. package/dist/format/format.d.ts +12 -0
  135. package/dist/format/format.js +50 -0
  136. package/dist/format/index.d.ts +2 -0
  137. package/dist/format/index.js +1 -0
  138. package/dist/grid/grid.d.ts +30 -0
  139. package/dist/grid/grid.js +36 -0
  140. package/dist/grid/index.d.ts +2 -0
  141. package/dist/grid/index.js +1 -0
  142. package/dist/heading/heading.d.ts +23 -0
  143. package/dist/heading/heading.js +30 -0
  144. package/dist/heading/index.d.ts +2 -0
  145. package/dist/heading/index.js +1 -0
  146. package/dist/index.d.ts +84 -1
  147. package/dist/index.js +40 -0
  148. package/dist/inline/index.d.ts +2 -0
  149. package/dist/inline/index.js +1 -0
  150. package/dist/inline/inline.d.ts +19 -0
  151. package/dist/inline/inline.js +16 -0
  152. package/dist/input/index.d.ts +2 -0
  153. package/dist/input/index.js +1 -0
  154. package/dist/input/input.d.ts +37 -0
  155. package/dist/input/input.js +63 -0
  156. package/dist/internal/control.d.ts +9 -0
  157. package/dist/internal/control.js +4 -0
  158. package/dist/internal/cx.js +7 -0
  159. package/dist/internal/media.js +15 -0
  160. package/dist/internal/messages.d.ts +75 -0
  161. package/dist/internal/messages.js +61 -0
  162. package/dist/internal/refs.js +23 -0
  163. package/dist/internal/render.d.ts +6 -0
  164. package/dist/internal/render.js +22 -0
  165. package/dist/internal/space.d.ts +9 -0
  166. package/dist/internal/text.d.ts +5 -0
  167. package/dist/internal/text.js +18 -0
  168. package/dist/link/index.d.ts +2 -0
  169. package/dist/link/index.js +1 -0
  170. package/dist/link/link.d.ts +19 -0
  171. package/dist/link/link.js +32 -0
  172. package/dist/locales/hi.d.ts +8 -0
  173. package/dist/locales/hi.js +60 -0
  174. package/dist/menu/index.d.ts +2 -0
  175. package/dist/menu/index.js +1 -0
  176. package/dist/menu/menu.d.ts +76 -0
  177. package/dist/menu/menu.js +172 -0
  178. package/dist/popover/index.d.ts +2 -0
  179. package/dist/popover/index.js +1 -0
  180. package/dist/popover/popover.d.ts +33 -0
  181. package/dist/popover/popover.js +48 -0
  182. package/dist/progress/index.d.ts +2 -0
  183. package/dist/progress/index.js +1 -0
  184. package/dist/progress/progress.d.ts +26 -0
  185. package/dist/progress/progress.js +62 -0
  186. package/dist/provider/index.d.ts +3 -0
  187. package/dist/provider/index.js +1 -0
  188. package/dist/provider/provider.d.ts +46 -0
  189. package/dist/provider/provider.js +90 -0
  190. package/dist/radio/index.d.ts +2 -0
  191. package/dist/radio/index.js +1 -0
  192. package/dist/radio/radio.d.ts +31 -0
  193. package/dist/radio/radio.js +47 -0
  194. package/dist/scroll-area/index.d.ts +2 -0
  195. package/dist/scroll-area/index.js +1 -0
  196. package/dist/scroll-area/scroll-area.d.ts +18 -0
  197. package/dist/scroll-area/scroll-area.js +39 -0
  198. package/dist/select/index.d.ts +2 -0
  199. package/dist/select/index.js +1 -0
  200. package/dist/select/select.d.ts +36 -0
  201. package/dist/select/select.js +82 -0
  202. package/dist/separator/index.d.ts +2 -0
  203. package/dist/separator/index.js +1 -0
  204. package/dist/separator/separator.d.ts +16 -0
  205. package/dist/separator/separator.js +16 -0
  206. package/dist/skeleton/index.d.ts +2 -0
  207. package/dist/skeleton/index.js +1 -0
  208. package/dist/skeleton/skeleton.d.ts +12 -0
  209. package/dist/skeleton/skeleton.js +18 -0
  210. package/dist/skip-link/index.d.ts +2 -0
  211. package/dist/skip-link/index.js +1 -0
  212. package/dist/skip-link/skip-link.d.ts +13 -0
  213. package/dist/skip-link/skip-link.js +18 -0
  214. package/dist/slider/index.d.ts +2 -0
  215. package/dist/slider/index.js +1 -0
  216. package/dist/slider/slider.d.ts +20 -0
  217. package/dist/slider/slider.js +58 -0
  218. package/dist/spinner/index.d.ts +2 -0
  219. package/dist/spinner/index.js +1 -0
  220. package/dist/spinner/spinner.d.ts +15 -0
  221. package/dist/spinner/spinner.js +19 -0
  222. package/dist/stack/index.d.ts +2 -0
  223. package/dist/stack/index.js +1 -0
  224. package/dist/stack/stack.d.ts +17 -0
  225. package/dist/stack/stack.js +15 -0
  226. package/dist/styles.css +4548 -0
  227. package/dist/switch/index.d.ts +2 -0
  228. package/dist/switch/index.js +1 -0
  229. package/dist/switch/switch.d.ts +18 -0
  230. package/dist/switch/switch.js +31 -0
  231. package/dist/table/index.d.ts +2 -0
  232. package/dist/table/index.js +1 -0
  233. package/dist/table/table.d.ts +36 -0
  234. package/dist/table/table.js +69 -0
  235. package/dist/tabs/index.d.ts +2 -0
  236. package/dist/tabs/index.js +1 -0
  237. package/dist/tabs/tabs.d.ts +43 -0
  238. package/dist/tabs/tabs.js +66 -0
  239. package/dist/text/index.d.ts +3 -0
  240. package/dist/text/index.js +1 -0
  241. package/dist/text/text.d.ts +24 -0
  242. package/dist/text/text.js +24 -0
  243. package/dist/text-field/index.d.ts +2 -0
  244. package/dist/text-field/index.js +1 -0
  245. package/dist/text-field/text-field.d.ts +15 -0
  246. package/dist/text-field/text-field.js +43 -0
  247. package/dist/toast/index.d.ts +2 -0
  248. package/dist/toast/index.js +1 -0
  249. package/dist/toast/toast.d.ts +57 -0
  250. package/dist/toast/toast.js +124 -0
  251. package/dist/toggle/index.d.ts +2 -0
  252. package/dist/toggle/index.js +1 -0
  253. package/dist/toggle/toggle.d.ts +48 -0
  254. package/dist/toggle/toggle.js +54 -0
  255. package/dist/tooltip/index.d.ts +2 -0
  256. package/dist/tooltip/index.js +1 -0
  257. package/dist/tooltip/tooltip.d.ts +26 -0
  258. package/dist/tooltip/tooltip.js +39 -0
  259. package/dist/visually-hidden/index.d.ts +2 -0
  260. package/dist/visually-hidden/index.js +1 -0
  261. package/dist/visually-hidden/visually-hidden.d.ts +8 -0
  262. package/dist/visually-hidden/visually-hidden.js +12 -0
  263. package/package.json +170 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,293 @@
1
1
  # @qeetrix/react
2
2
 
3
+ ## 0.0.3
4
+
5
+ The first two Phase 5 increments, from `@qeetrix/react/experimental`: C1, the console frame, and
6
+ C2, secure entry. Experimental parts may change in a patch release; each moves to the root export
7
+ once it has shipped in its product (07-components.md).
8
+
9
+ ### C1 · Console frame
10
+
11
+ - **`AppShell`** (design B · Ruled, chosen from three researched concepts; 07-components.md):
12
+ - the frame of every Qeet console: a skip link, the sticky TopBar across the full width, the
13
+ navigation column on the canvas tone, the page, and an end-edge panel; every seam a 1px rule;
14
+ - from 840px the navigation collapses to a 48px rail (`sidebarCollapsed` /
15
+ `defaultSidebarCollapsed` / `onSidebarCollapsedChange`) that expands over the page while hovered
16
+ or focused; below 840px it opens as a drawer from the start edge;
17
+ - `drawers` (help, assistant, activity), opened from the TopBar's tools: one at a time, pushing
18
+ the page from 1200px; `notifications` above the page; `density`, compact by default;
19
+ - `useAppShell()` tells content inside the frame when the sidebar is a rail.
20
+ - **`TopBar`, `TopBarSwitcher`, `TopBarSearch`:**
21
+ - black in light and dark (the dark scheme on itself), topped by a 2px Ember line;
22
+ - product to global, left to right: launcher cell, product, the scope path with drawn slashes,
23
+ centred search, panel triggers and utilities, account;
24
+ - `TopBarSwitcher` is a step of the path (organisation mark, environment status dot, always a
25
+ word) and works as a Menu trigger; `TopBarSearch` is a field-shaped button with its shortcut.
26
+ - **`PageHeader`, `PageContent`:** breadcrumb, `title.lg` title with count and status, actions, a
27
+ `summary` strip bled to the page edges, and tabs at the foot; a 32px page gutter.
28
+ - **`Stat`, `StatGroup`:** key numbers in ruled cells; numeric values formatted for the locale; an
29
+ optional decorative trend line whose last point is the Ember mark. Server-safe.
30
+ - **`Sidebar`, `SidebarGroup`, `SidebarItem`:**
31
+ - links only, one level of nesting, every item in the Tab order;
32
+ - square rows edge to edge with round icons; the current page gets `aria-current`, the 3px Ember
33
+ bar, weight 600 and an Ember (optionally filled) icon;
34
+ - groups between hairlines under tracked small capitals (`typography.label.caps`; untracked for
35
+ Indic text, which has no case); an optional `title`, `filter` and `footer`;
36
+ - in the rail, icons stay where they are and labels become tooltips.
37
+ - **`Breadcrumb`, `BreadcrumbItem`:** server-safe; mirrored chevrons; long labels truncate and keep a
38
+ title.
39
+ - **`EmptyState`:** a 24px icon, a title (a heading when given a `level`), a description and actions,
40
+ in `page` or `section` size and `neutral` or `danger` tone. Server-safe.
41
+ - **`DataState`:** loading, then error, then empty, then the content. The error state never shows the
42
+ raw error, and `onRetry` adds "Try again".
43
+ - **Colour scheme:**
44
+ - `QeetrixProvider` takes `colorScheme`, `defaultColorScheme` and `onColorSchemeChange`, writes
45
+ `data-qx-color-scheme` on `<html>`, and follows the operating system while `system`;
46
+ - `useColorScheme()` returns the scheme and a setter. The product persists the choice.
47
+ - **`Button` and `IconButton`:**
48
+ - a `neutral` tone for ghost utility actions in toolbars and headers, whose icons stay neutral
49
+ (design rules §16);
50
+ - a Button's icon sits in its own zone (the new `button-zone` slot), behind a 1px rule as tall as
51
+ the icon, in the label's colour (design rules §7).
52
+ - **Strings:** new `appShell`, `sidebar`, `breadcrumb` and `dataState` groups, in English and Hindi.
53
+ - **Peer dependency:** `@qeetrix/tokens` `^0.0.7`, for `typography.label.caps`. Since tokens 0.0.6
54
+ the stylesheet also loads Qeet Sans and Fira Code, so the tests now render in the real typefaces.
55
+ - **Source check:** every `var(--qx-*)` must name a token in the installed `@qeetrix/tokens` or a
56
+ property the package declares itself, so a token from a newer tokens release can't ship
57
+ unresolved.
58
+ - **Platform:**
59
+ - the CSS build, the dist check and the screenshot coverage test understand
60
+ `src/experimental/<family>/`;
61
+ - the test helper treats cancelled animations as settled.
62
+
63
+ ### C2 · Secure entry
64
+
65
+ - **`CopyButton`:** copies a value (or what a function returns, even asynchronously); the icon
66
+ turns into a check, "Copied" is announced politely, focus stays, and it resets after 2 seconds.
67
+ It fails visibly where there's no clipboard. An IconButton named by `label`, or a Button with a
68
+ visible label.
69
+ - **`Alert`:** `info`, `success`, `warning` or `danger` on the status tint with the 3px status edge;
70
+ a title (led by a hidden tone word), a description, actions and an optional Dismiss. `announce`
71
+ makes it `role="status"` or `role="alert"` only when it appears after something happens. It never
72
+ takes focus or disappears by itself.
73
+ - **`PasswordInput`:** an Input with a pressed-state Show password toggle, announced; hidden by
74
+ default and hidden again when its form submits; `current-password` / `new-password` / `off`;
75
+ spelling and capitalisation tools off; a Caps Lock warning; `endAdornment` for a CopyButton.
76
+ - **`OTPInput`:** one-time codes in slots on Base UI's OTP Field: typing moves on, paste fills
77
+ every slot, SMS autofill lands at once; numeric by default; `groups`, `md` and `lg` sizes; the
78
+ first slot takes the field's label, the rest are "Character 2 of 6".
79
+ - **Strings:** new `copyButton`, `alert`, `passwordInput` and `otpInput` groups, in English and
80
+ Hindi.
81
+
82
+ ## 0.0.2
83
+
84
+ Every primitive, in six increments: the foundation, actions, form controls, overlays, selection and
85
+ disclosure, and display and status. Then the Phase 4 close-out: localisation, forced colours,
86
+ right-to-left, reduced motion and the sign-in exit screen.
87
+
88
+ ### Foundation
89
+
90
+ All nine are server-safe and render in React Server Components.
91
+
92
+ - **Layout:**
93
+ - `Box`: a token-only surface (padding, background, border, corners);
94
+ - `Stack` and `Inline`: vertical and horizontal flows with token gaps;
95
+ - `Grid`: responsive equal columns on the window size classes.
96
+ - **Type:**
97
+ - `Text`: body roles, weights, tones, monospace, truncation;
98
+ - `Heading`: the level sets the element, the size sets the look; Geo letterforms for display.
99
+ - **`Separator`:** 1px, horizontal or vertical, optionally decorative.
100
+ - **Accessibility:**
101
+ - `VisuallyHidden`: screen-reader-only content;
102
+ - `SkipLink`: the first focusable element on a page, hidden until focused.
103
+ - **Shared types:** `Space`, `Inset`, `TextAlign`, `RenderProp`.
104
+ - **Imports:** every component also has its own entry point (`@qeetrix/react/stack`) and stylesheet
105
+ (`@qeetrix/react/css/stack.css`).
106
+
107
+ ### Actions
108
+
109
+ - **`Button`** (on Base UI Button):
110
+ - the label comes first, with an optional sharp trailing icon;
111
+ - variants `solid` (Ember primary), `soft` (warm-graphite secondary) and `ghost`; tones `brand` and
112
+ `danger`; sizes that follow the density;
113
+ - `loading` sets `aria-busy` and shows a spinner in the icon's place;
114
+ - a disabled button stays focusable, so its reason can be read.
115
+ - **`IconButton`:** square and icon-only; its `aria-label` is required by its type.
116
+ - **`Link`:** default, subtle and inherit tones. `external` opens a new tab with
117
+ `rel="noopener noreferrer"`, an arrow mark and a screen-reader note.
118
+ - **`Toggle` and `ToggleGroup`** (on Base UI):
119
+ - pressed state with `onPressedChange`, and `filledWhenPressed` swaps to the icon's filled drawing;
120
+ - groups are segmented or separate, single-choice or `multiple`, with arrow-key focus.
121
+ - **`Spinner`:** four sizes. It is announced as a status unless `decorative`, and fades instead of
122
+ spinning under reduced motion.
123
+ - **Shared types:** `ControlSize`, `QeetrixIcon`.
124
+ - **Dependencies:**
125
+ - new peer `@base-ui/react` ^1.9.0;
126
+ - new dependency `@qeetrix/icons` ^1.0.12, for the external-link mark.
127
+
128
+ ### Form controls
129
+
130
+ All are client components on Base UI's Field, Fieldset, Input, Checkbox, Radio Group and Switch, so
131
+ labels, descriptions and errors link to their controls without ids. On every form control, `ref` and
132
+ extra props go to the control, while `className` and `style` place the row.
133
+
134
+ - **`Field`:**
135
+ - a label, help text and error around any control;
136
+ - `optional` adds "(optional)", and Qeetrix never marks required fields;
137
+ - `error` shows a message now (from the server, say) and marks the field invalid. Without it, the
138
+ browser's or `validate`'s message shows with a sharp alert icon;
139
+ - the error replaces the help text;
140
+ - `hideLabel` is for toolbar searches only.
141
+ - **`Input`:** the filled box with a bottom line, a leading `icon`, and start and end adornments.
142
+ It takes its focus, invalid, read-only and disabled looks from the input.
143
+ - **`Textarea`:** the same look. It grows with its content between `rows` and `maxRows` through CSS
144
+ `field-sizing`, with no script.
145
+ - **`TextField`:** Field and Input in one, the default form row.
146
+ - **`Fieldset`:** a section of fields under a `title.sm` legend, spaced by the density's form gap.
147
+ `disabled` disables everything inside.
148
+ - **`Checkbox` and `CheckboxGroup`:**
149
+ - the 16px box with a sharp tick, and the mixed state with a dash;
150
+ - a `parent` checkbox selects the whole group;
151
+ - the label row is at least 24px tall.
152
+ - **`Radio` and `RadioGroup`:** the 5px Ember ring. Arrow keys move and select, and RadioGroup is
153
+ generic over its value type.
154
+ - **`Switch`:** a 32×18 track with a square thumb that moves to the end side, in either text
155
+ direction.
156
+ - With no visible label, Checkbox, Radio and Switch require `aria-label` or `aria-labelledby` by
157
+ their type.
158
+ - **Shared types:** `FieldMessageProps`, `ChoiceLabelProps`, `ChoiceOrientation`.
159
+
160
+ ### Overlays
161
+
162
+ All are client components on Base UI. Each has a root, a trigger that renders your control, and a
163
+ content part that assembles the portal, positioner and popup.
164
+
165
+ - **`Popover`:** an anchored panel with an optional title and description. Focus moves in on open
166
+ and returns to the trigger on close.
167
+ - **`Tooltip`:** one component around its trigger. It opens after 500ms of hover or at once on
168
+ focus. `TooltipProvider` groups neighbours, so the next one opens at once.
169
+ - **`Menu`:**
170
+ - items with sharp icons, shortcuts and a `danger` tone;
171
+ - checkbox and radio items in a 16px mark column;
172
+ - group labels, separators and submenus;
173
+ - the 2px Ember start bar on the highlighted item, which follows the text direction.
174
+ - **`Dialog`:**
175
+ - `intent` is `standard`, `danger` or `warning`, the last two as alert dialogs with a red or amber
176
+ intent line;
177
+ - `dismissible`, off by default for `danger`, so only its actions close it;
178
+ - sizes 400, 560 and 800px;
179
+ - the close button comes last in the focus order, so a dialog opens on its first field or its
180
+ least destructive action.
181
+ - **`Drawer`:**
182
+ - a full-height side panel at the `end` or `start` edge;
183
+ - ruled header and footer, and a scrolling body;
184
+ - focus goes to the first field, except on touch;
185
+ - it can be swiped away.
186
+ - **`ToastProvider` and `useToast`:**
187
+ - `add`, `close`, `update` and `promise`;
188
+ - tones `info`, `success`, `warning` and `danger`, each with a 3px status edge and a sharp icon;
189
+ - one action at most;
190
+ - `danger` toasts stay until dismissed and are announced at once.
191
+ - **`IconButton`** now shows its `aria-label` as a tooltip. `tooltip={false}` turns it off, and
192
+ other text replaces it.
193
+ - **Stacking:** anchored layers use `z.popover`, so a menu opened in a dialog sits above it.
194
+
195
+ ### Selection and disclosure
196
+
197
+ - **`Select`:**
198
+ - a field-style trigger with a chevron, and a menu-style list below it;
199
+ - the chosen option carries an Ember check;
200
+ - `items` can be flat or grouped, and `multiple` lists the chosen labels;
201
+ - generic over the value. A Field labels it.
202
+ - **`Tabs`, `TabsList`, `Tab`, `TabsPanel`:**
203
+ - the 3px Ember bar slides under the selected tab, and `TabsList` draws it;
204
+ - a selected tab has weight 600 and can fill its icon;
205
+ - padding is 12px at compact density and 16px otherwise;
206
+ - `activateOnFocus` is on by default;
207
+ - vertical tabs carry the line and bar on the start edge.
208
+ - **`Slider`:**
209
+ - a 4px track, the Ember range, and a 16px square thumb ringed in the surface colour;
210
+ - `label` and `showValue` with `Intl` formatting;
211
+ - a range renders one thumb per value, named Minimum and Maximum.
212
+ - **`Accordion`, `AccordionItem`:**
213
+ - ruled sections with a `title.sm` trigger inside a heading (`headingLevel`), and a turning
214
+ chevron;
215
+ - the panel's height animates;
216
+ - find in page opens a closed section that holds the match.
217
+ - **`Collapsible`, `CollapsibleTrigger`, `CollapsiblePanel`:** one unruled section.
218
+ - **`ScrollArea`:** 8px scrollbars that show on hover, while scrolling and on keyboard focus. The
219
+ region takes focus when it overflows.
220
+
221
+ ### Display and status
222
+
223
+ - **`Card`:**
224
+ - a raised surface with a 1px line, 4px corners and no shadow;
225
+ - a ruled header with `title`, the required heading `level`, `description` and `actions`, and an
226
+ optional ruled `footer`;
227
+ - a titled card is a section labelled by its title, and a table in the body runs edge to edge.
228
+ - Server-safe.
229
+ - **`Table`, `TableCaption`, `TableHeader`, `TableBody`, `TableRow`, `TableHead`, `TableCell`:**
230
+ - a neutral header band and row heights from the density;
231
+ - `numeric` cells use tabular figures, end-aligned;
232
+ - `selected` rows keep their Ember tint under the pointer.
233
+ - Server-safe.
234
+ - **`Badge`:** pills in six tones with an optional sharp icon. The `brand` fill is
235
+ `bg.brand.subtle`, so its text meets AA. Server-safe.
236
+ - **`Avatar`:** a circle with the photo, or initials while it loads or when it fails. The initials
237
+ use whole graphemes, so Indic names stay intact. It can be `decorative`.
238
+ - **`Progress` and `Meter`:** Slider's header, a 4px track and an Ember indicator (Meter takes a
239
+ status tone). Indeterminate progress slides, and fades under reduced motion.
240
+ - **`Skeleton`:** `text`, `block` or `circle`. It pulses, holds still under reduced motion, and is
241
+ always hidden from assistive technology. Server-safe.
242
+
243
+ ### Phase 4 close-out
244
+
245
+ - **`QeetrixProvider`:**
246
+ - `locale` (default `en-IN`) for formatting;
247
+ - `direction`, which wraps Base UI's DirectionProvider;
248
+ - `messages`, merged over the English defaults group by group.
249
+ - Nested providers inherit what they don't set. `useLocale()` reads the locale.
250
+ - **Strings:**
251
+ - every library string comes from one typed catalogue (`QeetrixMessages`);
252
+ - Field, TextField, DialogContent, DrawerContent, ToastProvider, Slider and Link take a `messages`
253
+ prop that wins over the provider;
254
+ - Link stays server-safe, so it takes its string per instance.
255
+ - **Locale packs:** Hindi (`@qeetrix/react/locales/hi`) is the first. It's a separate entry point,
256
+ typed as the complete catalogue, and uses Qeet ID's own Hindi words.
257
+ - **Formatting:** `formatNumber`, `formatCurrency` (lakh and crore grouping by default) and
258
+ `formatRelativeTime`. They're pure, server-safe and cache their formatters. Slider, Progress and
259
+ Meter format in the provider's locale.
260
+ - **Forced colours (Windows High Contrast):**
261
+ - surfaces that showed only a fill or a shadow get a system-colour edge;
262
+ - on states use `Highlight`: pressed Toggle, Switch, checked Checkbox, the Slider and Progress
263
+ fills, the Tabs bar, and highlighted Menu and Select items;
264
+ - separators and the tab list's base line stay visible.
265
+ - **Right-to-left:** tests cover arrow keys in Tabs, ToggleGroup, RadioGroup, Slider and the Menu
266
+ submenu, plus the Drawer edge, the Menu bar side and the Input adornment order.
267
+ - **Reduced motion:** a test checks that durations collapse and that Spinner and indeterminate
268
+ Progress fade instead.
269
+ - **Slider:** a slider named by `aria-label` (no visible label) now passes the name on to its
270
+ thumbs, which are what carry the slider role. A range adds Minimum and Maximum.
271
+ - **Sign-in exit screen:** built from primitives alone. Its keyboard order, axe, and its light,
272
+ dark, right-to-left and forced-colours screenshots are all tested.
273
+
274
+ ### Platform
275
+
276
+ - **Source check:**
277
+ - it bans physical CSS properties and system colours outside forced-colours blocks;
278
+ - it ignores comments when looking for client-only code.
279
+
280
+ - Each family's stylesheet (`css/<family>.css`) now imports the base layer and the stylesheets of
281
+ the families it renders, so it works on its own.
282
+ - Screenshots wait for transitions to finish, and overlays are captured across the whole viewport.
283
+ The accessibility check also waits, since contrast measured mid-fade isn't real. It skips Base
284
+ UI's aria-hidden focus guards, which hand focus on the moment it lands.
285
+ - A type-only import between families no longer pulls in the other family's stylesheet.
286
+ - Screenshots freeze endless animations (spinner, skeleton, indeterminate progress), so they match
287
+ every run.
288
+ - The nightly Firefox and WebKit run tests without V8 coverage, which works only in Chromium.
289
+ Every browser test passes in all three engines.
290
+
3
291
  ## 0.0.1
4
292
 
5
293
  The engineering platform, with no components yet:
package/README.md CHANGED
@@ -3,16 +3,19 @@
3
3
  The React components of **Qeetrix**, the Qeet Group design system: accessible, token-driven
4
4
  primitives built on [Base UI](https://base-ui.com), styled by the Qeetrix design language.
5
5
 
6
- > **Status: 0.0.x, platform only.** This release sets up the build, the stylesheet and the quality
7
- > gates. The first primitives (layout, text, separator and accessibility utilities) arrive in the
8
- > next increment.
6
+ > **Status: 0.0.x.** Every primitive is here: foundation, actions, form controls, overlays, selection
7
+ > and disclosure, and display and status. Components and patterns follow as Qeet products ask for
8
+ > them.
9
9
 
10
10
  ## Install
11
11
 
12
12
  ```bash
13
- bun add @qeetrix/react @qeetrix/tokens
13
+ bun add @qeetrix/react @qeetrix/tokens @base-ui/react @qeetrix/icons
14
14
  ```
15
15
 
16
+ `@base-ui/react` powers the interactive components. Icons come from `@qeetrix/icons`: a component's
17
+ `icon` prop takes any of its icons, e.g. `icon={PlusIcon}`.
18
+
16
19
  ## Styles
17
20
 
18
21
  Load the tokens, then the component styles, once at the root of your app:
@@ -26,6 +29,239 @@ The stylesheet is precompiled. Your Tailwind setup doesn't need to scan this pac
26
29
  utility classes. Qeetrix styles sit in the `qx.base` and `qx.components` cascade layers, so your own
27
30
  layout utilities override them without `!important`.
28
31
 
32
+ ## Use
33
+
34
+ ```tsx
35
+ import { PlusIcon } from "@qeetrix/icons/icons/plus";
36
+ import { Box, Button, Grid, Heading, Inline, SkipLink, Stack, Text } from "@qeetrix/react";
37
+
38
+ export function Invoices() {
39
+ return (
40
+ <>
41
+ <SkipLink />
42
+ <main id="main">
43
+ <Stack gap="xl">
44
+ <Inline justify="between">
45
+ <Heading level={1}>Invoices</Heading>
46
+ <Button icon={PlusIcon}>New invoice</Button>
47
+ </Inline>
48
+ <Grid columns={{ base: 1, medium: 2, large: 3 }} gap="md">
49
+ <Box padding="lg" background="raised" border="default" radius="surface">
50
+ <Text tone="subtle">Due this month</Text>
51
+ </Box>
52
+ </Grid>
53
+ </Stack>
54
+ </main>
55
+ </>
56
+ );
57
+ }
58
+ ```
59
+
60
+ - **Server-safe:** layout, type, `Link`, `Spinner`, `Card`, `Table`, `Badge` and `Skeleton` render in
61
+ React Server Components. Everything built on Base UI is a client component; its `"use client"`
62
+ directive ships in the build.
63
+ - **`render` prop:** renders a different element with the same props, e.g. `render={<section />}`.
64
+ - **Per-component entry points:** `@qeetrix/react/stack`, plus its own stylesheet at
65
+ `@qeetrix/react/css/stack.css` if you'd rather not load the full one. A family's stylesheet brings
66
+ the base styles and those of the components it renders (`css/text-field.css` brings Field and
67
+ Input).
68
+
69
+ | Component | For |
70
+ |---|---|
71
+ | `Box` | A token-only surface: padding, background, border, corners |
72
+ | `Stack`, `Inline` | Vertical and horizontal flows with token gaps (`none` … `4xl`) |
73
+ | `Grid` | Equal columns per window size class (600 / 840 / 1200 / 1600px) |
74
+ | `Text` | Body text: size, weight, tone, monospace, truncation |
75
+ | `Heading` | `level` sets h1–h6; `size` sets the look (display, heading, title) |
76
+ | `Separator` | A 1px line, horizontal or vertical |
77
+ | `VisuallyHidden` | Content for screen readers only |
78
+ | `SkipLink` | "Skip to main content", hidden until focused |
79
+ | `Button` | Label first, an optional trailing icon; solid (primary), soft (secondary), ghost; brand or danger; `loading` |
80
+ | `IconButton` | A square, icon-only button; `aria-label` is required by its type and shows as a tooltip |
81
+ | `Link` | Inline navigation; `external` opens a new tab safely and says so to screen readers |
82
+ | `Toggle`, `ToggleGroup` | Pressed or not; segmented or separate groups, single or `multiple` |
83
+ | `Spinner` | An indeterminate wait, announced as a status unless `decorative` |
84
+ | `TextField` | Label, input, help text and error in one: the default form row |
85
+ | `Field` | The label, help text and error around any control; `optional` adds "(optional)" |
86
+ | `Input`, `Textarea` | The filled text box with a bottom line; Textarea grows with its content |
87
+ | `Fieldset` | A section of fields under a heading, spaced by the density's form gap |
88
+ | `Checkbox`, `CheckboxGroup` | On/off or several of a set; mixed state; a `parent` checkbox selects all |
89
+ | `Radio`, `RadioGroup` | One of a set; arrow keys move and select |
90
+ | `Switch` | A setting that applies at once |
91
+ | `Popover` | A panel anchored to a trigger: filters, details, a short form |
92
+ | `Tooltip` | A short label for a control; `TooltipProvider` groups neighbours |
93
+ | `Menu` | Actions and options from a trigger: icons, shortcuts, checkbox and radio items, submenus |
94
+ | `Dialog` | A focused task or decision; `intent="danger"` makes a confirmation only its actions close |
95
+ | `Drawer` | A side panel for details or an edit form, swipeable on touch |
96
+ | `ToastProvider`, `useToast` | Brief notifications with a status edge; errors stay until dismissed |
97
+ | `Select` | One option, or several, from up to about 15; looks like a field, opens like a menu |
98
+ | `Tabs` | Views of one context, with the sliding Ember bar |
99
+ | `Slider` | A number or a range on a scale, with a formatted value |
100
+ | `Accordion`, `Collapsible` | Ruled sections, or one section, that show and hide |
101
+ | `ScrollArea` | A scrolling region with Qeetrix scrollbars |
102
+ | `Card` | A raised, ruled surface with a title, actions and footer; a table inside runs edge to edge |
103
+ | `Table` | Semantic table parts with the header band, density row heights and selected rows |
104
+ | `Badge` | A static status pill in six tones |
105
+ | `Avatar` | A person's photo, or their initials |
106
+ | `Progress`, `Meter` | A task advancing, or a measurement within a range |
107
+ | `Skeleton` | A placeholder shaped like the content that's loading |
108
+
109
+ **Layout:** `className` is for placement only (margin, width, grid placement). Visual changes go
110
+ through props, so every Qeet product looks the same. On a form control, `className` and `style` place
111
+ the whole row, while `ref` and every other prop go to the control itself.
112
+
113
+ ```tsx
114
+ import { Checkbox, CheckboxGroup, Fieldset, Radio, RadioGroup, TextField } from "@qeetrix/react";
115
+
116
+ <Fieldset legend="Company">
117
+ <TextField label="Trade name" name="tradeName" optional />
118
+ <TextField label="GSTIN" name="gstin" required description="15 characters." />
119
+ <RadioGroup label="Billing cycle" name="cycle" defaultValue="monthly">
120
+ <Radio value="monthly" label="Monthly" />
121
+ <Radio value="yearly" label="Yearly" description="Two months free." />
122
+ </RadioGroup>
123
+ <CheckboxGroup label="Notify me by" name="notify" orientation="horizontal">
124
+ <Checkbox value="email" label="Email" />
125
+ <Checkbox value="sms" label="SMS" />
126
+ </CheckboxGroup>
127
+ </Fieldset>
128
+ ```
129
+
130
+ ## Locale, direction and strings
131
+
132
+ Wrap the app in `QeetrixProvider` to set the locale (used for formatting), the reading direction and
133
+ the library's own strings ("Close", "(optional)", "Loading"…). Locale packs are separate imports, so
134
+ a language you don't use never ships:
135
+
136
+ ```tsx
137
+ import { QeetrixProvider } from "@qeetrix/react";
138
+ import { hi } from "@qeetrix/react/locales/hi";
139
+
140
+ <QeetrixProvider locale="hi-IN" messages={hi}>{app}</QeetrixProvider>
141
+ ```
142
+
143
+ - **Override one string:** pass a `messages` prop to the component, e.g.
144
+ `<Field messages={{ optional: "(if any)" }}>`.
145
+ - **Right-to-left:** use `direction="rtl"`, and set `dir="rtl"` on `<html>` too.
146
+ - **Server-safe components** (Link, Spinner, SkipLink) can't read the provider. Pass their strings
147
+ per instance.
148
+
149
+ Formatting helpers are plain functions and work on the server too:
150
+
151
+ ```ts
152
+ import { formatCurrency, formatNumber, formatRelativeTime } from "@qeetrix/react";
153
+
154
+ formatCurrency(124500, "INR"); // "₹1,24,500.00"
155
+ formatNumber(1234567); // "12,34,567"
156
+ formatRelativeTime(updatedAt, "en-IN", now); // "2 minutes ago"
157
+ ```
158
+
159
+ Every primitive also works in Windows High Contrast (forced colours) and under reduced motion. Tests
160
+ emulate both.
161
+
162
+ ## Experimental: the console frame
163
+
164
+ Components and patterns in their first product ship from `@qeetrix/react/experimental`. They may
165
+ change in a patch release until they're Ready.
166
+
167
+ ```tsx
168
+ import {
169
+ AppShell, PageContent, PageHeader, Sidebar, SidebarGroup, SidebarItem, Stat, StatGroup,
170
+ TopBar, TopBarSearch, TopBarSwitcher,
171
+ } from "@qeetrix/react/experimental";
172
+ import { Menu, MenuContent, MenuItem, MenuTrigger } from "@qeetrix/react";
173
+
174
+ <AppShell
175
+ topBar={
176
+ <TopBar
177
+ logo={<QeetLogo variant="dark" height={20} />}
178
+ product="Qeet ID"
179
+ context={
180
+ <>
181
+ <Menu>
182
+ <MenuTrigger render={<TopBarSwitcher mark="AC" label="Acme Pvt Ltd" />} />
183
+ <MenuContent>{organisations}</MenuContent>
184
+ </Menu>
185
+ <TopBarSwitcher status="success" label="Production" />
186
+ </>
187
+ }
188
+ search={<TopBarSearch placeholder="Search or jump to…" shortcut={["⌘", "K"]} onClick={openPalette} />}
189
+ account={accountMenu}
190
+ />
191
+ }
192
+ sidebar={
193
+ <Sidebar label="Qeet ID">
194
+ <SidebarGroup label="Directory">
195
+ <SidebarItem label="Users" icon={UsersIcon} current render={<Link to="/users" />} />
196
+ </SidebarGroup>
197
+ </Sidebar>
198
+ }
199
+ drawers={[{ id: "help", label: "Help", icon: CircleQuestionMarkIcon, content: help }]}
200
+ >
201
+ <PageHeader
202
+ title="Users"
203
+ actions={<Button icon={PlusIcon}>Invite users</Button>}
204
+ summary={
205
+ <StatGroup>
206
+ <Stat label="Total users" value={1284} note="+38 this week" tone="positive" trend={weekly} />
207
+ </StatGroup>
208
+ }
209
+ />
210
+ <PageContent>{table}</PageContent>
211
+ </AppShell>
212
+ ```
213
+
214
+ The bar is black in light and dark, topped by a 2px Ember line; the navigation collapses to a 48px
215
+ rail that expands while hovered. Also there: `Breadcrumb`, `EmptyState` and `DataState` (loading,
216
+ error, empty, content), and the secure-entry parts:
217
+
218
+ ```tsx
219
+ <Alert tone="warning" title="Copy this client secret now">It can't be shown again.</Alert>
220
+ <Field label="Client secret">
221
+ <PasswordInput readOnly autoComplete="off" defaultValue={secret}
222
+ endAdornment={<CopyButton value={secret} label="Copy client secret" size="sm" />} />
223
+ </Field>
224
+ <Field label="Verification code"><OTPInput size="lg" onValueComplete={verify} /></Field>
225
+ ```
226
+ Colour-scheme switching lives in `QeetrixProvider`
227
+ (`defaultColorScheme="system"`, `useColorScheme()`).
228
+
229
+ ## Overlays
230
+
231
+ Overlays share one shape: a root that holds the state, a trigger that renders your control, and a
232
+ content part that assembles everything else. Wrap the app once in `ToastProvider` (and, if you like,
233
+ `TooltipProvider`).
234
+
235
+ ```tsx
236
+ import {
237
+ Button, Dialog, DialogClose, DialogContent, DialogFooter, DialogTrigger, useToast,
238
+ } from "@qeetrix/react";
239
+
240
+ function RevokeKey({ onRevoke }: { onRevoke: () => Promise<void> }) {
241
+ const toast = useToast();
242
+ return (
243
+ <Dialog intent="danger">
244
+ <DialogTrigger render={<Button tone="danger">Revoke key</Button>} />
245
+ <DialogContent title="Revoke this API key?" description="Apps using it stop working at once.">
246
+ <DialogFooter>
247
+ <DialogClose render={<Button variant="ghost">Cancel</Button>} />
248
+ <DialogClose
249
+ render={<Button tone="danger">Revoke key</Button>}
250
+ onClick={() =>
251
+ toast.promise(onRevoke(), {
252
+ loading: { title: "Revoking…" },
253
+ success: { title: "Key revoked" },
254
+ error: { title: "Couldn't revoke the key" },
255
+ })
256
+ }
257
+ />
258
+ </DialogFooter>
259
+ </DialogContent>
260
+ </Dialog>
261
+ );
262
+ }
263
+ ```
264
+
29
265
  ## License
30
266
 
31
267
  [MIT](https://github.com/qeetgroup/qeetrix-react/blob/main/LICENSE) © Qeet Group
@@ -0,0 +1,42 @@
1
+ import { HeadingLevel } from "../heading/heading.js";
2
+ import { Accordion } from "@base-ui/react/accordion";
3
+ import { Collapsible } from "@base-ui/react/collapsible";
4
+ import { CSSProperties, ReactElement, ReactNode } from "react";
5
+ export interface AccordionProps<Value = string> extends Omit<Accordion.Root.Props<Value>, "className" | "style"> {
6
+ /** Placement only. */
7
+ className?: string;
8
+ style?: CSSProperties;
9
+ }
10
+ export interface AccordionItemProps extends Omit<Accordion.Item.Props, "className" | "style" | "title" | "children"> {
11
+ /** The section's title: the trigger's text. */
12
+ title: ReactNode;
13
+ /** The heading level that wraps the trigger. Default 3. */
14
+ headingLevel?: Exclude<HeadingLevel, 1>;
15
+ /** The section's content. */
16
+ children?: ReactNode;
17
+ className?: string;
18
+ style?: CSSProperties;
19
+ }
20
+ export type CollapsibleProps = Collapsible.Root.Props;
21
+ export type CollapsibleTriggerProps = Collapsible.Trigger.Props;
22
+ export interface CollapsiblePanelProps extends Omit<Collapsible.Panel.Props, "className" | "style"> {
23
+ className?: string;
24
+ style?: CSSProperties;
25
+ }
26
+ /**
27
+ * Stacked sections that show and hide, ruled between (06-component-specs.md, increment 5):
28
+ * `value` / `defaultValue` / `onValueChange(value[], details)`, `multiple`, `disabled`.
29
+ */
30
+ declare function Accordion$1<Value = string>({ className, ...props }: AccordionProps<Value>): ReactElement;
31
+ /**
32
+ * One section: a full-width trigger with a chevron, inside a heading, then the panel. Find in page
33
+ * opens a closed section that holds the match.
34
+ */
35
+ export declare function AccordionItem({ title, headingLevel, className, children, ...props }: AccordionItemProps): ReactElement;
36
+ /** One section without the rules, for "Show more" or advanced options. */
37
+ declare function Collapsible$1(props: CollapsibleProps): ReactElement;
38
+ /** Shows and hides the panel. Render your own control: `render={<Button variant="ghost" />}`. */
39
+ export declare function CollapsibleTrigger(props: CollapsibleTriggerProps): ReactElement;
40
+ /** The content that shows and hides, with the accordion's height animation. */
41
+ export declare function CollapsiblePanel({ className, ...props }: CollapsiblePanelProps): ReactElement;
42
+ export { Accordion$1 as Accordion, Collapsible$1 as Collapsible };
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { cx } from "../internal/cx.js";
3
+ import { Accordion } from "@base-ui/react/accordion";
4
+ import { Collapsible } from "@base-ui/react/collapsible";
5
+ import { ChevronDownIcon } from "@qeetrix/icons/icons/chevron-down";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ // src/accordion/accordion.tsx
8
+ /**
9
+ * Stacked sections that show and hide, ruled between (06-component-specs.md, increment 5):
10
+ * `value` / `defaultValue` / `onValueChange(value[], details)`, `multiple`, `disabled`.
11
+ */
12
+ function Accordion$1({ className, ...props }) {
13
+ return /* @__PURE__ */ jsx(Accordion.Root, {
14
+ ...props,
15
+ className: cx("qx-accordion", className),
16
+ "data-slot": "accordion"
17
+ });
18
+ }
19
+ /**
20
+ * One section: a full-width trigger with a chevron, inside a heading, then the panel. Find in page
21
+ * opens a closed section that holds the match.
22
+ */
23
+ function AccordionItem({ title, headingLevel = 3, className, children, ...props }) {
24
+ const Heading = `h${headingLevel}`;
25
+ return /* @__PURE__ */ jsxs(Accordion.Item, {
26
+ ...props,
27
+ className: cx("qx-accordion-item", className),
28
+ "data-slot": "accordion-item",
29
+ children: [/* @__PURE__ */ jsx(Accordion.Header, {
30
+ render: /* @__PURE__ */ jsx(Heading, {}),
31
+ className: "qx-accordion-header",
32
+ children: /* @__PURE__ */ jsxs(Accordion.Trigger, {
33
+ className: "qx-accordion-trigger",
34
+ "data-slot": "accordion-trigger",
35
+ children: [/* @__PURE__ */ jsx("span", {
36
+ className: "qx-accordion-title",
37
+ children: title
38
+ }), /* @__PURE__ */ jsx(ChevronDownIcon, {
39
+ shape: "sharp",
40
+ size: 16,
41
+ "aria-hidden": true,
42
+ className: "qx-accordion-icon"
43
+ })]
44
+ })
45
+ }), /* @__PURE__ */ jsx(Accordion.Panel, {
46
+ hiddenUntilFound: true,
47
+ className: "qx-accordion-panel",
48
+ "data-slot": "accordion-panel",
49
+ children: /* @__PURE__ */ jsx("div", {
50
+ className: "qx-accordion-content",
51
+ children
52
+ })
53
+ })]
54
+ });
55
+ }
56
+ /** One section without the rules, for "Show more" or advanced options. */
57
+ function Collapsible$1(props) {
58
+ return /* @__PURE__ */ jsx(Collapsible.Root, { ...props });
59
+ }
60
+ /** Shows and hides the panel. Render your own control: `render={<Button variant="ghost" />}`. */
61
+ function CollapsibleTrigger(props) {
62
+ return /* @__PURE__ */ jsx(Collapsible.Trigger, { ...props });
63
+ }
64
+ /** The content that shows and hides, with the accordion's height animation. */
65
+ function CollapsiblePanel({ className, ...props }) {
66
+ return /* @__PURE__ */ jsx(Collapsible.Panel, {
67
+ ...props,
68
+ className: cx("qx-collapsible-panel", className),
69
+ "data-slot": "collapsible-panel"
70
+ });
71
+ }
72
+ export { Accordion$1 as Accordion, AccordionItem, Collapsible$1 as Collapsible, CollapsiblePanel, CollapsibleTrigger };