@gerege-systems/ui 0.11.2

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 (218) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +234 -0
  3. package/dist-lib/__tests__/helpers/color.d.ts +9 -0
  4. package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
  5. package/dist-lib/components/ui/Accordion.d.ts +21 -0
  6. package/dist-lib/components/ui/Accordion.js +71 -0
  7. package/dist-lib/components/ui/Accordion.js.map +1 -0
  8. package/dist-lib/components/ui/Alert.d.ts +51 -0
  9. package/dist-lib/components/ui/Alert.js +79 -0
  10. package/dist-lib/components/ui/Alert.js.map +1 -0
  11. package/dist-lib/components/ui/Avatar.d.ts +52 -0
  12. package/dist-lib/components/ui/Avatar.js +94 -0
  13. package/dist-lib/components/ui/Avatar.js.map +1 -0
  14. package/dist-lib/components/ui/Badge.d.ts +26 -0
  15. package/dist-lib/components/ui/Badge.js +76 -0
  16. package/dist-lib/components/ui/Badge.js.map +1 -0
  17. package/dist-lib/components/ui/Breadcrumbs.d.ts +33 -0
  18. package/dist-lib/components/ui/Breadcrumbs.js +52 -0
  19. package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
  20. package/dist-lib/components/ui/Button.d.ts +52 -0
  21. package/dist-lib/components/ui/Button.js +131 -0
  22. package/dist-lib/components/ui/Button.js.map +1 -0
  23. package/dist-lib/components/ui/Calendar.d.ts +15 -0
  24. package/dist-lib/components/ui/Calendar.js +139 -0
  25. package/dist-lib/components/ui/Calendar.js.map +1 -0
  26. package/dist-lib/components/ui/Card.d.ts +39 -0
  27. package/dist-lib/components/ui/Card.js +97 -0
  28. package/dist-lib/components/ui/Card.js.map +1 -0
  29. package/dist-lib/components/ui/Carousel.d.ts +27 -0
  30. package/dist-lib/components/ui/Carousel.js +174 -0
  31. package/dist-lib/components/ui/Carousel.js.map +1 -0
  32. package/dist-lib/components/ui/Chart.d.ts +87 -0
  33. package/dist-lib/components/ui/Chart.js +487 -0
  34. package/dist-lib/components/ui/Chart.js.map +1 -0
  35. package/dist-lib/components/ui/Checkbox.d.ts +28 -0
  36. package/dist-lib/components/ui/Checkbox.js +70 -0
  37. package/dist-lib/components/ui/Checkbox.js.map +1 -0
  38. package/dist-lib/components/ui/Combobox.d.ts +67 -0
  39. package/dist-lib/components/ui/Combobox.js +195 -0
  40. package/dist-lib/components/ui/Combobox.js.map +1 -0
  41. package/dist-lib/components/ui/CommandPalette.d.ts +112 -0
  42. package/dist-lib/components/ui/CommandPalette.js +179 -0
  43. package/dist-lib/components/ui/CommandPalette.js.map +1 -0
  44. package/dist-lib/components/ui/ContextMenu.d.ts +50 -0
  45. package/dist-lib/components/ui/ContextMenu.js +165 -0
  46. package/dist-lib/components/ui/ContextMenu.js.map +1 -0
  47. package/dist-lib/components/ui/DataGrid.d.ts +79 -0
  48. package/dist-lib/components/ui/DataGrid.js +99 -0
  49. package/dist-lib/components/ui/DataGrid.js.map +1 -0
  50. package/dist-lib/components/ui/DatePicker.d.ts +45 -0
  51. package/dist-lib/components/ui/DatePicker.js +193 -0
  52. package/dist-lib/components/ui/DatePicker.js.map +1 -0
  53. package/dist-lib/components/ui/DesignSystemProvider.d.ts +147 -0
  54. package/dist-lib/components/ui/DesignSystemProvider.js +135 -0
  55. package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
  56. package/dist-lib/components/ui/Dialog.d.ts +77 -0
  57. package/dist-lib/components/ui/Dialog.js +165 -0
  58. package/dist-lib/components/ui/Dialog.js.map +1 -0
  59. package/dist-lib/components/ui/Drawer.d.ts +44 -0
  60. package/dist-lib/components/ui/Drawer.js +132 -0
  61. package/dist-lib/components/ui/Drawer.js.map +1 -0
  62. package/dist-lib/components/ui/DropdownMenu.d.ts +47 -0
  63. package/dist-lib/components/ui/DropdownMenu.js +169 -0
  64. package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
  65. package/dist-lib/components/ui/EmptyState.d.ts +48 -0
  66. package/dist-lib/components/ui/EmptyState.js +46 -0
  67. package/dist-lib/components/ui/EmptyState.js.map +1 -0
  68. package/dist-lib/components/ui/ErrorState.d.ts +51 -0
  69. package/dist-lib/components/ui/ErrorState.js +69 -0
  70. package/dist-lib/components/ui/ErrorState.js.map +1 -0
  71. package/dist-lib/components/ui/FileUpload.d.ts +26 -0
  72. package/dist-lib/components/ui/FileUpload.js +111 -0
  73. package/dist-lib/components/ui/FileUpload.js.map +1 -0
  74. package/dist-lib/components/ui/Form.d.ts +36 -0
  75. package/dist-lib/components/ui/Form.js +131 -0
  76. package/dist-lib/components/ui/Form.js.map +1 -0
  77. package/dist-lib/components/ui/Icon.d.ts +34 -0
  78. package/dist-lib/components/ui/Icon.js +32 -0
  79. package/dist-lib/components/ui/Icon.js.map +1 -0
  80. package/dist-lib/components/ui/IconButton.d.ts +34 -0
  81. package/dist-lib/components/ui/IconButton.js +64 -0
  82. package/dist-lib/components/ui/IconButton.js.map +1 -0
  83. package/dist-lib/components/ui/Input.d.ts +54 -0
  84. package/dist-lib/components/ui/Input.js +158 -0
  85. package/dist-lib/components/ui/Input.js.map +1 -0
  86. package/dist-lib/components/ui/Kbd.d.ts +16 -0
  87. package/dist-lib/components/ui/Kbd.js +24 -0
  88. package/dist-lib/components/ui/Kbd.js.map +1 -0
  89. package/dist-lib/components/ui/MultiSelect.d.ts +64 -0
  90. package/dist-lib/components/ui/MultiSelect.js +210 -0
  91. package/dist-lib/components/ui/MultiSelect.js.map +1 -0
  92. package/dist-lib/components/ui/Pagination.d.ts +55 -0
  93. package/dist-lib/components/ui/Pagination.js +153 -0
  94. package/dist-lib/components/ui/Pagination.js.map +1 -0
  95. package/dist-lib/components/ui/Popover.d.ts +16 -0
  96. package/dist-lib/components/ui/Popover.js +35 -0
  97. package/dist-lib/components/ui/Popover.js.map +1 -0
  98. package/dist-lib/components/ui/Progress.d.ts +40 -0
  99. package/dist-lib/components/ui/Progress.js +100 -0
  100. package/dist-lib/components/ui/Progress.js.map +1 -0
  101. package/dist-lib/components/ui/RadioGroup.d.ts +27 -0
  102. package/dist-lib/components/ui/RadioGroup.js +81 -0
  103. package/dist-lib/components/ui/RadioGroup.js.map +1 -0
  104. package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
  105. package/dist-lib/components/ui/RelativeTime.js +31 -0
  106. package/dist-lib/components/ui/RelativeTime.js.map +1 -0
  107. package/dist-lib/components/ui/ScrollArea.d.ts +21 -0
  108. package/dist-lib/components/ui/ScrollArea.js +60 -0
  109. package/dist-lib/components/ui/ScrollArea.js.map +1 -0
  110. package/dist-lib/components/ui/Select.d.ts +61 -0
  111. package/dist-lib/components/ui/Select.js +139 -0
  112. package/dist-lib/components/ui/Select.js.map +1 -0
  113. package/dist-lib/components/ui/Separator.d.ts +13 -0
  114. package/dist-lib/components/ui/Separator.js +26 -0
  115. package/dist-lib/components/ui/Separator.js.map +1 -0
  116. package/dist-lib/components/ui/Sheet.d.ts +55 -0
  117. package/dist-lib/components/ui/Sheet.js +136 -0
  118. package/dist-lib/components/ui/Sheet.js.map +1 -0
  119. package/dist-lib/components/ui/Sidebar.d.ts +91 -0
  120. package/dist-lib/components/ui/Sidebar.js +212 -0
  121. package/dist-lib/components/ui/Sidebar.js.map +1 -0
  122. package/dist-lib/components/ui/Skeleton.d.ts +37 -0
  123. package/dist-lib/components/ui/Skeleton.js +29 -0
  124. package/dist-lib/components/ui/Skeleton.js.map +1 -0
  125. package/dist-lib/components/ui/Slider.d.ts +33 -0
  126. package/dist-lib/components/ui/Slider.js +64 -0
  127. package/dist-lib/components/ui/Slider.js.map +1 -0
  128. package/dist-lib/components/ui/Snackbar.d.ts +21 -0
  129. package/dist-lib/components/ui/Snackbar.js +57 -0
  130. package/dist-lib/components/ui/Snackbar.js.map +1 -0
  131. package/dist-lib/components/ui/Spinner.d.ts +23 -0
  132. package/dist-lib/components/ui/Spinner.js +54 -0
  133. package/dist-lib/components/ui/Spinner.js.map +1 -0
  134. package/dist-lib/components/ui/Stepper.d.ts +40 -0
  135. package/dist-lib/components/ui/Stepper.js +111 -0
  136. package/dist-lib/components/ui/Stepper.js.map +1 -0
  137. package/dist-lib/components/ui/Switch.d.ts +33 -0
  138. package/dist-lib/components/ui/Switch.js +80 -0
  139. package/dist-lib/components/ui/Switch.js.map +1 -0
  140. package/dist-lib/components/ui/Table.d.ts +71 -0
  141. package/dist-lib/components/ui/Table.js +179 -0
  142. package/dist-lib/components/ui/Table.js.map +1 -0
  143. package/dist-lib/components/ui/Tabs.d.ts +32 -0
  144. package/dist-lib/components/ui/Tabs.js +91 -0
  145. package/dist-lib/components/ui/Tabs.js.map +1 -0
  146. package/dist-lib/components/ui/TagInput.d.ts +39 -0
  147. package/dist-lib/components/ui/TagInput.js +111 -0
  148. package/dist-lib/components/ui/TagInput.js.map +1 -0
  149. package/dist-lib/components/ui/Textarea.d.ts +34 -0
  150. package/dist-lib/components/ui/Textarea.js +88 -0
  151. package/dist-lib/components/ui/Textarea.js.map +1 -0
  152. package/dist-lib/components/ui/Timeline.d.ts +33 -0
  153. package/dist-lib/components/ui/Timeline.js +56 -0
  154. package/dist-lib/components/ui/Timeline.js.map +1 -0
  155. package/dist-lib/components/ui/Toast.d.ts +53 -0
  156. package/dist-lib/components/ui/Toast.js +158 -0
  157. package/dist-lib/components/ui/Toast.js.map +1 -0
  158. package/dist-lib/components/ui/Tooltip.d.ts +38 -0
  159. package/dist-lib/components/ui/Tooltip.js +60 -0
  160. package/dist-lib/components/ui/Tooltip.js.map +1 -0
  161. package/dist-lib/components/ui/TopNav.d.ts +37 -0
  162. package/dist-lib/components/ui/TopNav.js +69 -0
  163. package/dist-lib/components/ui/TopNav.js.map +1 -0
  164. package/dist-lib/components/ui/Tree.d.ts +51 -0
  165. package/dist-lib/components/ui/Tree.js +170 -0
  166. package/dist-lib/components/ui/Tree.js.map +1 -0
  167. package/dist-lib/hooks/use-debounce.d.ts +19 -0
  168. package/dist-lib/hooks/use-debounce.js +36 -0
  169. package/dist-lib/hooks/use-debounce.js.map +1 -0
  170. package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
  171. package/dist-lib/hooks/use-delayed-loading.js +20 -0
  172. package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
  173. package/dist-lib/hooks/use-field-ids.d.ts +30 -0
  174. package/dist-lib/hooks/use-field-ids.js +18 -0
  175. package/dist-lib/hooks/use-field-ids.js.map +1 -0
  176. package/dist-lib/hooks/use-media-query.d.ts +11 -0
  177. package/dist-lib/hooks/use-media-query.js +22 -0
  178. package/dist-lib/hooks/use-media-query.js.map +1 -0
  179. package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
  180. package/dist-lib/hooks/use-modifier-key.js +19 -0
  181. package/dist-lib/hooks/use-modifier-key.js.map +1 -0
  182. package/dist-lib/hooks/use-strings.d.ts +5 -0
  183. package/dist-lib/hooks/use-strings.js +12 -0
  184. package/dist-lib/hooks/use-strings.js.map +1 -0
  185. package/dist-lib/hooks/use-toast.d.ts +48 -0
  186. package/dist-lib/hooks/use-toast.js +67 -0
  187. package/dist-lib/hooks/use-toast.js.map +1 -0
  188. package/dist-lib/icon.d.ts +8 -0
  189. package/dist-lib/icon.js +7 -0
  190. package/dist-lib/icon.js.map +1 -0
  191. package/dist-lib/icons/index.d.ts +80 -0
  192. package/dist-lib/icons/index.js +163 -0
  193. package/dist-lib/icons/index.js.map +1 -0
  194. package/dist-lib/illustrations/index.d.ts +10 -0
  195. package/dist-lib/illustrations/index.js +114 -0
  196. package/dist-lib/illustrations/index.js.map +1 -0
  197. package/dist-lib/index.d.ts +68 -0
  198. package/dist-lib/index.js +291 -0
  199. package/dist-lib/index.js.map +1 -0
  200. package/dist-lib/lib/cva.d.ts +6 -0
  201. package/dist-lib/lib/format.d.ts +65 -0
  202. package/dist-lib/lib/format.js +95 -0
  203. package/dist-lib/lib/format.js.map +1 -0
  204. package/dist-lib/lib/return-focus.d.ts +11 -0
  205. package/dist-lib/lib/return-focus.js +12 -0
  206. package/dist-lib/lib/return-focus.js.map +1 -0
  207. package/dist-lib/lib/strings.d.ts +190 -0
  208. package/dist-lib/lib/strings.js +141 -0
  209. package/dist-lib/lib/strings.js.map +1 -0
  210. package/dist-lib/lib/strings.mn.d.ts +3 -0
  211. package/dist-lib/lib/strings.mn.js +115 -0
  212. package/dist-lib/lib/strings.mn.js.map +1 -0
  213. package/dist-lib/lib/utils.d.ts +11 -0
  214. package/dist-lib/lib/utils.js +10 -0
  215. package/dist-lib/lib/utils.js.map +1 -0
  216. package/dist-lib/styles.css +1 -0
  217. package/dist-lib/theme.css +840 -0
  218. package/package.json +158 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gerege Systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # @gerege-systems/ui
2
+
3
+ A refined-minimal Tailwind v4 + React design system. Production-grade
4
+ primitives — Button through DataGrid — plus composed patterns
5
+ (authentication, app shell, settings, etc.).
6
+
7
+ - **Showcase:** [ui.gecore.mn](https://ui.gecore.mn)
8
+ - **Components:** [ui.gecore.mn#components](https://ui.gecore.mn#components)
9
+ - **Templates:** [ui.gecore.mn#templates](https://ui.gecore.mn#templates)
10
+
11
+ > **Aesthetic direction:** Linear / Vercel / Stripe Dashboard / Notion / Raycast.
12
+ > Neutral-dominant, one accent, hairline borders, generous whitespace, fast quiet
13
+ > motion. See [`docs/PHILOSOPHY.md`](./docs/PHILOSOPHY.md).
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pnpm add @gerege-systems/ui # peers: react ^18 || ^19, react-dom ^18 || ^19
19
+ ```
20
+
21
+ **1. Fonts** — Geist + Geist Mono are referenced by the tokens but not bundled (Inter is only a fallback in the stack). Google Fonts serves the `cyrillic-ext` subset automatically; self-hosting (≤4 woff2, weights 400/500/600) is equally fine. Add to `<head>`:
22
+
23
+ ```html
24
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
25
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
26
+ <link
27
+ href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500&display=swap"
28
+ rel="stylesheet"
29
+ />
30
+ ```
31
+
32
+ **2. CSS** — pick one:
33
+
34
+ ```css
35
+ /* a) No Tailwind in your app: one precompiled sheet (tokens + base + every utility the components use) */
36
+ @import '@gerege-systems/ui/styles.css';
37
+
38
+ /* b) Your app already uses Tailwind v4: share the tokens and let Tailwind compile the library's classes */
39
+ @import 'tailwindcss';
40
+ @import 'tw-animate-css'; /* required on this path — overlays use animate-in / animate-out */
41
+ @import '@gerege-systems/ui/theme.css';
42
+ @source "../node_modules/@gerege-systems/ui/dist-lib";
43
+ ```
44
+
45
+ Path (b) needs `tw-animate-css` installed (`pnpm add -D tw-animate-css`; it is an optional peer). Path (a) already bundles it.
46
+
47
+ **3. Dark mode** — toggle the `dark` class on `<html>`; every token flips and `color-scheme` follows. Set it from a blocking script before first paint to avoid a flash (three states: light / dark / system). `data-theme` is not used.
48
+
49
+ **4. Providers** — `<Toaster />` once near the root if you use `toast()`; wrap the app in `<TooltipProvider>` if you use `Tooltip`. Nothing else is required.
50
+
51
+ ```tsx
52
+ import { Button, Toaster, TooltipProvider, toast } from '@gerege-systems/ui';
53
+
54
+ export function App() {
55
+ return (
56
+ <TooltipProvider>
57
+ <Button onClick={() => toast({ title: 'Saved' })}>Save</Button>
58
+ <Toaster />
59
+ </TooltipProvider>
60
+ );
61
+ }
62
+ ```
63
+
64
+ **Next.js App Router** — since 0.10 every built module carries a `'use client'` banner, so `import { Button } from '@gerege-systems/ui'` works directly inside Server Components without a local re-export file. Put the CSS import and fonts in `app/layout.tsx`. Note: the pure helpers (`formatDate`, `formatNumber`, `formatMNT`, `mnStrings`, `defaultStrings`) are also client-marked today — they still run fine in Server Components (the directive only affects the boundary), but they are not tree-shaken into a server-only chunk.
65
+
66
+ **Name-addressed icons** — `Icons.*` (curated, tree-shaken) ships in the main entry. The lazy `<Icon name="…">` + `iconNames` list lives in a separate entry so its ~1500-icon import map never enters your bundle unless asked for:
67
+
68
+ ```tsx
69
+ import { Icon } from '@gerege-systems/ui/icon';
70
+ <Icon name="calendar" className="size-4" />;
71
+ ```
72
+
73
+ The package is ESM-only, ships one module per component (`sideEffects` limited to CSS), so `import { Button }` pulls in about 8 KB gzipped — no calendar, drawer, or form dependencies.
74
+
75
+ ## Localisation (Mongolian built in)
76
+
77
+ Every built-in string (close/dismiss labels, placeholders, "No results.", pagination summary, error-state copy…) is typed in `UiStrings` and read through `useStrings()`. Defaults are English; a full Mongolian set ships as `mnStrings`.
78
+
79
+ ```tsx
80
+ import { DesignSystemProvider, mnStrings } from '@gerege-systems/ui';
81
+ <DesignSystemProvider strings={mnStrings}>…</DesignSystemProvider>; // whole library in Mongolian
82
+ <DesignSystemProvider strings={{ dialog: { close: 'Schließen' } }}>…</DesignSystemProvider>; // partial override, deep-merged over defaults
83
+ ```
84
+
85
+ Precedence is per-component props (`placeholder`, `labels`, `aria-label`) → nearest provider → `defaultStrings`. Templates use `{name}` placeholders, e.g. `pagination.showing: '{from}–{to} / {total}'`.
86
+
87
+ ## Design rules
88
+
89
+ This library implements the [gerege-systems design-research](https://github.com/gerege-systems/design-research) guidelines
90
+ ([rendered site](https://gerege-systems.github.io/design-research/)) — colour, type, spacing, components, accessibility, tokens.
91
+ design-research is the source of truth (canonical numbers in [`00-defaults.md`](https://github.com/gerege-systems/design-research/blob/main/00-defaults.md));
92
+ [`docs/PHILOSOPHY.md`](./docs/PHILOSOPHY.md) is the library-specific summary.
93
+
94
+ ## Local development
95
+
96
+ This package lives in the [`gerege-ui`](https://github.com/gerege-systems/gerege-ui)
97
+ monorepo (`packages/ui`). The showcase site is a separate workspace
98
+ (`apps/site`) that consumes this package's source directly. From the repo root:
99
+
100
+ ```bash
101
+ pnpm install
102
+ pnpm dev # showcase site (apps/site) — edits to this package hot-reload
103
+ pnpm build:lib # build this library → packages/ui/dist-lib/
104
+ ```
105
+
106
+ Inside `packages/ui` itself, `pnpm build` produces the distributable bundle and
107
+ `pnpm test` runs the component tests.
108
+
109
+ ## Tech stack
110
+
111
+ - **Tailwind CSS v4** — tokens defined in `@theme` in `src/styles/theme.css` (`globals.css` = tailwind + tw-animate-css + theme)
112
+ - **React 18 / 19** + **TypeScript 5.7**
113
+ - **Radix UI** primitives for accessibility-correct overlays
114
+ - **class-variance-authority** + `cn()` (`clsx` + `tailwind-merge`)
115
+ - **Lucide** icons (16 / 20px, 1.5 stroke)
116
+ - **Geist** sans + **Geist Mono** monospace
117
+
118
+ ## Project structure
119
+
120
+ ```
121
+ src/
122
+ ├── styles/
123
+ │ ├── theme.css # @theme tokens, semantic vars, dark variant, base layer
124
+ │ └── globals.css # tailwindcss + tw-animate-css + theme.css
125
+ ├── lib/
126
+ │ ├── utils.ts # cn() + uid()
127
+ │ ├── format.ts # formatDate / formatNumber / formatMNT
128
+ │ └── strings.ts # UiStrings, defaultStrings (+ strings.mn.ts)
129
+ ├── components/
130
+ │ ├── ui/ # primitives — 52 components
131
+ │ └── patterns/ # composed layouts
132
+ ├── hooks/
133
+ │ ├── use-toast.ts
134
+ │ └── use-media-query.ts
135
+ └── icons/ # curated Lucide re-exports
136
+ docs/
137
+ ├── PHILOSOPHY.md # the 6 refined-minimal principles
138
+ ├── VOICE.md # content + tone of voice
139
+ └── ACCESSIBILITY.md # WCAG checklist + contrast ratios
140
+ ```
141
+
142
+ ## Component index
143
+
144
+ ### Inputs
145
+
146
+ - [Input](./src/components/ui/Input.tsx) — text / email / password / number / search with prefix, suffix, error
147
+ - [Textarea](./src/components/ui/Textarea.tsx) — auto-resize multi-line input
148
+ - [Select](./src/components/ui/Select.tsx) — single-choice menu (Radix)
149
+ - [MultiSelect](./src/components/ui/MultiSelect.tsx) — chip-based multi-choice picker
150
+ - [Combobox](./src/components/ui/Combobox.tsx) — searchable single-select, sync or async
151
+ - [Checkbox](./src/components/ui/Checkbox.tsx) — including indeterminate
152
+ - [RadioGroup](./src/components/ui/RadioGroup.tsx) — mutually-exclusive choices
153
+ - [Switch](./src/components/ui/Switch.tsx) — instant-apply binary toggle
154
+ - [Slider](./src/components/ui/Slider.tsx) — single + range
155
+ - [DatePicker](./src/components/ui/DatePicker.tsx) — single + range
156
+ - [Form primitives](./src/components/ui/Form.tsx) — react-hook-form bindings
157
+
158
+ ### Buttons
159
+
160
+ - [Button](./src/components/ui/Button.tsx) — primary · secondary · outline · ghost · destructive · link
161
+ - [IconButton](./src/components/ui/IconButton.tsx) — square icon-only variant
162
+ - [Pagination](./src/components/ui/Pagination.tsx) — numbered + jumps + page-size
163
+
164
+ ### Feedback
165
+
166
+ - [Alert](./src/components/ui/Alert.tsx) — inline banner, dismissible
167
+ - [Toast](./src/components/ui/Toast.tsx) + `useToast` hook
168
+ - [Spinner](./src/components/ui/Spinner.tsx) — accent / neutral / on-accent tones
169
+ - [Progress](./src/components/ui/Progress.tsx) — linear + circular, determinate + indeterminate
170
+ - [Skeleton](./src/components/ui/Skeleton.tsx) — text, avatar, card variants
171
+ - [EmptyState](./src/components/ui/EmptyState.tsx)
172
+ - [ErrorState](./src/components/ui/ErrorState.tsx) — 404 / 500 / generic
173
+
174
+ ### Navigation
175
+
176
+ - [TopNav](./src/components/ui/TopNav.tsx) + `TopNavLink`
177
+ - [Sidebar](./src/components/ui/Sidebar.tsx) + `SidebarSection` + `SidebarItem` + `SidebarGroup`
178
+ - [Breadcrumbs](./src/components/ui/Breadcrumbs.tsx) — with overflow ellipsis
179
+ - [Tabs](./src/components/ui/Tabs.tsx) — underline + pills variants
180
+ - [Stepper](./src/components/ui/Stepper.tsx) — horizontal + vertical
181
+
182
+ ### Layout
183
+
184
+ - [Card](./src/components/ui/Card.tsx) + `CardHeader/Title/Description/Content/Footer`
185
+ - [Separator](./src/components/ui/Separator.tsx)
186
+ - [ScrollArea](./src/components/ui/ScrollArea.tsx) — Radix-backed styled scroll
187
+ - [Accordion](./src/components/ui/Accordion.tsx) — single + multiple
188
+
189
+ ### Overlays
190
+
191
+ - [Dialog](./src/components/ui/Dialog.tsx) + `ConfirmationDialog`
192
+ - [Sheet](./src/components/ui/Sheet.tsx) — left / right / top / bottom
193
+ - [Popover](./src/components/ui/Popover.tsx)
194
+ - [Tooltip](./src/components/ui/Tooltip.tsx) — 500ms delay default
195
+ - [DropdownMenu](./src/components/ui/DropdownMenu.tsx) — submenus, separators, kbd
196
+ - [ContextMenu](./src/components/ui/ContextMenu.tsx) — right-click menu
197
+ - [CommandPalette](./src/components/ui/CommandPalette.tsx) — ⌘K palette
198
+
199
+ ### Data display
200
+
201
+ - [Table](./src/components/ui/Table.tsx) + `TableSortHeader`
202
+ - [DataGrid](./src/components/ui/DataGrid.tsx) — column visibility, filter, sortable
203
+ - [Badge](./src/components/ui/Badge.tsx) — subtle + outline, 6 tones
204
+ - [Avatar](./src/components/ui/Avatar.tsx) + `AvatarGroup`
205
+
206
+ ### Typography
207
+
208
+ - [Kbd](./src/components/ui/Kbd.tsx) — keyboard shortcut indicator
209
+
210
+ ## Blocks (page templates)
211
+
212
+ Whole-page compositions — dashboard, settings, auth, pricing, data table,
213
+ record detail, onboarding, first-run — are **not** shipped as importable
214
+ components. They live in the showcase as copy-paste **blocks**: complete pages
215
+ assembled from the primitives above, with the full source on the page. Read it,
216
+ copy it, adapt it — no opaque `<Dashboard />` import. Browse them under
217
+ **Templates** in the [showcase](https://ui.gecore.mn/#templates).
218
+
219
+ ## Documentation
220
+
221
+ - [`docs/PHILOSOPHY.md`](./docs/PHILOSOPHY.md) — the six principles + forbidden list
222
+ - [`docs/VOICE.md`](./docs/VOICE.md) — tone of voice, button labels, error copy formula
223
+ - [`docs/ACCESSIBILITY.md`](./docs/ACCESSIBILITY.md) — WCAG AA contrast table, keyboard map
224
+
225
+ ## Contributing
226
+
227
+ 1. Read [`docs/PHILOSOPHY.md`](./docs/PHILOSOPHY.md) first — the forbidden list is
228
+ non-negotiable.
229
+ 2. Components reference semantic tokens (`bg-card`, `text-accent`), never raw
230
+ palette steps (`bg-indigo-500`).
231
+ 3. Every interactive element ships with: default, hover, focus-visible, active,
232
+ disabled, loading, and (where applicable) error / success states.
233
+ 4. Forward refs correctly; set `displayName`.
234
+ 5. Run `pnpm typecheck && pnpm test` before opening a PR.
@@ -0,0 +1,9 @@
1
+ /** Test-only colour maths: CSS `hsl()` / `oklch()` / hex → sRGB, WCAG 2.x contrast. */
2
+ export type RGB = [number, number, number];
3
+ export declare function parseColor(input: string): RGB;
4
+ export declare function hslToRgb(h: number, s: number, l: number): RGB;
5
+ /** OKLCH → OKLab → linear sRGB → gamma sRGB (clipped to gamut). */
6
+ export declare function oklchToRgb(L: number, C: number, hDeg: number): RGB;
7
+ export declare function luminance([r, g, b]: RGB): number;
8
+ export declare function contrast(a: RGB, b: RGB): number;
9
+ export declare function toHex([r, g, b]: RGB): string;
@@ -0,0 +1,18 @@
1
+ export declare const THEME_PATH: any;
2
+ export type TokenMap = Record<string, string>;
3
+ export interface ParsedTheme {
4
+ /** `:root { … }` semantic block. */
5
+ light: TokenMap;
6
+ /** `.dark { … }` semantic block. */
7
+ dark: TokenMap;
8
+ /** `@theme { … }` raw primitives (fonts, text scale, radius, shadow, z, motion, palette). */
9
+ raw: TokenMap;
10
+ /** `[data-accent='x']` presets: light (`:root[...]`) and dark (`.dark[...]`) overrides. */
11
+ accents: Record<string, {
12
+ shared: TokenMap;
13
+ light: TokenMap;
14
+ dark: TokenMap;
15
+ }>;
16
+ }
17
+ export declare function parseDeclarations(body: string): TokenMap;
18
+ export declare function parseTheme(css?: any): ParsedTheme;
@@ -0,0 +1,21 @@
1
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
2
+ /**
3
+ * Accessible disclosure list. Supports single (`type="single"`) and multiple
4
+ * (`type="multiple"`) open behaviour — both come from Radix.
5
+ *
6
+ * @example FAQ
7
+ * <Accordion type="single" collapsible>
8
+ * <AccordionItem value="q1">
9
+ * <AccordionTrigger>Can I cancel anytime?</AccordionTrigger>
10
+ * <AccordionContent>Yes — usage stops at the end of the billing period.</AccordionContent>
11
+ * </AccordionItem>
12
+ * …
13
+ * </Accordion>
14
+ *
15
+ * @do Use for FAQs, settings groups, and progressive-disclosure forms.
16
+ * @dont Nest accordions inside accordions — the focus order becomes opaque.
17
+ */
18
+ export declare const Accordion: import('react').ForwardRefExoticComponent<(AccordionPrimitive.AccordionSingleProps | AccordionPrimitive.AccordionMultipleProps) & import('react').RefAttributes<HTMLDivElement>>;
19
+ export declare const AccordionItem: import('react').ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionItemProps & import('react').RefAttributes<HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
20
+ export declare const AccordionTrigger: import('react').ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionTriggerProps & import('react').RefAttributes<HTMLButtonElement>, "ref"> & import('react').RefAttributes<HTMLButtonElement>>;
21
+ export declare const AccordionContent: import('react').ForwardRefExoticComponent<Omit<AccordionPrimitive.AccordionContentProps & import('react').RefAttributes<HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ import { jsx as t, jsxs as d } from "react/jsx-runtime";
3
+ import { forwardRef as a } from "react";
4
+ import * as n from "@radix-ui/react-accordion";
5
+ import { ChevronDown as m } from "../../icons/index.js";
6
+ import { cn as i } from "../../lib/utils.js";
7
+ const v = n.Root, f = a(function({ className: o, ...e }, r) {
8
+ return /* @__PURE__ */ t(
9
+ n.Item,
10
+ {
11
+ ref: r,
12
+ className: i("border-border border-b", o),
13
+ ...e
14
+ }
15
+ );
16
+ });
17
+ f.displayName = "AccordionItem";
18
+ const l = a(function({ className: o, children: e, ...r }, c) {
19
+ return /* @__PURE__ */ t(n.Header, { className: "flex", children: /* @__PURE__ */ d(
20
+ n.Trigger,
21
+ {
22
+ ref: c,
23
+ className: i(
24
+ "text-foreground flex flex-1 items-center justify-between gap-2 py-4 text-left text-sm font-medium",
25
+ "transition-colors duration-[var(--duration-fast)] outline-none",
26
+ "hover:text-accent",
27
+ "focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2",
28
+ "[&[data-state=open]>svg]:rotate-180",
29
+ o
30
+ ),
31
+ ...r,
32
+ children: [
33
+ e,
34
+ /* @__PURE__ */ t(
35
+ m,
36
+ {
37
+ className: "text-foreground-subtle size-4 shrink-0 transition-transform duration-[var(--duration-base)]",
38
+ "aria-hidden": !0
39
+ }
40
+ )
41
+ ]
42
+ }
43
+ ) });
44
+ });
45
+ l.displayName = "AccordionTrigger";
46
+ const u = a(function({ className: o, children: e, ...r }, c) {
47
+ return /* @__PURE__ */ t(
48
+ n.Content,
49
+ {
50
+ ref: c,
51
+ className: i(
52
+ "text-foreground-muted overflow-hidden text-sm",
53
+ // Deliberate motion exception: height animates (layout property). Radix only
54
+ // keeps Content mounted through CSS *animations*, so a grid-rows transition
55
+ // would cut the close. The panel is short and nothing below it reflows
56
+ // except by design; `prefers-reduced-motion` disables it globally.
57
+ "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
58
+ ),
59
+ ...r,
60
+ children: /* @__PURE__ */ t("div", { className: i("pt-0 pb-4", o), children: e })
61
+ }
62
+ );
63
+ });
64
+ u.displayName = "AccordionContent";
65
+ export {
66
+ v as Accordion,
67
+ u as AccordionContent,
68
+ f as AccordionItem,
69
+ l as AccordionTrigger
70
+ };
71
+ //# sourceMappingURL=Accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.js","sources":["../../../src/components/ui/Accordion.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react';\nimport * as AccordionPrimitive from '@radix-ui/react-accordion';\nimport { ChevronDown } from '@/icons';\nimport { cn } from '@/lib/utils';\n\n/**\n * Accessible disclosure list. Supports single (`type=\"single\"`) and multiple\n * (`type=\"multiple\"`) open behaviour — both come from Radix.\n *\n * @example FAQ\n * <Accordion type=\"single\" collapsible>\n * <AccordionItem value=\"q1\">\n * <AccordionTrigger>Can I cancel anytime?</AccordionTrigger>\n * <AccordionContent>Yes — usage stops at the end of the billing period.</AccordionContent>\n * </AccordionItem>\n * …\n * </Accordion>\n *\n * @do Use for FAQs, settings groups, and progressive-disclosure forms.\n * @dont Nest accordions inside accordions — the focus order becomes opaque.\n */\nexport const Accordion = AccordionPrimitive.Root;\n\nexport const AccordionItem = forwardRef<\n ElementRef<typeof AccordionPrimitive.Item>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(function AccordionItem({ className, ...props }, ref) {\n return (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn('border-border border-b', className)}\n {...props}\n />\n );\n});\nAccordionItem.displayName = 'AccordionItem';\n\nexport const AccordionTrigger = forwardRef<\n ElementRef<typeof AccordionPrimitive.Trigger>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(function AccordionTrigger({ className, children, ...props }, ref) {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n 'text-foreground flex flex-1 items-center justify-between gap-2 py-4 text-left text-sm font-medium',\n 'transition-colors duration-[var(--duration-fast)] outline-none',\n 'hover:text-accent',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n '[&[data-state=open]>svg]:rotate-180',\n className,\n )}\n {...props}\n >\n {children}\n <ChevronDown\n className=\"text-foreground-subtle size-4 shrink-0 transition-transform duration-[var(--duration-base)]\"\n aria-hidden\n />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n );\n});\nAccordionTrigger.displayName = 'AccordionTrigger';\n\nexport const AccordionContent = forwardRef<\n ElementRef<typeof AccordionPrimitive.Content>,\n ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(function AccordionContent({ className, children, ...props }, ref) {\n return (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n 'text-foreground-muted overflow-hidden text-sm',\n // Deliberate motion exception: height animates (layout property). Radix only\n // keeps Content mounted through CSS *animations*, so a grid-rows transition\n // would cut the close. The panel is short and nothing below it reflows\n // except by design; `prefers-reduced-motion` disables it globally.\n 'data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',\n )}\n {...props}\n >\n <div className={cn('pt-0 pb-4', className)}>{children}</div>\n </AccordionPrimitive.Content>\n );\n});\nAccordionContent.displayName = 'AccordionContent';\n"],"names":["Accordion","AccordionPrimitive","AccordionItem","forwardRef","className","props","ref","jsx","cn","AccordionTrigger","children","jsxs","ChevronDown","AccordionContent"],"mappings":";;;;;;AAuBO,MAAMA,IAAYC,EAAmB,MAE/BC,IAAgBC,EAG3B,SAAuB,EAAE,WAAAC,GAAW,GAAGC,EAAA,GAASC,GAAK;AACrD,SACE,gBAAAC;AAAA,IAACN,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE,EAAG,0BAA0BJ,CAAS;AAAA,MAChD,GAAGC;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AACDH,EAAc,cAAc;AAErB,MAAMO,IAAmBN,EAG9B,SAA0B,EAAE,WAAAC,GAAW,UAAAM,GAAU,GAAGL,EAAA,GAASC,GAAK;AAClE,SACE,gBAAAC,EAACN,EAAmB,QAAnB,EAA0B,WAAU,QACnC,UAAA,gBAAAU;AAAA,IAACV,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAJ;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEH,UAAA;AAAA,QAAAK;AAAA,QACD,gBAAAH;AAAA,UAACK;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA,UAAA;AAAA,QAAA;AAAA,MACb;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ,CAAC;AACDH,EAAiB,cAAc;AAExB,MAAMI,IAAmBV,EAG9B,SAA0B,EAAE,WAAAC,GAAW,UAAAM,GAAU,GAAGL,EAAA,GAASC,GAAK;AAClE,SACE,gBAAAC;AAAA,IAACN,EAAmB;AAAA,IAAnB;AAAA,MACC,KAAAK;AAAA,MACA,WAAWE;AAAA,QACT;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA;AAAA,MAAA;AAAA,MAED,GAAGH;AAAA,MAEJ,4BAAC,OAAA,EAAI,WAAWG,EAAG,aAAaJ,CAAS,GAAI,UAAAM,EAAA,CAAS;AAAA,IAAA;AAAA,EAAA;AAG5D,CAAC;AACDG,EAAiB,cAAc;"}
@@ -0,0 +1,51 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ import { VariantProps } from '../../lib/cva.js';
3
+ declare const alert: (props?: ({
4
+ variant?: "default" | "success" | "warning" | "danger" | "info" | null | undefined;
5
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
+ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>, VariantProps<typeof alert> {
7
+ /** Heading. Renders bold above the description. */
8
+ title?: ReactNode;
9
+ /** Show a dismiss (×) button. */
10
+ dismissible?: boolean;
11
+ /** Called when the user dismisses. */
12
+ onDismiss?: () => void;
13
+ /**
14
+ * Controlled visibility. Omit for the default uncontrolled behaviour
15
+ * (visible until dismissed).
16
+ */
17
+ open?: boolean;
18
+ /** Called with the next visibility when the user dismisses. */
19
+ onOpenChange?: (open: boolean) => void;
20
+ /** Override the variant's default icon. Pass `false` to suppress the icon. */
21
+ icon?: ReactNode | false;
22
+ /**
23
+ * Announce the alert to screen readers when it appears: `role="alert"` for
24
+ * `danger`, `role="status"` otherwise. Off by default — content that is on
25
+ * the page at load should not be announced as a live update.
26
+ * @default false
27
+ */
28
+ live?: boolean;
29
+ /** Heading level of `title`. Default 3 — fits under a page h1 / section h2. */
30
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
31
+ }
32
+ /**
33
+ * Inline banner for static page-level or section-level state. For transient
34
+ * notifications use `Toast` instead.
35
+ *
36
+ * @example Inline error
37
+ * <Alert variant="danger" title="Payment failed">
38
+ * Your card was declined. Update your billing info to retry.
39
+ * </Alert>
40
+ *
41
+ * @example Dismissible info
42
+ * <Alert variant="info" dismissible onDismiss={dismiss}>
43
+ * We're improving search — give us feedback at #search-feedback.
44
+ * </Alert>
45
+ *
46
+ * @do Use the closest semantic variant — `info` for neutral facts,
47
+ * `warning` for "watch out", `danger` for "this is broken".
48
+ * @dont Use Alert for one-time confirmations — that's Toast.
49
+ */
50
+ export declare const Alert: import('react').ForwardRefExoticComponent<AlertProps & import('react').RefAttributes<HTMLDivElement>>;
51
+ export {};
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsxs as l, jsx as e } from "react/jsx-runtime";
3
+ import { forwardRef as k, useState as w, createElement as z } from "react";
4
+ import { X as A, XCircle as j, AlertTriangle as y, CheckCircle2 as I, Info as S } from "../../icons/index.js";
5
+ import { cn as o } from "../../lib/utils.js";
6
+ import { useStrings as V } from "../../hooks/use-strings.js";
7
+ import { cva as X } from "class-variance-authority";
8
+ const E = X(["relative flex w-full gap-3 rounded-lg border p-4", "text-sm"], {
9
+ variants: {
10
+ variant: {
11
+ default: "border-border bg-background-subtle text-foreground",
12
+ info: "border-info-border-soft bg-info-soft text-info-text",
13
+ success: "border-success-border-soft bg-success-soft text-success-text",
14
+ warning: "border-warning-border-soft bg-warning-soft text-warning-text",
15
+ danger: "border-danger-border-soft bg-danger-soft text-danger-text"
16
+ }
17
+ },
18
+ defaultVariants: { variant: "default" }
19
+ }), F = {
20
+ default: null,
21
+ info: /* @__PURE__ */ e(S, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
22
+ success: /* @__PURE__ */ e(I, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
23
+ warning: /* @__PURE__ */ e(y, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
24
+ danger: /* @__PURE__ */ e(j, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": !0 })
25
+ }, R = k(function({
26
+ className: d,
27
+ variant: r = "default",
28
+ title: i,
29
+ dismissible: c,
30
+ onDismiss: t,
31
+ open: n,
32
+ onOpenChange: s,
33
+ icon: a,
34
+ live: f = !1,
35
+ headingLevel: u = 3,
36
+ children: m,
37
+ ...g
38
+ }, b) {
39
+ const x = V(), [h, v] = w(!0);
40
+ if (!(n ?? h)) return null;
41
+ const p = () => {
42
+ n === void 0 && v(!1), s == null || s(!1), t == null || t();
43
+ }, N = a === !1 ? null : a ?? F[r ?? "default"];
44
+ return /* @__PURE__ */ l(
45
+ "div",
46
+ {
47
+ ref: b,
48
+ role: f ? r === "danger" ? "alert" : "status" : void 0,
49
+ className: o(E({ variant: r }), d),
50
+ ...g,
51
+ children: [
52
+ N,
53
+ /* @__PURE__ */ l("div", { className: "min-w-0 flex-1", children: [
54
+ i && z(`h${u}`, { className: "mb-1 leading-tight font-medium" }, i),
55
+ /* @__PURE__ */ e("div", { className: "text-sm leading-relaxed [&_p]:leading-relaxed", children: m })
56
+ ] }),
57
+ c && /* @__PURE__ */ e(
58
+ "button",
59
+ {
60
+ type: "button",
61
+ "aria-label": x.alert.dismiss,
62
+ onClick: p,
63
+ className: o(
64
+ "inline-flex size-6 shrink-0 items-center justify-center rounded-md",
65
+ "outline-none hover:bg-current/10",
66
+ "focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2"
67
+ ),
68
+ children: /* @__PURE__ */ e(A, { className: "size-3.5", "aria-hidden": !0 })
69
+ }
70
+ )
71
+ ]
72
+ }
73
+ );
74
+ });
75
+ R.displayName = "Alert";
76
+ export {
77
+ R as Alert
78
+ };
79
+ //# sourceMappingURL=Alert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.js","sources":["../../../src/components/ui/Alert.tsx"],"sourcesContent":["'use client';\n\nimport { createElement, forwardRef, useState, type HTMLAttributes, type ReactNode } from 'react';\nimport { AlertTriangle, CheckCircle2, Info, X, XCircle } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst alert = cva(['relative flex w-full gap-3 rounded-lg border p-4', 'text-sm'], {\n variants: {\n variant: {\n default: 'border-border bg-background-subtle text-foreground',\n info: 'border-info-border-soft bg-info-soft text-info-text',\n success: 'border-success-border-soft bg-success-soft text-success-text',\n warning: 'border-warning-border-soft bg-warning-soft text-warning-text',\n danger: 'border-danger-border-soft bg-danger-soft text-danger-text',\n },\n },\n defaultVariants: { variant: 'default' },\n});\n\nconst iconForVariant = {\n default: null,\n info: <Info className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n success: <CheckCircle2 className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n warning: <AlertTriangle className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n danger: <XCircle className=\"mt-0.5 size-4 shrink-0\" aria-hidden />,\n};\n\nexport interface AlertProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>, VariantProps<typeof alert> {\n /** Heading. Renders bold above the description. */\n title?: ReactNode;\n /** Show a dismiss (×) button. */\n dismissible?: boolean;\n /** Called when the user dismisses. */\n onDismiss?: () => void;\n /**\n * Controlled visibility. Omit for the default uncontrolled behaviour\n * (visible until dismissed).\n */\n open?: boolean;\n /** Called with the next visibility when the user dismisses. */\n onOpenChange?: (open: boolean) => void;\n /** Override the variant's default icon. Pass `false` to suppress the icon. */\n icon?: ReactNode | false;\n /**\n * Announce the alert to screen readers when it appears: `role=\"alert\"` for\n * `danger`, `role=\"status\"` otherwise. Off by default — content that is on\n * the page at load should not be announced as a live update.\n * @default false\n */\n live?: boolean;\n /** Heading level of `title`. Default 3 — fits under a page h1 / section h2. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n}\n\n/**\n * Inline banner for static page-level or section-level state. For transient\n * notifications use `Toast` instead.\n *\n * @example Inline error\n * <Alert variant=\"danger\" title=\"Payment failed\">\n * Your card was declined. Update your billing info to retry.\n * </Alert>\n *\n * @example Dismissible info\n * <Alert variant=\"info\" dismissible onDismiss={dismiss}>\n * We're improving search — give us feedback at #search-feedback.\n * </Alert>\n *\n * @do Use the closest semantic variant — `info` for neutral facts,\n * `warning` for \"watch out\", `danger` for \"this is broken\".\n * @dont Use Alert for one-time confirmations — that's Toast.\n */\nexport const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(\n {\n className,\n variant = 'default',\n title,\n dismissible,\n onDismiss,\n open: openProp,\n onOpenChange,\n icon,\n live = false,\n headingLevel = 3,\n children,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const [internalOpen, setInternalOpen] = useState(true);\n const open = openProp ?? internalOpen;\n if (!open) return null;\n\n const handleDismiss = () => {\n if (openProp === undefined) setInternalOpen(false);\n onOpenChange?.(false);\n onDismiss?.();\n };\n\n const renderedIcon = icon === false ? null : (icon ?? iconForVariant[variant ?? 'default']);\n\n return (\n <div\n ref={ref}\n // Only errors interrupt the screen reader; everything else is polite.\n role={live ? (variant === 'danger' ? 'alert' : 'status') : undefined}\n className={cn(alert({ variant }), className)}\n {...props}\n >\n {renderedIcon}\n <div className=\"min-w-0 flex-1\">\n {title &&\n createElement(`h${headingLevel}`, { className: 'mb-1 leading-tight font-medium' }, title)}\n <div className=\"text-sm leading-relaxed [&_p]:leading-relaxed\">{children}</div>\n </div>\n {dismissible && (\n <button\n type=\"button\"\n aria-label={strings.alert.dismiss}\n onClick={handleDismiss}\n className={cn(\n 'inline-flex size-6 shrink-0 items-center justify-center rounded-md',\n 'outline-none hover:bg-current/10',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n )}\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </div>\n );\n});\nAlert.displayName = 'Alert';\n"],"names":["alert","cva","iconForVariant","jsx","Info","CheckCircle2","AlertTriangle","XCircle","Alert","forwardRef","className","variant","title","dismissible","onDismiss","openProp","onOpenChange","icon","live","headingLevel","children","props","ref","strings","useStrings","internalOpen","setInternalOpen","useState","handleDismiss","renderedIcon","jsxs","cn","createElement","X"],"mappings":";;;;;;;AAQA,MAAMA,IAAQC,EAAI,CAAC,oDAAoD,SAAS,GAAG;AAAA,EACjF,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,IAAA;AAAA,EACV;AAAA,EAEF,iBAAiB,EAAE,SAAS,UAAA;AAC9B,CAAC,GAEKC,IAAiB;AAAA,EACrB,SAAS;AAAA,EACT,MAAM,gBAAAC,EAACC,GAAA,EAAK,WAAU,0BAAyB,eAAW,IAAC;AAAA,EAC3D,SAAS,gBAAAD,EAACE,GAAA,EAAa,WAAU,0BAAyB,eAAW,IAAC;AAAA,EACtE,SAAS,gBAAAF,EAACG,GAAA,EAAc,WAAU,0BAAyB,eAAW,IAAC;AAAA,EACvE,QAAQ,gBAAAH,EAACI,GAAA,EAAQ,WAAU,0BAAyB,eAAW,GAAA,CAAC;AAClE,GAgDaC,IAAQC,EAAuC,SAC1D;AAAA,EACE,WAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,MAAMC;AAAA,EACN,cAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,cAAAC,IAAe;AAAA,EACf,UAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAUC,EAAA,GACV,CAACC,GAAcC,CAAe,IAAIC,EAAS,EAAI;AAErD,MAAI,EADSZ,KAAYU,GACd,QAAO;AAElB,QAAMG,IAAgB,MAAM;AAC1B,IAAIb,MAAa,UAAWW,EAAgB,EAAK,GACjDV,KAAA,QAAAA,EAAe,KACfF,KAAA,QAAAA;AAAA,EACF,GAEMe,IAAeZ,MAAS,KAAQ,OAAQA,KAAQf,EAAeS,KAAW,SAAS;AAEzF,SACE,gBAAAmB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAR;AAAA,MAEA,MAAMJ,IAAQP,MAAY,WAAW,UAAU,WAAY;AAAA,MAC3D,WAAWoB,EAAG/B,EAAM,EAAE,SAAAW,EAAA,CAAS,GAAGD,CAAS;AAAA,MAC1C,GAAGW;AAAA,MAEH,UAAA;AAAA,QAAAQ;AAAA,QACD,gBAAAC,EAAC,OAAA,EAAI,WAAU,kBACZ,UAAA;AAAA,UAAAlB,KACCoB,EAAc,IAAIb,CAAY,IAAI,EAAE,WAAW,iCAAA,GAAoCP,CAAK;AAAA,UAC1F,gBAAAT,EAAC,OAAA,EAAI,WAAU,iDAAiD,UAAAiB,EAAA,CAAS;AAAA,QAAA,GAC3E;AAAA,QACCP,KACC,gBAAAV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAYoB,EAAQ,MAAM;AAAA,YAC1B,SAASK;AAAA,YACT,WAAWG;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,YAGF,UAAA,gBAAA5B,EAAC8B,GAAA,EAAE,WAAU,YAAW,eAAW,GAAA,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MACtC;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AACDzB,EAAM,cAAc;"}
@@ -0,0 +1,52 @@
1
+ import { ComponentPropsWithoutRef, HTMLAttributes, ReactNode } from 'react';
2
+ import { VariantProps } from '../../lib/cva.js';
3
+ import * as AvatarPrimitive from '@radix-ui/react-avatar';
4
+ declare const sizeMap: {
5
+ readonly xs: "size-5 text-xs";
6
+ readonly sm: "size-6 text-xs";
7
+ readonly md: "size-8 text-xs";
8
+ readonly lg: "size-10 text-sm";
9
+ readonly xl: "size-12 text-base";
10
+ };
11
+ declare const avatarWrapper: (props?: ({
12
+ size?: "md" | "xs" | "sm" | "lg" | "xl" | null | undefined;
13
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
14
+ export interface AvatarProps extends ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>, VariantProps<typeof avatarWrapper> {
15
+ /** Image URL. If absent or fails to load, fallback initials are shown. */
16
+ src?: string;
17
+ /** Alt text for the image; falls back to fallback when there's no `src`. */
18
+ alt?: string;
19
+ /** Initials shown when image is missing or loading. Pass at most 2 characters. */
20
+ fallback?: string;
21
+ /** Status dot rendered on the bottom-right. */
22
+ status?: 'online' | 'busy' | 'away' | 'offline';
23
+ }
24
+ /**
25
+ * Avatar with image, initials fallback, and optional status dot.
26
+ *
27
+ * @example
28
+ * <Avatar src={user.photo} alt={user.name} fallback={getInitials(user.name)} status="online" />
29
+ *
30
+ * @example AvatarGroup
31
+ * <AvatarGroup max={3}>
32
+ * {users.map(u => <Avatar key={u.id} src={u.photo} fallback={getInitials(u.name)} />)}
33
+ * </AvatarGroup>
34
+ *
35
+ * @do Provide `alt` for image avatars and meaningful initials for the
36
+ * fallback so screen readers announce something useful.
37
+ * @dont Use the status dot without a tooltip explaining what it means.
38
+ */
39
+ export declare const Avatar: import('react').ForwardRefExoticComponent<AvatarProps & import('react').RefAttributes<HTMLSpanElement>>;
40
+ export interface AvatarGroupProps extends HTMLAttributes<HTMLDivElement> {
41
+ /** Maximum visible avatars before showing a "+N" overflow chip. */
42
+ max?: number;
43
+ /** Avatar size applied to children (unless a child sets its own) + overflow. */
44
+ size?: keyof typeof sizeMap;
45
+ /** Children — Avatars. */
46
+ children: ReactNode;
47
+ }
48
+ /**
49
+ * Overlapping avatars with overflow indicator. Pass plain `<Avatar>` children.
50
+ */
51
+ export declare const AvatarGroup: import('react').ForwardRefExoticComponent<AvatarGroupProps & import('react').RefAttributes<HTMLDivElement>>;
52
+ export {};