@krizaka/ui 2.2.0 → 2.3.0

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/README.md +17 -7
  2. package/dist/{button-Cgfkg_3y.d.ts → button-BMAO7dKF.d.ts} +29 -2
  3. package/dist/button.d.ts +32 -5
  4. package/dist/button.js +62 -9
  5. package/dist/button.js.map +1 -1
  6. package/dist/chip-link.d.ts +53 -0
  7. package/dist/chip-link.js +32 -0
  8. package/dist/chip-link.js.map +1 -0
  9. package/dist/chunk-43PTCDTL.js +31 -0
  10. package/dist/chunk-43PTCDTL.js.map +1 -0
  11. package/dist/chunk-6ISW63JI.js +11 -0
  12. package/dist/chunk-6ISW63JI.js.map +1 -0
  13. package/dist/chunk-HAQWKORZ.js +117 -0
  14. package/dist/chunk-HAQWKORZ.js.map +1 -0
  15. package/dist/{chunk-FHYZSO2S.js → chunk-R6SDS7JM.js} +5 -3
  16. package/dist/chunk-R6SDS7JM.js.map +1 -0
  17. package/dist/chunk-RUPNPMP3.js +103 -0
  18. package/dist/chunk-RUPNPMP3.js.map +1 -0
  19. package/dist/chunk-VDR2EP6K.js +49 -0
  20. package/dist/chunk-VDR2EP6K.js.map +1 -0
  21. package/dist/chunk-WEVYKZIF.js +33 -0
  22. package/dist/chunk-WEVYKZIF.js.map +1 -0
  23. package/dist/command.d.ts +7 -7
  24. package/dist/command.js +3 -2
  25. package/dist/command.js.map +1 -1
  26. package/dist/confirm-button.d.ts +1 -1
  27. package/dist/confirm-button.js +2 -1
  28. package/dist/confirm-button.js.map +1 -1
  29. package/dist/{dialog-GQ4Nvuyf.d.ts → dialog-D42QwYb_.d.ts} +11 -1
  30. package/dist/dialog.d.ts +1 -1
  31. package/dist/dialog.js +3 -2
  32. package/dist/dialog.js.map +1 -1
  33. package/dist/glow-card.d.ts +20 -0
  34. package/dist/glow-card.js +30 -0
  35. package/dist/glow-card.js.map +1 -0
  36. package/dist/index.d.ts +2 -20
  37. package/dist/index.js +7 -64
  38. package/dist/index.js.map +1 -1
  39. package/dist/iso.d.ts +400 -0
  40. package/dist/iso.js +363 -0
  41. package/dist/iso.js.map +1 -0
  42. package/dist/loader.d.ts +206 -0
  43. package/dist/loader.js +138 -0
  44. package/dist/loader.js.map +1 -0
  45. package/dist/marquee.d.ts +33 -0
  46. package/dist/marquee.js +52 -0
  47. package/dist/marquee.js.map +1 -0
  48. package/dist/meter.d.ts +146 -0
  49. package/dist/meter.js +107 -0
  50. package/dist/meter.js.map +1 -0
  51. package/dist/motion.css +394 -3
  52. package/dist/motion.d.ts +46 -0
  53. package/dist/motion.js +10 -0
  54. package/dist/motion.js.map +1 -0
  55. package/dist/nav-bar.d.ts +178 -0
  56. package/dist/nav-bar.js +157 -0
  57. package/dist/nav-bar.js.map +1 -0
  58. package/dist/number-ticker.d.ts +32 -0
  59. package/dist/number-ticker.js +61 -0
  60. package/dist/number-ticker.js.map +1 -0
  61. package/dist/progress.d.ts +56 -1
  62. package/dist/progress.js +72 -25
  63. package/dist/progress.js.map +1 -1
  64. package/dist/skeleton.d.ts +18 -3
  65. package/dist/skeleton.js +9 -1
  66. package/dist/skeleton.js.map +1 -1
  67. package/dist/spinner.d.ts +21 -5
  68. package/dist/spinner.js +13 -12
  69. package/dist/spinner.js.map +1 -1
  70. package/dist/stepper.d.ts +112 -0
  71. package/dist/stepper.js +67 -0
  72. package/dist/stepper.js.map +1 -0
  73. package/dist/tabs.d.ts +1 -1
  74. package/dist/tabs.js +14 -8
  75. package/dist/tabs.js.map +1 -1
  76. package/dist/theme.d.ts +1 -1
  77. package/dist/theme.js +2 -1
  78. package/dist/theme.js.map +1 -1
  79. package/dist/toast.d.ts +39 -4
  80. package/dist/toast.js +81 -7
  81. package/dist/toast.js.map +1 -1
  82. package/package.json +148 -8
  83. package/registry/alert.json +1 -1
  84. package/registry/avatar.json +1 -1
  85. package/registry/badge.json +1 -1
  86. package/registry/button.json +232 -26
  87. package/registry/card.json +1 -1
  88. package/registry/checkbox.json +1 -1
  89. package/registry/chip-link.json +158 -0
  90. package/registry/chip.json +1 -1
  91. package/registry/command.json +1 -1
  92. package/registry/confirm-button.json +91 -7
  93. package/registry/countdown.json +1 -1
  94. package/registry/dialog.json +15 -7
  95. package/registry/dropdown-menu.json +1 -1
  96. package/registry/empty-state.json +1 -1
  97. package/registry/examples/button/error.js +11 -0
  98. package/registry/examples/button/error.tsx +10 -0
  99. package/registry/examples/button/loading.tsx +1 -1
  100. package/registry/examples/button/save-flow.d.ts +4 -0
  101. package/registry/examples/button/save-flow.js +28 -0
  102. package/registry/examples/button/save-flow.tsx +24 -0
  103. package/registry/examples/button/signature.d.ts +4 -0
  104. package/registry/examples/button/signature.js +17 -0
  105. package/registry/examples/button/signature.tsx +19 -0
  106. package/registry/examples/button/upload-progress.d.ts +4 -0
  107. package/registry/examples/button/upload-progress.js +23 -0
  108. package/registry/examples/button/upload-progress.tsx +26 -0
  109. package/registry/examples/chip-link/filters.d.ts +4 -0
  110. package/registry/examples/chip-link/filters.js +12 -0
  111. package/registry/examples/chip-link/filters.tsx +17 -0
  112. package/registry/examples/dialog/left.d.ts +4 -0
  113. package/registry/examples/dialog/left.js +22 -0
  114. package/registry/examples/dialog/left.tsx +32 -0
  115. package/registry/examples/glow-card/link-card.d.ts +4 -0
  116. package/registry/examples/glow-card/link-card.js +14 -0
  117. package/registry/examples/glow-card/link-card.tsx +13 -0
  118. package/registry/examples/glow-card/plans.d.ts +4 -0
  119. package/registry/examples/glow-card/plans.js +25 -0
  120. package/registry/examples/glow-card/plans.tsx +26 -0
  121. package/registry/examples/glow-card/spotlight.d.ts +4 -0
  122. package/registry/examples/glow-card/spotlight.js +14 -0
  123. package/registry/examples/glow-card/spotlight.tsx +15 -0
  124. package/registry/examples/iso/compose.d.ts +4 -0
  125. package/registry/examples/iso/compose.js +22 -0
  126. package/registry/examples/iso/compose.tsx +19 -0
  127. package/registry/examples/iso/orazaka-hero.d.ts +4 -0
  128. package/registry/examples/iso/orazaka-hero.js +26 -0
  129. package/registry/examples/iso/orazaka-hero.tsx +21 -0
  130. package/registry/examples/iso/orochia-hero.d.ts +4 -0
  131. package/registry/examples/iso/orochia-hero.js +29 -0
  132. package/registry/examples/iso/orochia-hero.tsx +23 -0
  133. package/registry/examples/iso/still.d.ts +4 -0
  134. package/registry/examples/iso/still.js +21 -0
  135. package/registry/examples/iso/still.tsx +15 -0
  136. package/registry/examples/loader/endings.d.ts +4 -0
  137. package/registry/examples/loader/endings.js +14 -0
  138. package/registry/examples/loader/endings.tsx +11 -0
  139. package/registry/examples/loader/fetch-flow.d.ts +4 -0
  140. package/registry/examples/loader/fetch-flow.js +24 -0
  141. package/registry/examples/loader/fetch-flow.tsx +24 -0
  142. package/registry/examples/loader/minimal.d.ts +4 -0
  143. package/registry/examples/loader/minimal.js +11 -0
  144. package/registry/examples/loader/minimal.tsx +6 -0
  145. package/registry/examples/loader/overlay.d.ts +4 -0
  146. package/registry/examples/loader/overlay.js +16 -0
  147. package/registry/examples/loader/overlay.tsx +13 -0
  148. package/registry/examples/loader/region-handover.d.ts +4 -0
  149. package/registry/examples/loader/region-handover.js +42 -0
  150. package/registry/examples/loader/region-handover.tsx +29 -0
  151. package/registry/examples/loader/upload-eta.d.ts +4 -0
  152. package/registry/examples/loader/upload-eta.js +11 -0
  153. package/registry/examples/loader/upload-eta.tsx +10 -0
  154. package/registry/examples/marquee/cards.d.ts +4 -0
  155. package/registry/examples/marquee/cards.js +23 -0
  156. package/registry/examples/marquee/cards.tsx +21 -0
  157. package/registry/examples/marquee/logos.d.ts +4 -0
  158. package/registry/examples/marquee/logos.js +12 -0
  159. package/registry/examples/marquee/logos.tsx +17 -0
  160. package/registry/examples/meter/balance.d.ts +4 -0
  161. package/registry/examples/meter/balance.js +14 -0
  162. package/registry/examples/meter/balance.tsx +15 -0
  163. package/registry/examples/meter/quota.d.ts +4 -0
  164. package/registry/examples/meter/quota.js +15 -0
  165. package/registry/examples/meter/quota.tsx +12 -0
  166. package/registry/examples/meter/split.d.ts +4 -0
  167. package/registry/examples/meter/split.js +18 -0
  168. package/registry/examples/meter/split.tsx +18 -0
  169. package/registry/examples/nav-bar/app-bar.d.ts +4 -0
  170. package/registry/examples/nav-bar/app-bar.js +22 -0
  171. package/registry/examples/nav-bar/app-bar.tsx +21 -0
  172. package/registry/examples/nav-bar/menu-open.d.ts +4 -0
  173. package/registry/examples/nav-bar/menu-open.js +26 -0
  174. package/registry/examples/nav-bar/menu-open.tsx +26 -0
  175. package/registry/examples/nav-bar/site.d.ts +4 -0
  176. package/registry/examples/nav-bar/site.js +31 -0
  177. package/registry/examples/nav-bar/site.tsx +36 -0
  178. package/registry/examples/number-ticker/gradient.d.ts +4 -0
  179. package/registry/examples/number-ticker/gradient.js +11 -0
  180. package/registry/examples/number-ticker/gradient.tsx +10 -0
  181. package/registry/examples/number-ticker/live-total.d.ts +4 -0
  182. package/registry/examples/number-ticker/live-total.js +17 -0
  183. package/registry/examples/number-ticker/live-total.tsx +21 -0
  184. package/registry/examples/number-ticker/stats.d.ts +4 -0
  185. package/registry/examples/number-ticker/stats.js +19 -0
  186. package/registry/examples/number-ticker/stats.tsx +23 -0
  187. package/registry/examples/progress/buffer.d.ts +4 -0
  188. package/registry/examples/progress/buffer.js +11 -0
  189. package/registry/examples/progress/buffer.tsx +10 -0
  190. package/registry/examples/progress/statuses.d.ts +4 -0
  191. package/registry/examples/progress/statuses.js +14 -0
  192. package/registry/examples/progress/statuses.tsx +11 -0
  193. package/registry/examples/progress/steps.d.ts +4 -0
  194. package/registry/examples/progress/steps.js +14 -0
  195. package/registry/examples/progress/steps.tsx +13 -0
  196. package/registry/examples/progress/upload.d.ts +4 -0
  197. package/registry/examples/progress/upload.js +11 -0
  198. package/registry/examples/progress/upload.tsx +10 -0
  199. package/registry/examples/skeleton/paragraph.d.ts +4 -0
  200. package/registry/examples/skeleton/paragraph.js +14 -0
  201. package/registry/examples/skeleton/paragraph.tsx +12 -0
  202. package/registry/examples/skeleton/shape-of-content.d.ts +4 -0
  203. package/registry/examples/skeleton/shape-of-content.js +23 -0
  204. package/registry/examples/skeleton/shape-of-content.tsx +26 -0
  205. package/registry/examples/spinner/default.js +5 -2
  206. package/registry/examples/spinner/default.tsx +7 -1
  207. package/registry/examples/spinner/sizes.js +2 -1
  208. package/registry/examples/spinner/sizes.tsx +2 -0
  209. package/registry/examples/spinner/value.d.ts +4 -0
  210. package/registry/examples/spinner/value.js +11 -0
  211. package/registry/examples/spinner/value.tsx +12 -0
  212. package/registry/examples/stepper/checkout.d.ts +4 -0
  213. package/registry/examples/stepper/checkout.js +22 -0
  214. package/registry/examples/stepper/checkout.tsx +17 -0
  215. package/registry/examples/stepper/failed.d.ts +4 -0
  216. package/registry/examples/stepper/failed.js +26 -0
  217. package/registry/examples/stepper/failed.tsx +21 -0
  218. package/registry/examples/stepper/job.d.ts +4 -0
  219. package/registry/examples/stepper/job.js +32 -0
  220. package/registry/examples/stepper/job.tsx +28 -0
  221. package/registry/examples/toast/grouped.d.ts +4 -0
  222. package/registry/examples/toast/grouped.js +21 -0
  223. package/registry/examples/toast/grouped.tsx +23 -0
  224. package/registry/examples/toast/undo.d.ts +4 -0
  225. package/registry/examples/toast/undo.js +29 -0
  226. package/registry/examples/toast/undo.tsx +30 -0
  227. package/registry/examples/toast/upload-progress.d.ts +4 -0
  228. package/registry/examples/toast/upload-progress.js +29 -0
  229. package/registry/examples/toast/upload-progress.tsx +31 -0
  230. package/registry/field.json +1 -1
  231. package/registry/glow-card.json +162 -0
  232. package/registry/index.json +191 -20
  233. package/registry/iso.json +1355 -0
  234. package/registry/kbd.json +1 -1
  235. package/registry/loader.json +399 -0
  236. package/registry/marquee.json +225 -0
  237. package/registry/mascots.json +3 -3
  238. package/registry/meter.json +324 -0
  239. package/registry/nav-bar.json +480 -0
  240. package/registry/number-ticker.json +219 -0
  241. package/registry/page-header.json +1 -1
  242. package/registry/popover.json +1 -1
  243. package/registry/progress.json +116 -11
  244. package/registry/radio-group.json +1 -1
  245. package/registry/section-backdrop.json +1 -1
  246. package/registry/separator.json +1 -1
  247. package/registry/skeleton.json +69 -13
  248. package/registry/slider.json +1 -1
  249. package/registry/slot.json +1 -1
  250. package/registry/spinner.json +91 -27
  251. package/registry/stat.json +1 -1
  252. package/registry/stepper.json +174 -0
  253. package/registry/switch.json +1 -1
  254. package/registry/tabs.json +11 -7
  255. package/registry/theme.json +3 -3
  256. package/registry/toast.json +41 -13
  257. package/registry/tooltip.json +1 -1
  258. package/dist/chunk-FHYZSO2S.js.map +0 -1
  259. package/dist/chunk-PESA2R4U.js +0 -51
  260. package/dist/chunk-PESA2R4U.js.map +0 -1
  261. package/registry/examples/spinner/muted.js +0 -11
  262. package/registry/examples/spinner/muted.tsx +0 -6
  263. /package/registry/examples/{spinner/muted.d.ts → button/error.d.ts} +0 -0
@@ -0,0 +1,480 @@
1
+ {
2
+ "name": "nav-bar",
3
+ "type": "primitive",
4
+ "title": "Nav bar",
5
+ "summary": "The Krizaka navigation bar: a floating pill that tightens as the page scrolls, one light that glides to the item under the pointer or the focus and rests on the current page, accessible menus with rich links, and a mobile panel.",
6
+ "description": "The Krizaka navigation bar: a floating pill that tightens as the page scrolls, one light that glides to the item under the pointer or the focus and rests on the current page, accessible menus with rich links, and a mobile panel.",
7
+ "status": "beta",
8
+ "category": "navigation",
9
+ "platforms": "web",
10
+ "whenToUse": [
11
+ "The top navigation of a site or a product: a brand, a few links, one or two menus, actions on the right.",
12
+ "A section bar inside an app (`floating={false}`): videos, earnings, auctions."
13
+ ],
14
+ "whenNotToUse": [
15
+ {
16
+ "when": "To switch views inside a page without changing the address.",
17
+ "use": "tabs"
18
+ },
19
+ {
20
+ "when": "For a list of actions on one object.",
21
+ "use": "dropdown-menu"
22
+ },
23
+ {
24
+ "when": "For search and commands across the app.",
25
+ "use": "command"
26
+ }
27
+ ],
28
+ "bestPractices": [
29
+ "Five items at most on the bar; group the rest in a menu, each link with a name and one line (`NavBar.MenuLink`).",
30
+ "Mark the current page with `active`: the light rests on it, `aria-current=\"page\"` says it.",
31
+ "Use your router's link with `asChild` on `NavBar.Link`, `NavBar.MenuLink` and `NavBar.MobileLink`.",
32
+ "One primary action at most in `NavBar.Actions` (a `Button` `size=\"sm\"` `shape=\"pill\"`).",
33
+ "Wrap it in a `sticky top-3` (floating) or `sticky top-0` container to keep it on screen."
34
+ ],
35
+ "accessibility": {
36
+ "keyboard": [
37
+ {
38
+ "keys": "Tab",
39
+ "action": "Moves between the brand, the items and the actions; leaving an open menu closes it."
40
+ },
41
+ {
42
+ "keys": "Enter / Space / ↓",
43
+ "action": "Opens a menu and moves into it."
44
+ },
45
+ {
46
+ "keys": "↑ / ↓ · Home / End",
47
+ "action": "Walks the links of an open menu."
48
+ },
49
+ {
50
+ "keys": "← / →",
51
+ "action": "Moves between the items of the bar."
52
+ },
53
+ {
54
+ "keys": "Esc",
55
+ "action": "Closes the menu or the mobile panel and returns the focus to its trigger."
56
+ }
57
+ ],
58
+ "notes": [
59
+ "A `nav` named by `label`, a list of links and menu triggers (Radix NavigationMenu): `aria-expanded` and `aria-controls` on each trigger.",
60
+ "The mobile panel is a disclosure (no focus trap): its toggle is named by `openLabel` / `closeLabel` and says `aria-expanded`.",
61
+ "The light, the chevron and the tightening do not move under `prefers-reduced-motion`; the current page is tinted before scripts load."
62
+ ]
63
+ },
64
+ "related": [
65
+ "tabs",
66
+ "dropdown-menu",
67
+ "command",
68
+ "button"
69
+ ],
70
+ "web": {
71
+ "entry": "@krizaka/ui/nav-bar",
72
+ "import": "import { NavBar } from \"@krizaka/ui/nav-bar\";",
73
+ "props": [
74
+ {
75
+ "component": "NavBarRoot",
76
+ "description": "The bar. Place it in a `sticky top-3` (floating) or `sticky top-0` wrapper — or let it scroll.",
77
+ "props": [
78
+ {
79
+ "name": "label",
80
+ "type": "string",
81
+ "default": null,
82
+ "description": "The accessible name of the navigation (“Main”) — passed translated.",
83
+ "required": true
84
+ },
85
+ {
86
+ "name": "compactAfter",
87
+ "type": "number | false",
88
+ "default": "24",
89
+ "description": "Pixels of scroll after which the bar tightens (denser, lower, lifted). `false`: never.",
90
+ "required": false
91
+ },
92
+ {
93
+ "name": "floating",
94
+ "type": "boolean",
95
+ "default": "true",
96
+ "description": "A floating pill (the Krizaka bar) or a full-width bar with a hairline under it.",
97
+ "required": false
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "component": "NavBarBrand",
103
+ "description": "The brand: a mark and a name, linking home.",
104
+ "props": [
105
+ {
106
+ "name": "asChild",
107
+ "type": "boolean",
108
+ "default": null,
109
+ "description": "Renders your link (a router `<Link>`) with the brand's look.",
110
+ "required": false
111
+ }
112
+ ]
113
+ },
114
+ {
115
+ "component": "NavBarItems",
116
+ "description": "The desktop navigation: links and menus, with the light that glides between them. Hidden below `md` (see `NavBar.Mobile`).",
117
+ "props": []
118
+ },
119
+ {
120
+ "component": "NavBarLink",
121
+ "description": "A link of the bar. `asChild` renders your router's `<Link>`.",
122
+ "props": [
123
+ {
124
+ "name": "active",
125
+ "type": "boolean",
126
+ "default": null,
127
+ "description": "The current page: `aria-current=\"page\"`, and the light rests on it.",
128
+ "required": false
129
+ }
130
+ ]
131
+ },
132
+ {
133
+ "component": "NavBarMenu",
134
+ "description": "A menu of the bar: its trigger, and its links in a panel that opens under the bar.",
135
+ "props": [
136
+ {
137
+ "name": "children",
138
+ "type": "ReactNode",
139
+ "default": null,
140
+ "description": "The menu's links (`NavBar.MenuLink`), laid out in a panel; `columns` sets the grid.",
141
+ "required": true
142
+ },
143
+ {
144
+ "name": "label",
145
+ "type": "string",
146
+ "default": null,
147
+ "description": "The trigger's words (“Products”) — passed translated.",
148
+ "required": true
149
+ },
150
+ {
151
+ "name": "active",
152
+ "type": "boolean",
153
+ "default": null,
154
+ "description": "The current page is inside this menu: the light rests on its trigger.",
155
+ "required": false
156
+ },
157
+ {
158
+ "name": "columns",
159
+ "type": "1 | 2",
160
+ "default": "1",
161
+ "description": "Columns of the panel.",
162
+ "required": false
163
+ }
164
+ ]
165
+ },
166
+ {
167
+ "component": "NavBarMenuLink",
168
+ "description": "A link inside a menu: an icon, a name and a line of text.",
169
+ "props": [
170
+ {
171
+ "name": "title",
172
+ "type": "string",
173
+ "default": null,
174
+ "description": "The link's name — passed translated.",
175
+ "required": true
176
+ },
177
+ {
178
+ "name": "active",
179
+ "type": "boolean",
180
+ "default": null,
181
+ "description": "The current page.",
182
+ "required": false
183
+ },
184
+ {
185
+ "name": "description",
186
+ "type": "string",
187
+ "default": null,
188
+ "description": "One line under it — passed translated.",
189
+ "required": false
190
+ },
191
+ {
192
+ "name": "icon",
193
+ "type": "ReactNode",
194
+ "default": null,
195
+ "description": "An icon (a signature icon of `@krizaka/icons`: its node lights up on hover).",
196
+ "required": false
197
+ }
198
+ ]
199
+ },
200
+ {
201
+ "component": "NavBarActions",
202
+ "description": "The right side: search, theme, language, the call to action.",
203
+ "props": []
204
+ },
205
+ {
206
+ "component": "NavBarMobile",
207
+ "description": "Below `md`: a toggle, and a panel under the bar with its links (`NavBar.MobileLink`). A disclosure: Escape and a click\non a link close it, the focus returns to the toggle.",
208
+ "props": [
209
+ {
210
+ "name": "closeLabel",
211
+ "type": "string",
212
+ "default": null,
213
+ "description": "The toggle's name when open (“Close the menu”) — passed translated.",
214
+ "required": true
215
+ },
216
+ {
217
+ "name": "openLabel",
218
+ "type": "string",
219
+ "default": null,
220
+ "description": "The toggle's name when closed (“Open the menu”) — passed translated.",
221
+ "required": true
222
+ }
223
+ ]
224
+ },
225
+ {
226
+ "component": "NavBarMobileLink",
227
+ "description": "A link of the mobile panel.",
228
+ "props": [
229
+ {
230
+ "name": "active",
231
+ "type": "boolean",
232
+ "default": null,
233
+ "description": "The current page.",
234
+ "required": false
235
+ },
236
+ {
237
+ "name": "asChild",
238
+ "type": "boolean",
239
+ "default": null,
240
+ "description": "Renders your router's `<Link>`.",
241
+ "required": false
242
+ }
243
+ ]
244
+ }
245
+ ],
246
+ "examples": [
247
+ {
248
+ "name": "site",
249
+ "title": "A site's bar",
250
+ "description": "Brand, a Products menu, links, a call to action, a mobile panel — hover the items: one light glides between them.",
251
+ "path": "examples/nav-bar/site.tsx",
252
+ "module": "@krizaka/ui/registry/examples/nav-bar/site",
253
+ "code": "import { Button } from \"@krizaka/ui/button\";\nimport { NavBar } from \"@krizaka/ui/nav-bar\";\n\nconst products = [\n { href: \"#orazaka\", title: \"Orazaka\", description: \"Sovereign AI, on your own servers.\" },\n { href: \"#orochia\", title: \"Orochia\", description: \"Creators keep 90 % of every support.\" },\n { href: \"#ui\", title: \"Krizaka UI\", description: \"The components behind both.\" },\n];\n\n// The site's bar: a floating pill that tightens on scroll, one light gliding between its items and resting on the\n// current page, an accessible menu (Enter or ↓ opens, Escape closes), and a panel below `md`.\nexport default function NavBarSite() {\n return (\n <NavBar.Root label=\"Main\">\n <NavBar.Brand href=\"#home\">Krizaka</NavBar.Brand>\n <NavBar.Items>\n <NavBar.Menu label=\"Products\">\n {products.map((p) => (\n <NavBar.MenuLink key={p.href} {...p} />\n ))}\n </NavBar.Menu>\n <NavBar.Link href=\"#docs\">Docs</NavBar.Link>\n <NavBar.Link href=\"#story\" active>Our story</NavBar.Link>\n </NavBar.Items>\n <NavBar.Actions>\n <Button variant=\"primary\" size=\"sm\" shape=\"pill\" className=\"hidden sm:inline-flex\">Contact</Button>\n <NavBar.Mobile openLabel=\"Open the menu\" closeLabel=\"Close the menu\">\n {products.map((p) => (\n <NavBar.MobileLink key={p.href} href={p.href}>{p.title}</NavBar.MobileLink>\n ))}\n <NavBar.MobileLink href=\"#story\" active>Our story</NavBar.MobileLink>\n </NavBar.Mobile>\n </NavBar.Actions>\n </NavBar.Root>\n );\n}\n"
254
+ },
255
+ {
256
+ "name": "menu-open",
257
+ "title": "A rich menu",
258
+ "description": "Two columns of links with icons whose node lights up, the current page tinted.",
259
+ "path": "examples/nav-bar/menu-open.tsx",
260
+ "module": "@krizaka/ui/registry/examples/nav-bar/menu-open",
261
+ "code": "import { NavBar } from \"@krizaka/ui/nav-bar\";\n\nconst icon = (\n <svg data-kz-icon viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden>\n <path d=\"M12 3 19.8 7.5v9L12 21l-7.8-4.5v-9Z\" />\n <circle data-node cx=\"12\" cy=\"12\" r=\"2.4\" fill=\"currentColor\" />\n </svg>\n);\n\n// A menu in two columns, open: icons whose node lights up under the pointer, the current page tinted.\nexport default function NavBarMenuOpen({ defaultOpen = false }: { defaultOpen?: boolean }) {\n return (\n <NavBar.Root label=\"Main\" className=\"mb-72\">\n <NavBar.Brand href=\"#home\">Krizaka</NavBar.Brand>\n <NavBar.Items defaultValue={defaultOpen ? \"products\" : undefined}>\n <NavBar.Menu value=\"products\" label=\"Products\" columns={2} active>\n <NavBar.MenuLink href=\"#overview\" icon={icon} title=\"Overview\" description=\"What it does, in one page.\" active />\n <NavBar.MenuLink href=\"#how\" icon={icon} title=\"How it works\" description=\"The request journey, step by step.\" />\n <NavBar.MenuLink href=\"#demo\" icon={icon} title=\"Demo\" description=\"Try it on the real application.\" />\n <NavBar.MenuLink href=\"#signature\" icon={icon} title=\"Signature\" description=\"What only it does.\" />\n </NavBar.Menu>\n <NavBar.Link href=\"#docs\">Docs</NavBar.Link>\n </NavBar.Items>\n </NavBar.Root>\n );\n}\n"
262
+ },
263
+ {
264
+ "name": "app-bar",
265
+ "title": "Inside an app",
266
+ "description": "`floating={false}`: a full-width bar with a hairline, for an app's sections.",
267
+ "path": "examples/nav-bar/app-bar.tsx",
268
+ "module": "@krizaka/ui/registry/examples/nav-bar/app-bar",
269
+ "code": "import { NavBar } from \"@krizaka/ui/nav-bar\";\n\n// Inside an app: `floating={false}` — a full-width bar with a hairline, the same light between sections.\nexport default function NavBarAppBar() {\n return (\n <NavBar.Root label=\"Studio\" floating={false} compactAfter={false}>\n <NavBar.Brand href=\"#studio\">Studio</NavBar.Brand>\n <NavBar.Items>\n <NavBar.Link href=\"#videos\" active>Videos</NavBar.Link>\n <NavBar.Link href=\"#earnings\">Earnings</NavBar.Link>\n <NavBar.Link href=\"#auctions\">Auctions</NavBar.Link>\n </NavBar.Items>\n <NavBar.Actions>\n <NavBar.Mobile openLabel=\"Open the menu\" closeLabel=\"Close the menu\">\n <NavBar.MobileLink href=\"#videos\" active>Videos</NavBar.MobileLink>\n <NavBar.MobileLink href=\"#earnings\">Earnings</NavBar.MobileLink>\n </NavBar.Mobile>\n </NavBar.Actions>\n </NavBar.Root>\n );\n}\n"
270
+ }
271
+ ]
272
+ },
273
+ "native": null,
274
+ "files": [
275
+ {
276
+ "path": "meta.ts",
277
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
278
+ },
279
+ {
280
+ "path": "nav-bar/index.ts",
281
+ "content": "/** @krizaka/ui/nav-bar — `NavBar` (Root, Brand, Items, Link, Menu, MenuLink, Actions, Mobile, MobileLink), `navBar`. Client. */\nexport {\n NavBar,\n navBar,\n NavBarActions,\n NavBarBrand,\n type NavBarBrandProps,\n NavBarItems,\n NavBarLink,\n type NavBarLinkProps,\n NavBarMenu,\n NavBarMenuLink,\n type NavBarMenuLinkProps,\n type NavBarMenuProps,\n NavBarMobile,\n NavBarMobileLink,\n type NavBarMobileLinkProps,\n type NavBarMobileProps,\n NavBarRoot,\n type NavBarRootProps,\n} from \"./nav-bar\";\n"
282
+ },
283
+ {
284
+ "path": "nav-bar/meta.ts",
285
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Nav bar\",\n summary:\n \"The Krizaka navigation bar: a floating pill that tightens as the page scrolls, one light that glides to the item under the pointer or the focus and rests on the current page, accessible menus with rich links, and a mobile panel.\",\n status: \"beta\",\n category: \"navigation\",\n platforms: \"web\",\n whenToUse: [\n \"The top navigation of a site or a product: a brand, a few links, one or two menus, actions on the right.\",\n \"A section bar inside an app (`floating={false}`): videos, earnings, auctions.\",\n ],\n whenNotToUse: [\n { when: \"To switch views inside a page without changing the address.\", use: \"tabs\" },\n { when: \"For a list of actions on one object.\", use: \"dropdown-menu\" },\n { when: \"For search and commands across the app.\", use: \"command\" },\n ],\n bestPractices: [\n \"Five items at most on the bar; group the rest in a menu, each link with a name and one line (`NavBar.MenuLink`).\",\n \"Mark the current page with `active`: the light rests on it, `aria-current=\\\"page\\\"` says it.\",\n \"Use your router's link with `asChild` on `NavBar.Link`, `NavBar.MenuLink` and `NavBar.MobileLink`.\",\n \"One primary action at most in `NavBar.Actions` (a `Button` `size=\\\"sm\\\"` `shape=\\\"pill\\\"`).\",\n \"Wrap it in a `sticky top-3` (floating) or `sticky top-0` container to keep it on screen.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Tab\", action: \"Moves between the brand, the items and the actions; leaving an open menu closes it.\" },\n { keys: \"Enter / Space / ↓\", action: \"Opens a menu and moves into it.\" },\n { keys: \"↑ / ↓ · Home / End\", action: \"Walks the links of an open menu.\" },\n { keys: \"← / →\", action: \"Moves between the items of the bar.\" },\n { keys: \"Esc\", action: \"Closes the menu or the mobile panel and returns the focus to its trigger.\" },\n ],\n notes: [\n \"A `nav` named by `label`, a list of links and menu triggers (Radix NavigationMenu): `aria-expanded` and `aria-controls` on each trigger.\",\n \"The mobile panel is a disclosure (no focus trap): its toggle is named by `openLabel` / `closeLabel` and says `aria-expanded`.\",\n \"The light, the chevron and the tightening do not move under `prefers-reduced-motion`; the current page is tinted before scripts load.\",\n ],\n },\n related: [\"tabs\", \"dropdown-menu\", \"command\", \"button\"],\n web: {\n imports: [\"NavBar\"],\n examples: [\n { name: \"site\", title: \"A site's bar\", description: \"Brand, a Products menu, links, a call to action, a mobile panel — hover the items: one light glides between them.\" },\n { name: \"menu-open\", title: \"A rich menu\", description: \"Two columns of links with icons whose node lights up, the current page tinted.\" },\n { name: \"app-bar\", title: \"Inside an app\", description: \"`floating={false}`: a full-width bar with a hairline, for an app's sections.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
286
+ },
287
+ {
288
+ "path": "nav-bar/nav-bar.tsx",
289
+ "content": "// Client (scroll, a sliding indicator, menus). Radix NavigationMenu does the menus' semantics and keyboard (Enter, Space\n// and ↓ open; ↑ ↓ Home End walk; Escape closes and returns the focus; Tab out closes); this file draws the Krizaka bar:\n// a floating pill that tightens once the page scrolls, one light that glides to the item under the pointer or the\n// focus and rests on the current page, and a mobile panel that is a plain disclosure (no focus trap, Escape closes).\nimport { NavigationMenu as Nav, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\n\nexport const navBar = tv({\n slots: {\n root:\n \"group/nav relative z-40 mx-auto flex h-14 w-full items-center gap-2 border-border-subtle bg-surface-1/75 backdrop-blur-xl backdrop-saturate-150 \" +\n \"transition-[height,background-color,border-color,box-shadow] duration-300 motion-reduce:transition-none \" +\n \"data-compact:h-12 data-compact:border-border-default data-compact:bg-surface-1/90 data-compact:shadow-lg\",\n brand:\n \"mr-2 inline-flex shrink-0 items-center gap-2 font-display text-[15px] font-bold tracking-tight text-fg no-underline \" +\n \"rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n items: \"relative hidden md:block\",\n list:\n \"group/list relative isolate flex items-center gap-0.5 \" +\n // The light: under the item that is pointed at or focused, resting on the current page.\n \"before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-full before:bg-surface-3 before:opacity-0 before:transition-[translate,width,opacity] before:duration-300 motion-reduce:before:transition-none \" +\n \"data-[indicator=on]:before:opacity-100\",\n item:\n \"inline-flex h-9 select-none items-center gap-1 rounded-full px-3.5 text-sm font-medium text-fg-secondary no-underline transition-colors \" +\n \"hover:text-fg data-active:text-fg data-[state=open]:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n // Before the light is ready (server render), the current page carries its own tint.\n \"group-not-data-[indicator=on]/list:data-active:bg-surface-3\",\n chevron: \"h-3.5 w-3.5 transition-transform duration-300 group-data-[state=open]/trigger:rotate-180 motion-reduce:transition-none\",\n viewport:\n \"kz-pop absolute left-0 top-full mt-2 h-(--radix-navigation-menu-viewport-height) w-(--radix-navigation-menu-viewport-width) origin-top-left overflow-hidden \" +\n \"rounded-2xl border border-border-default bg-surface-1 shadow-lg transition-[width,height] duration-300 motion-reduce:transition-none\",\n panel: \"grid w-max max-w-[min(92vw,30rem)] gap-1 p-2\",\n menuLink:\n \"group/link flex items-start gap-3 rounded-xl p-3 text-left no-underline outline-hidden transition-colors \" +\n \"hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring data-active:bg-accent-soft\",\n menuIcon:\n \"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-border-default bg-surface-2 text-fg-accent \" +\n \"transition-[transform,border-color] duration-300 group-hover/link:-translate-y-0.5 group-hover/link:border-accent [&>svg]:h-4.5 [&>svg]:w-4.5 motion-reduce:transform-none\",\n menuTitle: \"block text-sm font-semibold text-fg\",\n menuText: \"mt-0.5 block text-xs leading-relaxed text-fg-secondary\",\n actions: \"ml-auto flex items-center gap-2\",\n toggle:\n \"inline-flex h-9 w-9 items-center justify-center rounded-full border border-border-subtle bg-surface-2/60 text-fg md:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n mobile: \"kz-pop absolute inset-x-0 top-full mt-2 grid gap-1 rounded-2xl border border-border-default bg-surface-1 p-2 shadow-lg md:hidden\",\n mobileLink:\n \"flex items-center justify-between rounded-xl px-3 py-3 text-[15px] font-medium text-fg no-underline hover:bg-surface-2 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:text-fg-accent\",\n },\n variants: {\n floating: {\n true: { root: \"max-w-5xl rounded-full border pl-5 pr-2 data-compact:max-w-4xl\" },\n false: { root: \"border-b px-4 md:px-6\" },\n },\n },\n defaultVariants: { floating: true },\n});\nconst s = navBar();\n\ninterface NavBarContext {\n label: string;\n open: boolean;\n setOpen: (open: boolean) => void;\n panelId: string;\n}\nconst Context = React.createContext<NavBarContext | null>(null);\nconst useNavBar = () => {\n const ctx = React.useContext(Context);\n if (!ctx) throw new Error(\"NavBar parts must be inside <NavBar.Root>.\");\n return ctx;\n};\n\nexport type NavBarRootProps = React.ComponentProps<\"header\"> & {\n /** The accessible name of the navigation (“Main”) — passed translated. */\n label: string;\n /**\n * Pixels of scroll after which the bar tightens (denser, lower, lifted). `false`: never.\n * @default 24\n */\n compactAfter?: number | false;\n /**\n * A floating pill (the Krizaka bar) or a full-width bar with a hairline under it.\n * @default true\n */\n floating?: boolean;\n};\n\n/** The bar. Place it in a `sticky top-3` (floating) or `sticky top-0` wrapper — or let it scroll. */\nexport function NavBarRoot({ label, compactAfter = 24, floating = true, className, children, ...props }: NavBarRootProps) {\n const [compact, setCompact] = React.useState(false);\n const [open, setOpen] = React.useState(false);\n const panelId = React.useId();\n React.useEffect(() => {\n if (compactAfter === false) return;\n const onScroll = () => setCompact(window.scrollY > compactAfter);\n onScroll();\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => window.removeEventListener(\"scroll\", onScroll);\n }, [compactAfter]);\n const ctx = React.useMemo(() => ({ label, open, setOpen, panelId }), [label, open, panelId]);\n return (\n <Context.Provider value={ctx}>\n <header data-compact={compact ? \"\" : undefined} data-floating={floating} className={navBar({ floating }).root({ className })} {...props}>\n {children}\n </header>\n </Context.Provider>\n );\n}\n\nexport type NavBarBrandProps = React.ComponentProps<\"a\"> & {\n /** Renders your link (a router `<Link>`) with the brand's look. */\n asChild?: boolean;\n};\n\n/** The brand: a mark and a name, linking home. */\nexport function NavBarBrand({ asChild, className, ...props }: NavBarBrandProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp className={s.brand({ className })} {...props} />;\n}\n\n/** The desktop navigation: links and menus, with the light that glides between them. Hidden below `md` (see `NavBar.Mobile`). */\nexport function NavBarItems({ className, children, ...props }: Omit<React.ComponentProps<typeof Nav.Root>, \"orientation\">) {\n const { label } = useNavBar();\n const { ref, handlers } = useSlidingIndicator<HTMLUListElement>({ item: \"[data-nav-item]\", active: \"[data-nav-item][data-active]\", follow: true });\n return (\n <Nav.Root aria-label={label} className={s.items({ className })} delayDuration={80} {...props}>\n <Nav.List ref={ref} className={s.list()} {...handlers}>\n {children}\n </Nav.List>\n <Nav.Viewport className={s.viewport()} />\n </Nav.Root>\n );\n}\n\nexport type NavBarLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"active\"> & {\n /** The current page: `aria-current=\"page\"`, and the light rests on it. */\n active?: boolean;\n};\n\n/** A link of the bar. `asChild` renders your router's `<Link>`. */\nexport function NavBarLink({ active, className, ...props }: NavBarLinkProps) {\n return (\n <Nav.Item>\n <Nav.Link data-nav-item=\"\" data-active={active ? \"\" : undefined} active={active} aria-current={active ? \"page\" : undefined} className={s.item({ className })} {...props} />\n </Nav.Item>\n );\n}\n\nexport type NavBarMenuProps = Omit<React.ComponentProps<typeof Nav.Item>, \"children\"> & {\n /** The trigger's words (“Products”) — passed translated. */\n label: string;\n /** The current page is inside this menu: the light rests on its trigger. */\n active?: boolean;\n /** The menu's links (`NavBar.MenuLink`), laid out in a panel; `columns` sets the grid. */\n children: React.ReactNode;\n /**\n * Columns of the panel.\n * @default 1\n */\n columns?: 1 | 2;\n};\n\n/** A menu of the bar: its trigger, and its links in a panel that opens under the bar. */\nexport function NavBarMenu({ label, active, columns = 1, children, className, ...props }: NavBarMenuProps) {\n return (\n <Nav.Item className={className} {...props}>\n <Nav.Trigger data-nav-item=\"\" data-active={active ? \"\" : undefined} className={cn(s.item(), \"group/trigger\")}>\n {label}\n <svg viewBox=\"0 0 24 24\" className={s.chevron()} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </Nav.Trigger>\n <Nav.Content className={cn(s.panel(), columns === 2 && \"md:grid-cols-2\")}>{children}</Nav.Content>\n </Nav.Item>\n );\n}\n\nexport type NavBarMenuLinkProps = Omit<React.ComponentProps<typeof Nav.Link>, \"title\" | \"active\"> & {\n /** The link's name — passed translated. */\n title: string;\n /** One line under it — passed translated. */\n description?: string;\n /** An icon (a signature icon of `@krizaka/icons`: its node lights up on hover). */\n icon?: React.ReactNode;\n /** The current page. */\n active?: boolean;\n};\n\n/** A link inside a menu: an icon, a name and a line of text. */\nexport function NavBarMenuLink({ title, description, icon, active, href, className, asChild, children, ...props }: NavBarMenuLinkProps) {\n const body = (\n <>\n {icon && <span className={s.menuIcon()}>{icon}</span>}\n <span className=\"min-w-0\">\n <span className={s.menuTitle()}>{title}</span>\n {description && <span className={s.menuText()}>{description}</span>}\n </span>\n </>\n );\n return (\n <Nav.Link asChild={asChild} href={asChild ? undefined : href} active={active} aria-current={active ? \"page\" : undefined} data-active={active ? \"\" : undefined} className={s.menuLink({ className })} {...props}>\n {asChild && React.isValidElement<{ children?: React.ReactNode }>(children) ? React.cloneElement(children, undefined, body) : body}\n </Nav.Link>\n );\n}\n\n/** The right side: search, theme, language, the call to action. */\nexport function NavBarActions({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.actions({ className })} {...props} />;\n}\n\nexport type NavBarMobileProps = React.ComponentProps<\"nav\"> & {\n /** The toggle's name when closed (“Open the menu”) — passed translated. */\n openLabel: string;\n /** The toggle's name when open (“Close the menu”) — passed translated. */\n closeLabel: string;\n};\n\n/**\n * Below `md`: a toggle, and a panel under the bar with its links (`NavBar.MobileLink`). A disclosure: Escape and a click\n * on a link close it, the focus returns to the toggle.\n */\nexport function NavBarMobile({ openLabel, closeLabel, className, children, ...props }: NavBarMobileProps) {\n const { label, open, setOpen, panelId } = useNavBar();\n const toggle = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n setOpen(false);\n toggle.current?.focus();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [open, setOpen]);\n return (\n <>\n <button ref={toggle} type=\"button\" aria-expanded={open} aria-controls={panelId} aria-label={open ? closeLabel : openLabel} onClick={() => setOpen(!open)} className={s.toggle()}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4.5 w-4.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n {open ? <path d=\"M18 6 6 18M6 6l12 12\" /> : <path d=\"M4 7h16M4 12h16M4 17h10\" />}\n </svg>\n </button>\n {open && (\n <nav\n id={panelId}\n aria-label={label}\n className={s.mobile({ className })}\n onClick={(e) => {\n if ((e.target as Element).closest(\"a\")) setOpen(false);\n }}\n {...props}\n >\n {children}\n </nav>\n )}\n </>\n );\n}\n\nexport type NavBarMobileLinkProps = React.ComponentProps<\"a\"> & {\n /** The current page. */\n active?: boolean;\n /** Renders your router's `<Link>`. */\n asChild?: boolean;\n};\n\n/** A link of the mobile panel. */\nexport function NavBarMobileLink({ active, asChild, className, ...props }: NavBarMobileLinkProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return <Comp aria-current={active ? \"page\" : undefined} className={s.mobileLink({ className })} {...props} />;\n}\n\n/**\n * `NavBar.Root/Brand/Items/Link/Menu/MenuLink/Actions/Mobile/MobileLink` — the Krizaka navigation bar: floating, tightening\n * on scroll, one light gliding between its items, accessible menus, a mobile panel.\n */\nexport const NavBar = {\n Root: NavBarRoot,\n Brand: NavBarBrand,\n Items: NavBarItems,\n Link: NavBarLink,\n Menu: NavBarMenu,\n MenuLink: NavBarMenuLink,\n Actions: NavBarActions,\n Mobile: NavBarMobile,\n MobileLink: NavBarMobileLink,\n};\n"
290
+ },
291
+ {
292
+ "path": "sliding-indicator.ts",
293
+ "content": "// The shared sliding indicator (NavBar, Tabs): one element that glides from item to item instead of each item lighting\n// up on its own. It writes the target's box as CSS variables on the list (`--kz-ind-x/y/w/h`) and `data-indicator=\"on\"`\n// — no re-render, no animation frame: the glide is a CSS transition (none under reduced motion). Before hydration the\n// items keep their own active style, so nothing flashes.\nimport * as React from \"react\";\n\nconst useIsoLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\nexport function useSlidingIndicator<T extends HTMLElement>({ item, active, follow = false }: { item: string; active: string; follow?: boolean }) {\n const ref = React.useRef<T>(null);\n const place = React.useCallback((target: Element | null) => {\n const list = ref.current;\n if (!list) return;\n if (!(target instanceof HTMLElement) || !list.contains(target)) {\n list.removeAttribute(\"data-indicator\");\n return;\n }\n const a = target.getBoundingClientRect();\n const b = list.getBoundingClientRect();\n list.style.setProperty(\"--kz-ind-x\", `${a.left - b.left + list.scrollLeft}px`);\n list.style.setProperty(\"--kz-ind-y\", `${a.top - b.top + list.scrollTop}px`);\n list.style.setProperty(\"--kz-ind-w\", `${a.width}px`);\n list.style.setProperty(\"--kz-ind-h\", `${a.height}px`);\n list.setAttribute(\"data-indicator\", \"on\");\n }, []);\n const rest = React.useCallback(() => place(ref.current?.querySelector(active) ?? null), [place, active]);\n\n useIsoLayoutEffect(() => {\n const list = ref.current;\n if (!list) return;\n rest();\n const resize = new ResizeObserver(rest);\n resize.observe(list);\n const changes = new MutationObserver(rest);\n changes.observe(list, { subtree: true, attributes: true, attributeFilter: [\"data-state\", \"aria-current\", \"data-active\"] });\n return () => {\n resize.disconnect();\n changes.disconnect();\n };\n }, [rest]);\n\n const handlers = follow\n ? {\n onPointerOver: (e: React.PointerEvent) => place((e.target as Element).closest(item)),\n onPointerLeave: rest,\n onFocus: (e: React.FocusEvent) => place((e.target as Element).closest(item)),\n onBlur: rest,\n }\n : {};\n return { ref, handlers };\n}\n"
294
+ }
295
+ ],
296
+ "dependencies": [
297
+ "radix-ui@^1.7.0",
298
+ "tailwind-variants@^3.3.1"
299
+ ],
300
+ "registryDependencies": [
301
+ "cn"
302
+ ],
303
+ "demo": {
304
+ "path": "examples/nav-bar/site.tsx",
305
+ "content": "import { Button } from \"@krizaka/ui/button\";\nimport { NavBar } from \"@krizaka/ui/nav-bar\";\n\nconst products = [\n { href: \"#orazaka\", title: \"Orazaka\", description: \"Sovereign AI, on your own servers.\" },\n { href: \"#orochia\", title: \"Orochia\", description: \"Creators keep 90 % of every support.\" },\n { href: \"#ui\", title: \"Krizaka UI\", description: \"The components behind both.\" },\n];\n\n// The site's bar: a floating pill that tightens on scroll, one light gliding between its items and resting on the\n// current page, an accessible menu (Enter or ↓ opens, Escape closes), and a panel below `md`.\nexport default function NavBarSite() {\n return (\n <NavBar.Root label=\"Main\">\n <NavBar.Brand href=\"#home\">Krizaka</NavBar.Brand>\n <NavBar.Items>\n <NavBar.Menu label=\"Products\">\n {products.map((p) => (\n <NavBar.MenuLink key={p.href} {...p} />\n ))}\n </NavBar.Menu>\n <NavBar.Link href=\"#docs\">Docs</NavBar.Link>\n <NavBar.Link href=\"#story\" active>Our story</NavBar.Link>\n </NavBar.Items>\n <NavBar.Actions>\n <Button variant=\"primary\" size=\"sm\" shape=\"pill\" className=\"hidden sm:inline-flex\">Contact</Button>\n <NavBar.Mobile openLabel=\"Open the menu\" closeLabel=\"Close the menu\">\n {products.map((p) => (\n <NavBar.MobileLink key={p.href} href={p.href}>{p.title}</NavBar.MobileLink>\n ))}\n <NavBar.MobileLink href=\"#story\" active>Our story</NavBar.MobileLink>\n </NavBar.Mobile>\n </NavBar.Actions>\n </NavBar.Root>\n );\n}\n"
306
+ },
307
+ "props": [
308
+ {
309
+ "component": "NavBarRoot",
310
+ "description": "The bar. Place it in a `sticky top-3` (floating) or `sticky top-0` wrapper — or let it scroll.",
311
+ "props": [
312
+ {
313
+ "name": "label",
314
+ "type": "string",
315
+ "default": null,
316
+ "description": "The accessible name of the navigation (“Main”) — passed translated.",
317
+ "required": true
318
+ },
319
+ {
320
+ "name": "compactAfter",
321
+ "type": "number | false",
322
+ "default": "24",
323
+ "description": "Pixels of scroll after which the bar tightens (denser, lower, lifted). `false`: never.",
324
+ "required": false
325
+ },
326
+ {
327
+ "name": "floating",
328
+ "type": "boolean",
329
+ "default": "true",
330
+ "description": "A floating pill (the Krizaka bar) or a full-width bar with a hairline under it.",
331
+ "required": false
332
+ }
333
+ ]
334
+ },
335
+ {
336
+ "component": "NavBarBrand",
337
+ "description": "The brand: a mark and a name, linking home.",
338
+ "props": [
339
+ {
340
+ "name": "asChild",
341
+ "type": "boolean",
342
+ "default": null,
343
+ "description": "Renders your link (a router `<Link>`) with the brand's look.",
344
+ "required": false
345
+ }
346
+ ]
347
+ },
348
+ {
349
+ "component": "NavBarItems",
350
+ "description": "The desktop navigation: links and menus, with the light that glides between them. Hidden below `md` (see `NavBar.Mobile`).",
351
+ "props": []
352
+ },
353
+ {
354
+ "component": "NavBarLink",
355
+ "description": "A link of the bar. `asChild` renders your router's `<Link>`.",
356
+ "props": [
357
+ {
358
+ "name": "active",
359
+ "type": "boolean",
360
+ "default": null,
361
+ "description": "The current page: `aria-current=\"page\"`, and the light rests on it.",
362
+ "required": false
363
+ }
364
+ ]
365
+ },
366
+ {
367
+ "component": "NavBarMenu",
368
+ "description": "A menu of the bar: its trigger, and its links in a panel that opens under the bar.",
369
+ "props": [
370
+ {
371
+ "name": "children",
372
+ "type": "ReactNode",
373
+ "default": null,
374
+ "description": "The menu's links (`NavBar.MenuLink`), laid out in a panel; `columns` sets the grid.",
375
+ "required": true
376
+ },
377
+ {
378
+ "name": "label",
379
+ "type": "string",
380
+ "default": null,
381
+ "description": "The trigger's words (“Products”) — passed translated.",
382
+ "required": true
383
+ },
384
+ {
385
+ "name": "active",
386
+ "type": "boolean",
387
+ "default": null,
388
+ "description": "The current page is inside this menu: the light rests on its trigger.",
389
+ "required": false
390
+ },
391
+ {
392
+ "name": "columns",
393
+ "type": "1 | 2",
394
+ "default": "1",
395
+ "description": "Columns of the panel.",
396
+ "required": false
397
+ }
398
+ ]
399
+ },
400
+ {
401
+ "component": "NavBarMenuLink",
402
+ "description": "A link inside a menu: an icon, a name and a line of text.",
403
+ "props": [
404
+ {
405
+ "name": "title",
406
+ "type": "string",
407
+ "default": null,
408
+ "description": "The link's name — passed translated.",
409
+ "required": true
410
+ },
411
+ {
412
+ "name": "active",
413
+ "type": "boolean",
414
+ "default": null,
415
+ "description": "The current page.",
416
+ "required": false
417
+ },
418
+ {
419
+ "name": "description",
420
+ "type": "string",
421
+ "default": null,
422
+ "description": "One line under it — passed translated.",
423
+ "required": false
424
+ },
425
+ {
426
+ "name": "icon",
427
+ "type": "ReactNode",
428
+ "default": null,
429
+ "description": "An icon (a signature icon of `@krizaka/icons`: its node lights up on hover).",
430
+ "required": false
431
+ }
432
+ ]
433
+ },
434
+ {
435
+ "component": "NavBarActions",
436
+ "description": "The right side: search, theme, language, the call to action.",
437
+ "props": []
438
+ },
439
+ {
440
+ "component": "NavBarMobile",
441
+ "description": "Below `md`: a toggle, and a panel under the bar with its links (`NavBar.MobileLink`). A disclosure: Escape and a click\non a link close it, the focus returns to the toggle.",
442
+ "props": [
443
+ {
444
+ "name": "closeLabel",
445
+ "type": "string",
446
+ "default": null,
447
+ "description": "The toggle's name when open (“Close the menu”) — passed translated.",
448
+ "required": true
449
+ },
450
+ {
451
+ "name": "openLabel",
452
+ "type": "string",
453
+ "default": null,
454
+ "description": "The toggle's name when closed (“Open the menu”) — passed translated.",
455
+ "required": true
456
+ }
457
+ ]
458
+ },
459
+ {
460
+ "component": "NavBarMobileLink",
461
+ "description": "A link of the mobile panel.",
462
+ "props": [
463
+ {
464
+ "name": "active",
465
+ "type": "boolean",
466
+ "default": null,
467
+ "description": "The current page.",
468
+ "required": false
469
+ },
470
+ {
471
+ "name": "asChild",
472
+ "type": "boolean",
473
+ "default": null,
474
+ "description": "Renders your router's `<Link>`.",
475
+ "required": false
476
+ }
477
+ ]
478
+ }
479
+ ]
480
+ }