@krizaka/ui 3.4.0 → 3.6.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 (337) hide show
  1. package/README.md +1 -0
  2. package/dist/app-shell.d.ts +24 -22
  3. package/dist/app-shell.js +9 -3
  4. package/dist/app-shell.js.map +1 -1
  5. package/dist/argument-card.js +3 -3
  6. package/dist/chat.d.ts +187 -0
  7. package/dist/chat.js +132 -0
  8. package/dist/chat.js.map +1 -0
  9. package/dist/chunk-3GODX3D6.js +25 -0
  10. package/dist/chunk-3GODX3D6.js.map +1 -0
  11. package/dist/chunk-ECSCA3CN.js +128 -0
  12. package/dist/chunk-ECSCA3CN.js.map +1 -0
  13. package/dist/chunk-F2GKFIYK.js +47 -0
  14. package/dist/chunk-F2GKFIYK.js.map +1 -0
  15. package/dist/{chunk-R6SDS7JM.js → chunk-ND3L5VHT.js} +25 -2
  16. package/dist/chunk-ND3L5VHT.js.map +1 -0
  17. package/dist/command.js +1 -1
  18. package/dist/data-table.js +20 -134
  19. package/dist/data-table.js.map +1 -1
  20. package/dist/dialog.js +7 -3
  21. package/dist/dialog.js.map +1 -1
  22. package/dist/loader.d.ts +21 -1
  23. package/dist/loader.js +7 -3
  24. package/dist/loader.js.map +1 -1
  25. package/dist/map.js +7 -1
  26. package/dist/map.js.map +1 -1
  27. package/dist/marketing-layout.d.ts +3 -9
  28. package/dist/marketing-layout.js +24 -7
  29. package/dist/marketing-layout.js.map +1 -1
  30. package/dist/match-dialog.d.ts +32 -0
  31. package/dist/match-dialog.js +168 -0
  32. package/dist/match-dialog.js.map +1 -0
  33. package/dist/native.d.ts +484 -106
  34. package/dist/native.js +1967 -1323
  35. package/dist/native.js.map +1 -1
  36. package/dist/page-hero.js +3 -3
  37. package/dist/photo-grid.d.ts +122 -0
  38. package/dist/photo-grid.js +94 -0
  39. package/dist/photo-grid.js.map +1 -0
  40. package/dist/skip-link.d.ts +15 -0
  41. package/dist/skip-link.js +10 -0
  42. package/dist/skip-link.js.map +1 -0
  43. package/dist/step-progress.d.ts +20 -0
  44. package/dist/step-progress.js +17 -0
  45. package/dist/step-progress.js.map +1 -0
  46. package/dist/swipe-deck.d.ts +183 -0
  47. package/dist/swipe-deck.js +144 -0
  48. package/dist/swipe-deck.js.map +1 -0
  49. package/dist/tag-selector.d.ts +26 -0
  50. package/dist/tag-selector.js +21 -0
  51. package/dist/tag-selector.js.map +1 -0
  52. package/dist/tree-view.d.ts +2 -0
  53. package/dist/tree-view.js.map +1 -1
  54. package/dist/verified-badge.d.ts +16 -0
  55. package/dist/verified-badge.js +23 -0
  56. package/dist/verified-badge.js.map +1 -0
  57. package/package.json +120 -8
  58. package/registry/achievement.json +2 -1
  59. package/registry/action-bar.json +2 -1
  60. package/registry/action-sheet.json +1 -0
  61. package/registry/activity-feed.json +2 -1
  62. package/registry/alert.json +2 -1
  63. package/registry/app-shell.json +27 -5
  64. package/registry/argument-card.json +2 -1
  65. package/registry/audio-player.json +2 -1
  66. package/registry/auth-layout.json +7 -2
  67. package/registry/avatar.json +2 -1
  68. package/registry/badge.json +2 -1
  69. package/registry/button.json +2 -1
  70. package/registry/card-grid.json +2 -1
  71. package/registry/card.json +2 -1
  72. package/registry/catalogue-page.json +5 -2
  73. package/registry/changelog-page.json +5 -2
  74. package/registry/chat.json +918 -0
  75. package/registry/checkbox.json +2 -1
  76. package/registry/chip-link.json +2 -1
  77. package/registry/chip.json +2 -1
  78. package/registry/choice-grid.json +2 -1
  79. package/registry/cn.json +1 -0
  80. package/registry/code-block.json +2 -1
  81. package/registry/combobox.json +2 -1
  82. package/registry/command.json +2 -1
  83. package/registry/component-page.json +5 -2
  84. package/registry/confirm-button.json +2 -1
  85. package/registry/contact-page.json +5 -2
  86. package/registry/continue-prompt.json +2 -1
  87. package/registry/countdown.json +2 -1
  88. package/registry/cta-panel.json +2 -1
  89. package/registry/dashboard-layout.json +5 -2
  90. package/registry/data-table.json +2 -1
  91. package/registry/date-picker.json +3 -2
  92. package/registry/decision-list.json +2 -1
  93. package/registry/description-list.json +2 -1
  94. package/registry/detail-layout.json +16 -2
  95. package/registry/dialog.json +10 -4
  96. package/registry/docs-article-page.json +16 -2
  97. package/registry/docs-hub-page.json +5 -2
  98. package/registry/docs-layout.json +2 -1
  99. package/registry/dropdown-menu.json +2 -1
  100. package/registry/empty-state.json +14 -2
  101. package/registry/error-page.json +5 -2
  102. package/registry/examples/chat/conversation.d.ts +4 -0
  103. package/registry/examples/chat/conversation.js +22 -0
  104. package/registry/examples/chat/conversation.tsx +21 -0
  105. package/registry/examples/chat/inbox.d.ts +4 -0
  106. package/registry/examples/chat/inbox.js +15 -0
  107. package/registry/examples/chat/inbox.tsx +12 -0
  108. package/registry/examples/chat/native/conversation.d.ts +4 -0
  109. package/registry/examples/chat/native/conversation.dark.png +0 -0
  110. package/registry/examples/chat/native/conversation.js +23 -0
  111. package/registry/examples/chat/native/conversation.light.png +0 -0
  112. package/registry/examples/chat/native/conversation.tsx +20 -0
  113. package/registry/examples/chat/native/inbox.d.ts +4 -0
  114. package/registry/examples/chat/native/inbox.dark.png +0 -0
  115. package/registry/examples/chat/native/inbox.js +16 -0
  116. package/registry/examples/chat/native/inbox.light.png +0 -0
  117. package/registry/examples/chat/native/inbox.tsx +13 -0
  118. package/registry/examples/detail-layout/checkout.d.ts +4 -0
  119. package/registry/examples/detail-layout/checkout.js +37 -0
  120. package/registry/examples/detail-layout/checkout.tsx +44 -0
  121. package/registry/examples/docs-article-page/faq.d.ts +4 -0
  122. package/registry/examples/docs-article-page/faq.js +31 -0
  123. package/registry/examples/docs-article-page/faq.tsx +40 -0
  124. package/registry/examples/empty-state/library.d.ts +4 -0
  125. package/registry/examples/empty-state/library.js +25 -0
  126. package/registry/examples/empty-state/library.tsx +20 -0
  127. package/registry/examples/loader/branded.d.ts +4 -0
  128. package/registry/examples/loader/branded.js +12 -0
  129. package/registry/examples/loader/branded.tsx +7 -0
  130. package/registry/examples/loader/native/branded.d.ts +4 -0
  131. package/registry/examples/loader/native/branded.dark.png +0 -0
  132. package/registry/examples/loader/native/branded.js +12 -0
  133. package/registry/examples/loader/native/branded.light.png +0 -0
  134. package/registry/examples/loader/native/branded.tsx +11 -0
  135. package/registry/examples/longform-page/case-study.d.ts +4 -0
  136. package/registry/examples/longform-page/case-study.js +41 -0
  137. package/registry/examples/longform-page/case-study.tsx +40 -0
  138. package/registry/examples/match-dialog/its-a-match.d.ts +4 -0
  139. package/registry/examples/match-dialog/its-a-match.js +29 -0
  140. package/registry/examples/match-dialog/its-a-match.tsx +25 -0
  141. package/registry/examples/match-dialog/native/its-a-match.d.ts +4 -0
  142. package/registry/examples/match-dialog/native/its-a-match.dark.png +0 -0
  143. package/registry/examples/match-dialog/native/its-a-match.js +28 -0
  144. package/registry/examples/match-dialog/native/its-a-match.light.png +0 -0
  145. package/registry/examples/match-dialog/native/its-a-match.tsx +22 -0
  146. package/registry/examples/photo-grid/native/profile-photos.d.ts +4 -0
  147. package/registry/examples/photo-grid/native/profile-photos.dark.png +0 -0
  148. package/registry/examples/photo-grid/native/profile-photos.js +27 -0
  149. package/registry/examples/photo-grid/native/profile-photos.light.png +0 -0
  150. package/registry/examples/photo-grid/native/profile-photos.tsx +24 -0
  151. package/registry/examples/photo-grid/profile-photos.d.ts +4 -0
  152. package/registry/examples/photo-grid/profile-photos.js +28 -0
  153. package/registry/examples/photo-grid/profile-photos.tsx +25 -0
  154. package/registry/examples/pull-to-refresh/native/branded.d.ts +4 -0
  155. package/registry/examples/pull-to-refresh/native/branded.dark.png +0 -0
  156. package/registry/examples/pull-to-refresh/native/branded.js +14 -0
  157. package/registry/examples/pull-to-refresh/native/branded.light.png +0 -0
  158. package/registry/examples/pull-to-refresh/native/branded.tsx +23 -0
  159. package/registry/examples/screen/native/app-shell.d.ts +4 -0
  160. package/registry/examples/screen/native/app-shell.dark.png +0 -0
  161. package/registry/examples/screen/native/app-shell.js +25 -0
  162. package/registry/examples/screen/native/app-shell.light.png +0 -0
  163. package/registry/examples/screen/native/app-shell.tsx +33 -0
  164. package/registry/examples/screen/native/auth.d.ts +4 -0
  165. package/registry/examples/screen/native/auth.dark.png +0 -0
  166. package/registry/examples/screen/native/auth.js +24 -0
  167. package/registry/examples/screen/native/auth.light.png +0 -0
  168. package/registry/examples/screen/native/auth.tsx +20 -0
  169. package/registry/examples/screen/native/chat.d.ts +4 -0
  170. package/registry/examples/screen/native/chat.dark.png +0 -0
  171. package/registry/examples/screen/native/chat.js +27 -0
  172. package/registry/examples/screen/native/chat.light.png +0 -0
  173. package/registry/examples/screen/native/chat.tsx +22 -0
  174. package/registry/examples/screen/native/feed.d.ts +4 -0
  175. package/registry/examples/screen/native/feed.dark.png +0 -0
  176. package/registry/examples/screen/native/feed.js +24 -0
  177. package/registry/examples/screen/native/feed.light.png +0 -0
  178. package/registry/examples/screen/native/feed.tsx +34 -0
  179. package/registry/examples/screen/native/map.d.ts +4 -0
  180. package/registry/examples/screen/native/map.dark.png +0 -0
  181. package/registry/examples/screen/native/map.js +26 -0
  182. package/registry/examples/screen/native/map.light.png +0 -0
  183. package/registry/examples/screen/native/map.tsx +31 -0
  184. package/registry/examples/screen/native/notifications.d.ts +4 -0
  185. package/registry/examples/screen/native/notifications.dark.png +0 -0
  186. package/registry/examples/screen/native/notifications.js +21 -0
  187. package/registry/examples/screen/native/notifications.light.png +0 -0
  188. package/registry/examples/screen/native/notifications.tsx +21 -0
  189. package/registry/examples/screen/native/profile.d.ts +4 -0
  190. package/registry/examples/screen/native/profile.dark.png +0 -0
  191. package/registry/examples/screen/native/profile.js +29 -0
  192. package/registry/examples/screen/native/profile.light.png +0 -0
  193. package/registry/examples/screen/native/profile.tsx +27 -0
  194. package/registry/examples/screen/native/settings.d.ts +4 -0
  195. package/registry/examples/screen/native/settings.dark.png +0 -0
  196. package/registry/examples/screen/native/settings.js +20 -0
  197. package/registry/examples/screen/native/settings.light.png +0 -0
  198. package/registry/examples/screen/native/settings.tsx +19 -0
  199. package/registry/examples/skip-link/default.d.ts +4 -0
  200. package/registry/examples/skip-link/default.js +19 -0
  201. package/registry/examples/skip-link/default.tsx +12 -0
  202. package/registry/examples/splash-handoff/native/default.d.ts +4 -0
  203. package/registry/examples/splash-handoff/native/default.dark.png +0 -0
  204. package/registry/examples/splash-handoff/native/default.js +12 -0
  205. package/registry/examples/splash-handoff/native/default.light.png +0 -0
  206. package/registry/examples/splash-handoff/native/default.tsx +11 -0
  207. package/registry/examples/split-view/nearby.d.ts +4 -0
  208. package/registry/examples/split-view/nearby.js +33 -0
  209. package/registry/examples/split-view/nearby.tsx +45 -0
  210. package/registry/examples/split-view/notifications.d.ts +4 -0
  211. package/registry/examples/split-view/notifications.js +33 -0
  212. package/registry/examples/split-view/notifications.tsx +31 -0
  213. package/registry/examples/status-page/maintenance.d.ts +4 -0
  214. package/registry/examples/status-page/maintenance.js +29 -0
  215. package/registry/examples/status-page/maintenance.tsx +24 -0
  216. package/registry/examples/step-progress/native/sign-up.d.ts +4 -0
  217. package/registry/examples/step-progress/native/sign-up.dark.png +0 -0
  218. package/registry/examples/step-progress/native/sign-up.js +12 -0
  219. package/registry/examples/step-progress/native/sign-up.light.png +0 -0
  220. package/registry/examples/step-progress/native/sign-up.tsx +11 -0
  221. package/registry/examples/step-progress/sign-up.d.ts +4 -0
  222. package/registry/examples/step-progress/sign-up.js +22 -0
  223. package/registry/examples/step-progress/sign-up.tsx +16 -0
  224. package/registry/examples/swipe-deck/discover.d.ts +4 -0
  225. package/registry/examples/swipe-deck/discover.js +41 -0
  226. package/registry/examples/swipe-deck/discover.tsx +37 -0
  227. package/registry/examples/swipe-deck/native/discover.d.ts +4 -0
  228. package/registry/examples/swipe-deck/native/discover.dark.png +0 -0
  229. package/registry/examples/swipe-deck/native/discover.js +39 -0
  230. package/registry/examples/swipe-deck/native/discover.light.png +0 -0
  231. package/registry/examples/swipe-deck/native/discover.tsx +35 -0
  232. package/registry/examples/tag-selector/interests.d.ts +4 -0
  233. package/registry/examples/tag-selector/interests.js +14 -0
  234. package/registry/examples/tag-selector/interests.tsx +12 -0
  235. package/registry/examples/tag-selector/native/interests.d.ts +4 -0
  236. package/registry/examples/tag-selector/native/interests.dark.png +0 -0
  237. package/registry/examples/tag-selector/native/interests.js +15 -0
  238. package/registry/examples/tag-selector/native/interests.light.png +0 -0
  239. package/registry/examples/tag-selector/native/interests.tsx +15 -0
  240. package/registry/examples/verified-badge/native/verified.d.ts +4 -0
  241. package/registry/examples/verified-badge/native/verified.dark.png +0 -0
  242. package/registry/examples/verified-badge/native/verified.js +18 -0
  243. package/registry/examples/verified-badge/native/verified.light.png +0 -0
  244. package/registry/examples/verified-badge/native/verified.tsx +15 -0
  245. package/registry/examples/verified-badge/verified.d.ts +4 -0
  246. package/registry/examples/verified-badge/verified.js +17 -0
  247. package/registry/examples/verified-badge/verified.tsx +13 -0
  248. package/registry/fab.json +1 -0
  249. package/registry/field.json +2 -1
  250. package/registry/file-upload.json +3 -2
  251. package/registry/filter-bar.json +2 -1
  252. package/registry/form.json +3 -2
  253. package/registry/gallery-page.json +5 -2
  254. package/registry/glow-card.json +2 -1
  255. package/registry/goal-gauge.json +2 -1
  256. package/registry/header.json +1 -0
  257. package/registry/how-it-works-page.json +5 -2
  258. package/registry/image-cropper.json +2 -1
  259. package/registry/image-viewer.json +1 -0
  260. package/registry/index.json +608 -19
  261. package/registry/iso.json +2 -1
  262. package/registry/kanban.json +2 -1
  263. package/registry/kbd.json +2 -1
  264. package/registry/landing-page.json +5 -2
  265. package/registry/landscape.json +2 -1
  266. package/registry/legal-page.json +5 -2
  267. package/registry/link-card.json +2 -1
  268. package/registry/list.json +2 -1
  269. package/registry/loader.json +47 -3
  270. package/registry/longform-page.json +16 -2
  271. package/registry/map.json +4 -3
  272. package/registry/marketing-layout.json +13 -4
  273. package/registry/marquee.json +2 -1
  274. package/registry/mascot-quote.json +2 -1
  275. package/registry/mascot-state.json +2 -1
  276. package/registry/mascots.json +2 -1
  277. package/registry/match-dialog.json +315 -0
  278. package/registry/media-capture.json +2 -1
  279. package/registry/media-picker.json +1 -0
  280. package/registry/meter.json +2 -1
  281. package/registry/nav-bar.json +2 -1
  282. package/registry/number-ticker.json +2 -1
  283. package/registry/onboarding.json +1 -0
  284. package/registry/otp-input.json +1 -0
  285. package/registry/page-header.json +2 -1
  286. package/registry/page-hero.json +2 -1
  287. package/registry/page.json +2 -1
  288. package/registry/pagination.json +2 -1
  289. package/registry/permission-prompt.json +1 -0
  290. package/registry/photo-grid.json +303 -0
  291. package/registry/pointer-light.json +2 -1
  292. package/registry/popover.json +2 -1
  293. package/registry/pricing-page.json +5 -2
  294. package/registry/product-page.json +5 -2
  295. package/registry/profile-card.json +2 -1
  296. package/registry/progress.json +2 -1
  297. package/registry/promise-card.json +2 -1
  298. package/registry/pull-to-refresh.json +68 -4
  299. package/registry/radio-group.json +2 -1
  300. package/registry/screen.json +233 -0
  301. package/registry/scroll-stage.json +2 -1
  302. package/registry/search-field.json +2 -1
  303. package/registry/section-backdrop.json +2 -1
  304. package/registry/segmented.json +1 -0
  305. package/registry/separator.json +2 -1
  306. package/registry/settings-layout.json +5 -2
  307. package/registry/skeleton.json +2 -1
  308. package/registry/skip-link.json +122 -0
  309. package/registry/slider.json +2 -1
  310. package/registry/slot.json +2 -1
  311. package/registry/spinner.json +2 -1
  312. package/registry/splash-handoff.json +138 -0
  313. package/registry/split-view.json +27 -2
  314. package/registry/stage-banner.json +2 -1
  315. package/registry/stat.json +2 -1
  316. package/registry/status-page.json +18 -2
  317. package/registry/step-progress.json +256 -0
  318. package/registry/stepper.json +2 -1
  319. package/registry/swipe-deck.json +513 -0
  320. package/registry/swipeable-row.json +1 -0
  321. package/registry/switch.json +2 -1
  322. package/registry/tab-bar.json +1 -0
  323. package/registry/tabs.json +2 -1
  324. package/registry/tag-input.json +2 -1
  325. package/registry/tag-selector.json +277 -0
  326. package/registry/terminal-replay.json +2 -1
  327. package/registry/theme.json +2 -1
  328. package/registry/toast.json +2 -1
  329. package/registry/toc.json +2 -1
  330. package/registry/tooltip.json +2 -1
  331. package/registry/tree-view.json +10 -4
  332. package/registry/txt.json +1 -0
  333. package/registry/verified-badge.json +203 -0
  334. package/registry/video-player.json +2 -1
  335. package/registry/video-trimmer.json +2 -1
  336. package/registry/virtual.json +2 -1
  337. package/dist/chunk-R6SDS7JM.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  "summary": "Dates and times chosen the way the task asks — a calendar, a period with presets, a strip of days, wheels, time slots, a month, a date of birth — as ISO values (`2026-10-11`, `2026-10`, `18:30`) the form, the address and the API share, in the locale's own words.",
6
6
  "description": "Dates and times chosen the way the task asks — a calendar, a period with presets, a strip of days, wheels, time slots, a month, a date of birth — as ISO values (`2026-10-11`, `2026-10`, `18:30`) the form, the address and the API share, in the locale's own words.",
7
7
  "status": "beta",
8
+ "deprecated": null,
8
9
  "category": "forms",
9
10
  "platforms": "both",
10
11
  "whenToUse": [
@@ -1491,7 +1492,7 @@
1491
1492
  },
1492
1493
  {
1493
1494
  "path": "meta.ts",
1494
- "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"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 * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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"
1495
+ "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\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 */\ninterface ExampleBase {\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/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: 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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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 /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\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"
1495
1496
  },
1496
1497
  {
1497
1498
  "path": "tv.ts",
@@ -1499,7 +1500,7 @@
1499
1500
  }
1500
1501
  ],
1501
1502
  "dependencies": [
1502
- "@krizaka/intl@^0.1.1",
1503
+ "@krizaka/intl@^0.2.0",
1503
1504
  "tailwind-variants@^3.3.1"
1504
1505
  ],
1505
1506
  "registryDependencies": [
@@ -5,6 +5,7 @@
5
5
  "summary": "The decisions behind a product, numbered: what was chosen, large, then side by side what was refused, why, and what it costs the reader — each with its mark and its colour.",
6
6
  "description": "The decisions behind a product, numbered: what was chosen, large, then side by side what was refused, why, and what it costs the reader — each with its mark and its colour.",
7
7
  "status": "beta",
8
+ "deprecated": null,
8
9
  "category": "blocks",
9
10
  "platforms": "web",
10
11
  "whenToUse": [
@@ -101,7 +102,7 @@
101
102
  },
102
103
  {
103
104
  "path": "meta.ts",
104
- "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"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 * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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"
105
+ "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\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 */\ninterface ExampleBase {\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/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: 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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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 /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\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"
105
106
  }
106
107
  ],
107
108
  "dependencies": [],
@@ -5,6 +5,7 @@
5
5
  "summary": "Facts about one thing as term and value: stacked, inline like a receipt, or a grid of key figures (`KeyValueGrid`).",
6
6
  "description": "Facts about one thing as term and value: stacked, inline like a receipt, or a grid of key figures (`KeyValueGrid`).",
7
7
  "status": "beta",
8
+ "deprecated": null,
8
9
  "category": "data-display",
9
10
  "platforms": "both",
10
11
  "whenToUse": [
@@ -176,7 +177,7 @@
176
177
  },
177
178
  {
178
179
  "path": "meta.ts",
179
- "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"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 * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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"
180
+ "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\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 */\ninterface ExampleBase {\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/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: 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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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 /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\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"
180
181
  }
181
182
  ],
182
183
  "dependencies": [
@@ -5,6 +5,7 @@
5
5
  "summary": "One object's page: the way back, its media, name, status, facts and actions, its tabs, the content and a side panel that goes under the content on a phone.",
6
6
  "description": "One object's page: the way back, its media, name, status, facts and actions, its tabs, the content and a side panel that goes under the content on a phone.",
7
7
  "status": "beta",
8
+ "deprecated": null,
8
9
  "category": "layouts",
9
10
  "platforms": "web",
10
11
  "whenToUse": [
@@ -118,10 +119,23 @@
118
119
  "title": "A video",
119
120
  "description": "Orochia's video page: thumbnail, status, facts, tabs and the sales beside.",
120
121
  "template": true,
122
+ "purpose": "show",
123
+ "why": "A viewer sees the object itself first, with its facts and the actions that concern it beside it.",
121
124
  "path": "examples/detail-layout/video.tsx",
122
125
  "module": "@krizaka/ui/registry/examples/detail-layout/video",
123
126
  "code": "\"use client\";\n\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { DetailLayout } from \"@krizaka/ui/detail-layout\";\nimport { Facts } from \"@krizaka/ui/page\";\nimport { Stat } from \"@krizaka/ui/stat\";\nimport { Tabs } from \"@krizaka/ui/tabs\";\n\n// TEMPLATE — one video in Orochia's studio: the way back to the list, its thumbnail, title and status, its facts, the\n// actions; the tabs (overview · sales · comments); the side panel with the price and the audience — under the content on\n// a phone, where the actions wrap under the title.\nexport default function DetailLayoutVideo() {\n return (\n <div className=\"brand-orochia w-full\">\n <Tabs.Root defaultValue=\"overview\">\n <DetailLayout\n back={<a href=\"#videos\">← Videos</a>}\n media={<div className=\"aspect-video w-40 bg-linear-to-br from-accent to-accent-2\" />}\n eyebrow=\"Video · v-2087\"\n title=\"Night shoot, part 2\"\n status={<Badge tone=\"success\" dot>Published</Badge>}\n meta={<Facts items={[{ label: \"12:48\" }, { label: \"Unlock €4.99\" }, { label: \"18+ checked\", tone: \"ok\" }]} />}\n actions={\n <>\n <Button size=\"sm\" variant=\"secondary\" shape=\"cut\">Edit</Button>\n <Button size=\"sm\" variant=\"primary\" shape=\"cut\">Share</Button>\n </>\n }\n tabs={\n <Tabs.List aria-label=\"Video\">\n <Tabs.Trigger value=\"overview\">Overview</Tabs.Trigger>\n <Tabs.Trigger value=\"sales\">Sales</Tabs.Trigger>\n <Tabs.Trigger value=\"comments\">Comments</Tabs.Trigger>\n </Tabs.List>\n }\n aside={\n <div className=\"flex flex-col gap-4 rounded-xl border border-border-default bg-surface-1 p-4\">\n <Stat label=\"Unlocks\" value=\"146\" trend=\"up\" trendLabel=\"+12\" hint=\"7 days\" />\n <Stat label=\"Earned\" value=\"€653\" hint=\"after the 10 % fee\" />\n </div>\n }\n >\n <Tabs.Content value=\"overview\" className=\"text-sm leading-relaxed text-fg-secondary\">\n The second half of the night shoot in Old Montréal: the rooftop, the rain, the last take at dawn.\n </Tabs.Content>\n <Tabs.Content value=\"sales\" className=\"text-sm text-fg-secondary\">146 unlocks, 3 tips.</Tabs.Content>\n <Tabs.Content value=\"comments\" className=\"text-sm text-fg-secondary\">28 comments, 2 waiting for moderation.</Tabs.Content>\n </DetailLayout>\n </Tabs.Root>\n </div>\n );\n}\n"
124
127
  },
128
+ {
129
+ "name": "checkout",
130
+ "title": "Tip checkout",
131
+ "description": "Who is paid, the amount in one tap, an optional word, the total beside the pay button.",
132
+ "template": true,
133
+ "purpose": "act",
134
+ "why": "A person pays in a few taps and sees the exact total before committing.",
135
+ "path": "examples/detail-layout/checkout.tsx",
136
+ "module": "@krizaka/ui/registry/examples/detail-layout/checkout",
137
+ "code": "\"use client\";\n\nimport { Button } from \"@krizaka/ui/button\";\nimport { Chip } from \"@krizaka/ui/chip\";\nimport { DetailLayout } from \"@krizaka/ui/detail-layout\";\nimport { Field, Textarea } from \"@krizaka/ui/field\";\nimport { Stat } from \"@krizaka/ui/stat\";\nimport * as React from \"react\";\n\nconst AMOUNTS = [3, 5, 10, 20];\n\n// TEMPLATE — tipping a creator (Orochia): who is paid, the amount in one tap, an optional word, the summary beside the\n// action — under the form on a phone, where the pay button stays last. The total is always said before the button.\nexport default function DetailLayoutCheckout() {\n const [amount, setAmount] = React.useState(\"5\");\n return (\n <div className=\"brand-orochia w-full\">\n <DetailLayout\n back={<a href=\"#elena\">← Elena's page</a>}\n eyebrow=\"Tip\"\n title=\"Say thanks to Elena\"\n meta=\"She keeps 90 %. Paid out every Monday.\"\n aside={\n <div className=\"flex flex-col gap-4 rounded-xl border border-border-default bg-surface-1 p-4\">\n <Stat label=\"You pay\" value={`€${amount}`} hint=\"no fee added on top\" />\n <Button variant=\"primary\" shape=\"cut\" className=\"w-full\">{`Tip €${amount}`}</Button>\n </div>\n }\n >\n <form className=\"flex max-w-lg flex-col gap-5\" onSubmit={(e) => e.preventDefault()}>\n <Chip.Group type=\"single\" label=\"Amount\" value={amount} onValueChange={(v) => v && setAmount(v)} required>\n {AMOUNTS.map((a) => (\n <Chip key={a} value={String(a)}>{`€${a}`}</Chip>\n ))}\n </Chip.Group>\n <Field.Root>\n <Field.Label htmlFor=\"note\">A word for her (optional)</Field.Label>\n <Textarea id=\"note\" rows={3} placeholder=\"The rooftop scene was incredible.\" />\n </Field.Root>\n </form>\n </DetailLayout>\n </div>\n );\n}\n"
138
+ },
125
139
  {
126
140
  "name": "job",
127
141
  "title": "A job",
@@ -144,11 +158,11 @@
144
158
  },
145
159
  {
146
160
  "path": "detail-layout/meta.ts",
147
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Detail layout\",\n summary: \"One object's page: the way back, its media, name, status, facts and actions, its tabs, the content and a side panel that goes under the content on a phone.\",\n status: \"beta\",\n category: \"layouts\",\n platforms: \"web\",\n whenToUse: [\"A video, a job, an account, a pack, an order.\", \"Any object reached from a list.\"],\n whenNotToUse: [\n { when: \"For a list and its item side by side.\", use: \"split-view\" },\n { when: \"For a screen of figures.\", use: \"dashboard-layout\" },\n ],\n bestPractices: [\"The status beside the name, in words (a Badge), not only a colour.\", \"Two or three actions at most in the header; the rest in a DropdownMenu.\", \"Put the tabs' `Tabs.Root` around the layout so the content follows the tab.\"],\n accessibility: {\n keyboard: [],\n notes: [\"The name is the page's `h1`.\", \"The side panel is an `aside`; on a phone it comes after the content in reading order.\"],\n },\n related: [\"tabs\", \"badge\", \"split-view\"],\n web: {\n imports: [\"DetailLayout\"],\n examples: [\n { name: \"video\", title: \"A video\", description: \"Orochia's video page: thumbnail, status, facts, tabs and the sales beside.\", template: true },\n { name: \"job\", title: \"A job\", description: \"An engine job without tabs or side panel.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
161
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Detail layout\",\n summary: \"One object's page: the way back, its media, name, status, facts and actions, its tabs, the content and a side panel that goes under the content on a phone.\",\n status: \"beta\",\n category: \"layouts\",\n platforms: \"web\",\n whenToUse: [\"A video, a job, an account, a pack, an order.\", \"Any object reached from a list.\"],\n whenNotToUse: [\n { when: \"For a list and its item side by side.\", use: \"split-view\" },\n { when: \"For a screen of figures.\", use: \"dashboard-layout\" },\n ],\n bestPractices: [\"The status beside the name, in words (a Badge), not only a colour.\", \"Two or three actions at most in the header; the rest in a DropdownMenu.\", \"Put the tabs' `Tabs.Root` around the layout so the content follows the tab.\"],\n accessibility: {\n keyboard: [],\n notes: [\"The name is the page's `h1`.\", \"The side panel is an `aside`; on a phone it comes after the content in reading order.\"],\n },\n related: [\"tabs\", \"badge\", \"split-view\"],\n web: {\n imports: [\"DetailLayout\"],\n examples: [\n { name: \"video\", title: \"A video\", description: \"Orochia's video page: thumbnail, status, facts, tabs and the sales beside.\", template: true, purpose: \"show\", why: \"A viewer sees the object itself first, with its facts and the actions that concern it beside it.\" },\n { name: \"checkout\", title: \"Tip checkout\", description: \"Who is paid, the amount in one tap, an optional word, the total beside the pay button.\", template: true, purpose: \"act\", why: \"A person pays in a few taps and sees the exact total before committing.\" },\n { name: \"job\", title: \"A job\", description: \"An engine job without tabs or side panel.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
148
162
  },
149
163
  {
150
164
  "path": "meta.ts",
151
- "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"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 * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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"
165
+ "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\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 */\ninterface ExampleBase {\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/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: 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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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 /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\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"
152
166
  }
153
167
  ],
154
168
  "dependencies": [
@@ -5,6 +5,7 @@
5
5
  "summary": "A window over the page — centred, a bottom sheet or a side panel — and `AlertDialog` for a confirmation.",
6
6
  "description": "A window over the page — centred, a bottom sheet or a side panel — and `AlertDialog` for a confirmation.",
7
7
  "status": "stable",
8
+ "deprecated": null,
8
9
  "category": "overlays",
9
10
  "platforms": "both",
10
11
  "whenToUse": [
@@ -47,6 +48,7 @@
47
48
  }
48
49
  ],
49
50
  "notes": [
51
+ "Closing returns focus to what opened it — also when a plain button opened it (a controlled `open`), not only a `Dialog.Trigger`; your `onCloseAutoFocus` can still decide otherwise.",
50
52
  "Radix Dialog: `role=\"dialog\"` (or `alertdialog`), `aria-modal`, named by its title and described by its description.",
51
53
  "The focus moves into the dialog when it opens and returns to the trigger when it closes; the page behind does not scroll.",
52
54
  "`AlertDialog` keeps the focus on the cancel button first."
@@ -383,11 +385,11 @@
383
385
  "files": [
384
386
  {
385
387
  "path": "dialog/alert-dialog.tsx",
386
- "content": "// Client. A confirmation that interrupts: Radix AlertDialog (role=\"alertdialog\", no outside click, focus on Cancel).\n// `onConfirm` may be async: the dialog stays open with the confirm button loading until it settles, closes when it\n// resolves, and stays open when it rejects (the product shows the error).\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { Button } from \"../button/button\";\nimport { dialog } from \"./dialog\";\n\nconst s = dialog({ placement: \"center\", size: \"sm\" });\n\nexport type AlertDialogProps = {\n /** Open or closed (controlled), with `onOpenChange`. */\n open?: boolean;\n /** Open at first, uncontrolled. */\n defaultOpen?: boolean;\n /** Called when it opens or closes (never while `onConfirm` is pending). */\n onOpenChange?: (open: boolean) => void;\n /** The element that opens the dialog (rendered as is, `asChild`): a Button, an IconButton. */\n trigger?: React.ReactNode;\n /** The question asked (\"Delete this video?\"): the dialog's accessible name. */\n title: React.ReactNode;\n /** What the action does and what it costs: the dialog's description. */\n description?: React.ReactNode;\n /** More content between the description and the actions. */\n children?: React.ReactNode;\n /** The words of the confirm button — passed translated. */\n confirmLabel: string;\n /** The words of the cancel button (it takes the focus at open) — passed translated. */\n cancelLabel: string;\n /** `danger` for a destructive action (delete, leave, cancel a payment). */\n tone?: \"danger\" | \"primary\";\n /** Runs on confirm. A promise keeps the dialog open and the button loading until it settles. */\n onConfirm: () => void | Promise<unknown>;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n /** Classes of the dialog's content, merged last. */\n className?: string;\n};\n\nexport function AlertDialog({\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n trigger,\n title,\n description,\n children,\n confirmLabel,\n cancelLabel,\n tone = \"primary\",\n onConfirm,\n container,\n className,\n}: AlertDialogProps) {\n const [openState, setOpenState] = React.useState(defaultOpen);\n const [pending, setPending] = React.useState(false);\n const open = openProp ?? openState;\n\n const setOpen = (next: boolean) => {\n if (openProp === undefined) setOpenState(next);\n onOpenChange?.(next);\n };\n\n const confirm = async () => {\n setPending(true);\n try {\n await onConfirm();\n setOpen(false);\n } catch {\n // Stays open: the product reports the failure (a toast, an inline error) and the person may retry.\n } finally {\n setPending(false);\n }\n };\n\n return (\n <AlertDialogPrimitive.Root open={open} onOpenChange={(next) => (pending ? undefined : setOpen(next))}>\n {trigger && <AlertDialogPrimitive.Trigger asChild>{trigger}</AlertDialogPrimitive.Trigger>}\n <AlertDialogPrimitive.Portal container={container}>\n <AlertDialogPrimitive.Overlay className={s.overlay()} />\n <AlertDialogPrimitive.Content\n data-tone={tone}\n aria-busy={pending || undefined}\n className={s.content({ className })}\n onEscapeKeyDown={(event) => pending && event.preventDefault()}\n >\n <div className={s.header({ className: \"pr-5\" })}>\n <AlertDialogPrimitive.Title className={s.title()}>{title}</AlertDialogPrimitive.Title>\n {description && <AlertDialogPrimitive.Description className={s.description()}>{description}</AlertDialogPrimitive.Description>}\n </div>\n {children && <div className={s.body()}>{children}</div>}\n <div className={s.footer()}>\n <AlertDialogPrimitive.Cancel asChild>\n <Button variant=\"ghost\" disabled={pending}>\n {cancelLabel}\n </Button>\n </AlertDialogPrimitive.Cancel>\n <Button variant={tone === \"danger\" ? \"danger\" : \"primary\"} loading={pending} onClick={confirm}>\n {confirmLabel}\n </Button>\n </div>\n </AlertDialogPrimitive.Content>\n </AlertDialogPrimitive.Portal>\n </AlertDialogPrimitive.Root>\n );\n}\n"
388
+ "content": "// Client. A confirmation that interrupts: Radix AlertDialog (role=\"alertdialog\", no outside click, focus on Cancel).\n// `onConfirm` may be async: the dialog stays open with the confirm button loading until it settles, closes when it\n// resolves, and stays open when it rejects (the product shows the error).\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { Button } from \"../button/button\";\nimport { dialog } from \"./dialog\";\nimport { useReturnFocus } from \"./return-focus\";\n\nconst s = dialog({ placement: \"center\", size: \"sm\" });\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {\n return <AlertDialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\nexport type AlertDialogProps = {\n /** Open or closed (controlled), with `onOpenChange`. */\n open?: boolean;\n /** Open at first, uncontrolled. */\n defaultOpen?: boolean;\n /** Called when it opens or closes (never while `onConfirm` is pending). */\n onOpenChange?: (open: boolean) => void;\n /** The element that opens the dialog (rendered as is, `asChild`): a Button, an IconButton. */\n trigger?: React.ReactNode;\n /** The question asked (\"Delete this video?\"): the dialog's accessible name. */\n title: React.ReactNode;\n /** What the action does and what it costs: the dialog's description. */\n description?: React.ReactNode;\n /** More content between the description and the actions. */\n children?: React.ReactNode;\n /** The words of the confirm button — passed translated. */\n confirmLabel: string;\n /** The words of the cancel button (it takes the focus at open) — passed translated. */\n cancelLabel: string;\n /** `danger` for a destructive action (delete, leave, cancel a payment). */\n tone?: \"danger\" | \"primary\";\n /** Runs on confirm. A promise keeps the dialog open and the button loading until it settles. */\n onConfirm: () => void | Promise<unknown>;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n /** Classes of the dialog's content, merged last. */\n className?: string;\n};\n\nexport function AlertDialog({\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n trigger,\n title,\n description,\n children,\n confirmLabel,\n cancelLabel,\n tone = \"primary\",\n onConfirm,\n container,\n className,\n}: AlertDialogProps) {\n const [openState, setOpenState] = React.useState(defaultOpen);\n const [pending, setPending] = React.useState(false);\n const open = openProp ?? openState;\n\n const setOpen = (next: boolean) => {\n if (openProp === undefined) setOpenState(next);\n onOpenChange?.(next);\n };\n\n const confirm = async () => {\n setPending(true);\n try {\n await onConfirm();\n setOpen(false);\n } catch {\n // Stays open: the product reports the failure (a toast, an inline error) and the person may retry.\n } finally {\n setPending(false);\n }\n };\n\n return (\n <AlertDialogPrimitive.Root open={open} onOpenChange={(next) => (pending ? undefined : setOpen(next))}>\n {trigger && <AlertDialogPrimitive.Trigger asChild>{trigger}</AlertDialogPrimitive.Trigger>}\n <AlertDialogPrimitive.Portal container={container}>\n <AlertDialogPrimitive.Overlay className={s.overlay()} />\n <ReturnFocusContent\n data-tone={tone}\n aria-busy={pending || undefined}\n className={s.content({ className })}\n onEscapeKeyDown={(event) => pending && event.preventDefault()}\n >\n <div className={s.header({ className: \"pr-5\" })}>\n <AlertDialogPrimitive.Title className={s.title()}>{title}</AlertDialogPrimitive.Title>\n {description && <AlertDialogPrimitive.Description className={s.description()}>{description}</AlertDialogPrimitive.Description>}\n </div>\n {children && <div className={s.body()}>{children}</div>}\n <div className={s.footer()}>\n <AlertDialogPrimitive.Cancel asChild>\n <Button variant=\"ghost\" disabled={pending}>\n {cancelLabel}\n </Button>\n </AlertDialogPrimitive.Cancel>\n <Button variant={tone === \"danger\" ? \"danger\" : \"primary\"} loading={pending} onClick={confirm}>\n {confirmLabel}\n </Button>\n </div>\n </ReturnFocusContent>\n </AlertDialogPrimitive.Portal>\n </AlertDialogPrimitive.Root>\n );\n}\n"
387
389
  },
388
390
  {
389
391
  "path": "dialog/dialog.tsx",
390
- "content": "// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <DialogPrimitive.Content\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n"
392
+ "content": "// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\nimport { useReturnFocus } from \"./return-focus\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return <DialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <ReturnFocusContent\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </ReturnFocusContent>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n"
391
393
  },
392
394
  {
393
395
  "path": "dialog/index.ts",
@@ -395,11 +397,15 @@
395
397
  },
396
398
  {
397
399
  "path": "dialog/meta.ts",
398
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Dialog\",\n summary: \"A window over the page — centred, a bottom sheet or a side panel — and `AlertDialog` for a confirmation.\",\n status: \"stable\",\n category: \"overlays\",\n platforms: \"both\",\n whenToUse: [\n \"For a short task that must be finished or cancelled before going back: invite someone, edit a title.\",\n \"`AlertDialog` to confirm a destructive or irreversible action, with an async `onConfirm`.\",\n \"`Sheet` (bottom) on phones, `placement=\\\"right\\\"` for a side panel of details.\",\n \"A gate the user must answer (age, terms): `hideClose` and `dismissible={false}`.\",\n ],\n whenNotToUse: [\n { when: \"For information next to an element, without blocking the page.\", use: \"popover\" },\n { when: \"For a message that needs no answer.\", use: \"toast\" },\n { when: \"For a long form or a flow of several steps: give it its own page.\" },\n { when: \"To confirm the deletion of one item in place.\", use: \"confirm-button\" },\n ],\n bestPractices: [\n \"Always a `Dialog.Title`; a `Dialog.Description` when the title is not enough.\",\n \"Name the buttons by what they do (“Send the invitation”), the cancel one by what it keeps (“Keep it”).\",\n \"Pass `closeLabel` translated: it names the close button.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Esc\", action: \"Closes the dialog (unless `dismissible={false}`).\" },\n { keys: \"Tab / Shift+Tab\", action: \"Move within the dialog: the focus is trapped while it is open.\" },\n ],\n notes: [\n \"Radix Dialog: `role=\\\"dialog\\\"` (or `alertdialog`), `aria-modal`, named by its title and described by its description.\",\n \"The focus moves into the dialog when it opens and returns to the trigger when it closes; the page behind does not scroll.\",\n \"`AlertDialog` keeps the focus on the cancel button first.\",\n ],\n },\n related: [\"popover\", \"confirm-button\", \"command\", \"toast\"],\n web: {\n imports: [\"Dialog\", \"Sheet\", \"AlertDialog\"],\n examples: [\n { name: \"center\", title: \"Centred\", description: \"A short form, `size=\\\"md\\\"`.\" },\n { name: \"bottom\", title: \"Bottom sheet\", description: \"`placement=\\\"bottom\\\"`: a sheet on a phone, centred from `sm` up.\" },\n { name: \"right\", title: \"Side panel\", description: \"`placement=\\\"right\\\"`: full height, for details.\" },\n { name: \"left\", title: \"Navigation drawer\", description: \"`placement=\\\"left\\\"`: full height from the left, for an app's navigation on a phone.\" },\n { name: \"sheet-form\", title: \"Sheet with a form\", description: \"`Sheet`, `size=\\\"lg\\\"`.\" },\n { name: \"alert-destructive\", title: \"Alert dialog\", description: \"`AlertDialog tone=\\\"danger\\\"` with an async `onConfirm`.\" },\n { name: \"gate\", title: \"Gate\", description: \"No close button: it closes only through its actions.\" },\n ],\n },\n native: {\n imports: [\"Sheet\"],\n differences: [\n \"One component, `Sheet`: a bottom sheet on React Native's `Modal` (focus kept inside, the back button and the scrim close it), its grip on top, rising with the theme's ease.\",\n \"It holds what a phone shows over a screen: a choice (`Select`, `Combobox` use it), the details of a pin on a map, a short form.\",\n ],\n examples: [{ name: \"sheet\", title: \"Bottom sheet\", description: \"The details of a pick-up point over the dimmed screen.\" }],\n },\n} satisfies ComponentMeta;\n"
400
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Dialog\",\n summary: \"A window over the page — centred, a bottom sheet or a side panel — and `AlertDialog` for a confirmation.\",\n status: \"stable\",\n category: \"overlays\",\n platforms: \"both\",\n whenToUse: [\n \"For a short task that must be finished or cancelled before going back: invite someone, edit a title.\",\n \"`AlertDialog` to confirm a destructive or irreversible action, with an async `onConfirm`.\",\n \"`Sheet` (bottom) on phones, `placement=\\\"right\\\"` for a side panel of details.\",\n \"A gate the user must answer (age, terms): `hideClose` and `dismissible={false}`.\",\n ],\n whenNotToUse: [\n { when: \"For information next to an element, without blocking the page.\", use: \"popover\" },\n { when: \"For a message that needs no answer.\", use: \"toast\" },\n { when: \"For a long form or a flow of several steps: give it its own page.\" },\n { when: \"To confirm the deletion of one item in place.\", use: \"confirm-button\" },\n ],\n bestPractices: [\n \"Always a `Dialog.Title`; a `Dialog.Description` when the title is not enough.\",\n \"Name the buttons by what they do (“Send the invitation”), the cancel one by what it keeps (“Keep it”).\",\n \"Pass `closeLabel` translated: it names the close button.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Esc\", action: \"Closes the dialog (unless `dismissible={false}`).\" },\n { keys: \"Tab / Shift+Tab\", action: \"Move within the dialog: the focus is trapped while it is open.\" },\n ],\n notes: [\"Closing returns focus to what opened it — also when a plain button opened it (a controlled `open`), not only a `Dialog.Trigger`; your `onCloseAutoFocus` can still decide otherwise.\", \n \"Radix Dialog: `role=\\\"dialog\\\"` (or `alertdialog`), `aria-modal`, named by its title and described by its description.\",\n \"The focus moves into the dialog when it opens and returns to the trigger when it closes; the page behind does not scroll.\",\n \"`AlertDialog` keeps the focus on the cancel button first.\",\n ],\n },\n related: [\"popover\", \"confirm-button\", \"command\", \"toast\"],\n web: {\n imports: [\"Dialog\", \"Sheet\", \"AlertDialog\"],\n examples: [\n { name: \"center\", title: \"Centred\", description: \"A short form, `size=\\\"md\\\"`.\" },\n { name: \"bottom\", title: \"Bottom sheet\", description: \"`placement=\\\"bottom\\\"`: a sheet on a phone, centred from `sm` up.\" },\n { name: \"right\", title: \"Side panel\", description: \"`placement=\\\"right\\\"`: full height, for details.\" },\n { name: \"left\", title: \"Navigation drawer\", description: \"`placement=\\\"left\\\"`: full height from the left, for an app's navigation on a phone.\" },\n { name: \"sheet-form\", title: \"Sheet with a form\", description: \"`Sheet`, `size=\\\"lg\\\"`.\" },\n { name: \"alert-destructive\", title: \"Alert dialog\", description: \"`AlertDialog tone=\\\"danger\\\"` with an async `onConfirm`.\" },\n { name: \"gate\", title: \"Gate\", description: \"No close button: it closes only through its actions.\" },\n ],\n },\n native: {\n imports: [\"Sheet\"],\n differences: [\n \"One component, `Sheet`: a bottom sheet on React Native's `Modal` (focus kept inside, the back button and the scrim close it), its grip on top, rising with the theme's ease.\",\n \"It holds what a phone shows over a screen: a choice (`Select`, `Combobox` use it), the details of a pin on a map, a short form.\",\n ],\n examples: [{ name: \"sheet\", title: \"Bottom sheet\", description: \"The details of a pick-up point over the dimmed screen.\" }],\n },\n} satisfies ComponentMeta;\n"
401
+ },
402
+ {
403
+ "path": "dialog/return-focus.ts",
404
+ "content": "// Client. WCAG 2.4.3: when a dialog closes, focus goes back to what opened it. Radix only does it for `Dialog.Trigger`;\n// most app dialogs are controlled (`open` / `onOpenChange`) and opened by a plain button, so on Escape focus fell to\n// <body>. The content remembers the focused element when it mounts (it is still the opener: Radix moves focus in an\n// effect) and gives it back on close, unless the consumer's `onCloseAutoFocus` prevented the default.\nimport * as React from \"react\";\n\n/** The handler for `onCloseAutoFocus`: yours first, then focus back on the opener if it is still on the page. */\nexport function useReturnFocus(onCloseAutoFocus?: (event: Event) => void) {\n const [opener] = React.useState<Element | null>(() => (typeof document === \"undefined\" ? null : document.activeElement));\n return React.useCallback(\n (event: Event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n if (opener instanceof HTMLElement && opener !== document.body && opener.isConnected) {\n event.preventDefault();\n opener.focus();\n }\n },\n [opener, onCloseAutoFocus],\n );\n}\n"
399
405
  },
400
406
  {
401
407
  "path": "meta.ts",
402
- "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"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 * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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"
408
+ "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 | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\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/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\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 */\ninterface ExampleBase {\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/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: 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 /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\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 /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\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"
403
409
  }
404
410
  ],
405
411
  "dependencies": [