@krizaka/ui 3.51.0 → 3.53.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 (251) hide show
  1. package/dist/action-bar.js +1 -1
  2. package/dist/argument-card.js +4 -4
  3. package/dist/blocks.css +293 -0
  4. package/dist/chapter.d.ts +89 -0
  5. package/dist/chapter.js +123 -0
  6. package/dist/chapter.js.map +1 -0
  7. package/dist/choice-grid.js +1 -1
  8. package/dist/choice-grid.js.map +1 -1
  9. package/dist/{chunk-IBDTAMPF.js → chunk-7JG3VR4C.js} +2 -2
  10. package/dist/{chunk-C7LTF5CC.js → chunk-ENOS6ETI.js} +2 -2
  11. package/dist/{chunk-C7LTF5CC.js.map → chunk-ENOS6ETI.js.map} +1 -1
  12. package/dist/{chunk-2AGSO3WH.js → chunk-FVGFPB73.js} +2 -2
  13. package/dist/{chunk-2AGSO3WH.js.map → chunk-FVGFPB73.js.map} +1 -1
  14. package/dist/chunk-YATTFMDV.js +45 -0
  15. package/dist/chunk-YATTFMDV.js.map +1 -0
  16. package/dist/cover-hero.js +1 -1
  17. package/dist/glossary-term.d.ts +12 -0
  18. package/dist/glossary-term.js +16 -0
  19. package/dist/glossary-term.js.map +1 -0
  20. package/dist/glossary.d.ts +20 -0
  21. package/dist/glossary.js +17 -0
  22. package/dist/glossary.js.map +1 -0
  23. package/dist/index.js +7 -7
  24. package/dist/kpi-explained.d.ts +66 -0
  25. package/dist/kpi-explained.js +84 -0
  26. package/dist/kpi-explained.js.map +1 -0
  27. package/dist/link-card.js +2 -2
  28. package/dist/media-grid.js +1 -1
  29. package/dist/media-grid.js.map +1 -1
  30. package/dist/next-actions.d.ts +27 -0
  31. package/dist/next-actions.js +27 -0
  32. package/dist/next-actions.js.map +1 -0
  33. package/dist/page-hero.js +1 -1
  34. package/dist/page-index.js +1 -1
  35. package/dist/service-card.js +1 -1
  36. package/dist/slot-picker.js +1 -1
  37. package/dist/text-style/server.d.ts +63 -0
  38. package/dist/text-style/server.js +109 -0
  39. package/dist/text-style/server.js.map +1 -0
  40. package/dist/text-style.d.ts +149 -0
  41. package/dist/text-style.js +399 -0
  42. package/dist/text-style.js.map +1 -0
  43. package/dist/tooltip.js +5 -37
  44. package/dist/tooltip.js.map +1 -1
  45. package/package.json +86 -8
  46. package/registry/account-menu.json +1 -1
  47. package/registry/account-settings.json +1 -1
  48. package/registry/achievement.json +1 -1
  49. package/registry/action-bar.json +2 -2
  50. package/registry/activity-feed.json +1 -1
  51. package/registry/agenda.json +1 -1
  52. package/registry/alert.json +1 -1
  53. package/registry/anatomy-plate.json +1 -1
  54. package/registry/app-header.json +1 -1
  55. package/registry/app-shell.json +1 -1
  56. package/registry/argument-card.json +2 -2
  57. package/registry/audio-player.json +1 -1
  58. package/registry/auth-layout.json +1 -1
  59. package/registry/avatar.json +1 -1
  60. package/registry/badge.json +1 -1
  61. package/registry/bird-plate.json +1 -1
  62. package/registry/button.json +1 -1
  63. package/registry/captcha.json +1 -1
  64. package/registry/card-grid.json +1 -1
  65. package/registry/card.json +1 -1
  66. package/registry/catalogue-page.json +1 -1
  67. package/registry/changelog-page.json +1 -1
  68. package/registry/chapter.json +478 -0
  69. package/registry/chat.json +1 -1
  70. package/registry/checkbox.json +1 -1
  71. package/registry/chip-link.json +1 -1
  72. package/registry/chip-row.json +1 -1
  73. package/registry/chip.json +1 -1
  74. package/registry/choice-grid.json +2 -2
  75. package/registry/code-block.json +1 -1
  76. package/registry/combobox.json +1 -1
  77. package/registry/command.json +1 -1
  78. package/registry/comparison-table.json +1 -1
  79. package/registry/component-page.json +1 -1
  80. package/registry/confirm-button.json +1 -1
  81. package/registry/contact-page.json +1 -1
  82. package/registry/continue-prompt.json +1 -1
  83. package/registry/countdown.json +1 -1
  84. package/registry/cover-hero.json +2 -2
  85. package/registry/cta-panel.json +1 -1
  86. package/registry/dashboard-layout.json +1 -1
  87. package/registry/data-table.json +1 -1
  88. package/registry/date-picker.json +2 -2
  89. package/registry/decision-list.json +1 -1
  90. package/registry/description-list.json +1 -1
  91. package/registry/detail-layout.json +1 -1
  92. package/registry/dialog.json +1 -1
  93. package/registry/docs-article-page.json +1 -1
  94. package/registry/docs-hub-page.json +1 -1
  95. package/registry/docs-layout.json +1 -1
  96. package/registry/docs-nav.json +1 -1
  97. package/registry/document-viewer.json +1 -1
  98. package/registry/drop-overlay.json +1 -1
  99. package/registry/dropdown-menu.json +1 -1
  100. package/registry/elephant.json +2 -2
  101. package/registry/empty-state.json +1 -1
  102. package/registry/engine-flow.json +1 -1
  103. package/registry/error-page.json +1 -1
  104. package/registry/examples/chapter/detail.d.ts +4 -0
  105. package/registry/examples/chapter/detail.js +21 -0
  106. package/registry/examples/chapter/detail.tsx +16 -0
  107. package/registry/examples/chapter/foot.d.ts +4 -0
  108. package/registry/examples/chapter/foot.js +20 -0
  109. package/registry/examples/chapter/foot.tsx +15 -0
  110. package/registry/examples/chapter/head.d.ts +4 -0
  111. package/registry/examples/chapter/head.js +22 -0
  112. package/registry/examples/chapter/head.tsx +17 -0
  113. package/registry/examples/chapter/index.d.ts +4 -0
  114. package/registry/examples/chapter/index.js +34 -0
  115. package/registry/examples/chapter/index.tsx +30 -0
  116. package/registry/examples/chapter/reading-page.d.ts +4 -0
  117. package/registry/examples/chapter/reading-page.js +34 -0
  118. package/registry/examples/chapter/reading-page.tsx +25 -0
  119. package/registry/examples/glossary/shop-terms.d.ts +4 -0
  120. package/registry/examples/glossary/shop-terms.js +11 -0
  121. package/registry/examples/glossary/shop-terms.tsx +5 -0
  122. package/registry/examples/glossary-term/inline.d.ts +4 -0
  123. package/registry/examples/glossary-term/inline.js +15 -0
  124. package/registry/examples/glossary-term/inline.tsx +5 -0
  125. package/registry/examples/kpi-explained/founder-cockpit.d.ts +4 -0
  126. package/registry/examples/kpi-explained/founder-cockpit.js +11 -0
  127. package/registry/examples/kpi-explained/founder-cockpit.tsx +5 -0
  128. package/registry/examples/kpi-explained/salon-month.d.ts +4 -0
  129. package/registry/examples/kpi-explained/salon-month.js +11 -0
  130. package/registry/examples/kpi-explained/salon-month.tsx +5 -0
  131. package/registry/examples/next-actions/shop-owner.d.ts +4 -0
  132. package/registry/examples/next-actions/shop-owner.js +11 -0
  133. package/registry/examples/next-actions/shop-owner.tsx +5 -0
  134. package/registry/examples/text-style/compact-toolbar.d.ts +4 -0
  135. package/registry/examples/text-style/compact-toolbar.js +28 -0
  136. package/registry/examples/text-style/compact-toolbar.tsx +21 -0
  137. package/registry/examples/text-style/product-price.d.ts +4 -0
  138. package/registry/examples/text-style/product-price.js +26 -0
  139. package/registry/examples/text-style/product-price.tsx +17 -0
  140. package/registry/examples/text-style/storefront-title.d.ts +4 -0
  141. package/registry/examples/text-style/storefront-title.js +36 -0
  142. package/registry/examples/text-style/storefront-title.tsx +22 -0
  143. package/registry/field.json +1 -1
  144. package/registry/file-upload.json +2 -2
  145. package/registry/filter-bar.json +1 -1
  146. package/registry/flock.json +1 -1
  147. package/registry/footnote.json +1 -1
  148. package/registry/force-update-screen.json +1 -1
  149. package/registry/form.json +2 -2
  150. package/registry/gallery-page.json +1 -1
  151. package/registry/genui.json +2 -2
  152. package/registry/glossary-term.json +144 -0
  153. package/registry/glossary.json +137 -0
  154. package/registry/glow-card.json +1 -1
  155. package/registry/goal-gauge.json +1 -1
  156. package/registry/how-it-works-page.json +1 -1
  157. package/registry/image-cropper.json +1 -1
  158. package/registry/index.json +199 -11
  159. package/registry/iso.json +1 -1
  160. package/registry/kanban.json +1 -1
  161. package/registry/kbd.json +1 -1
  162. package/registry/kpi-explained.json +364 -0
  163. package/registry/landing-page.json +1 -1
  164. package/registry/landscape.json +1 -1
  165. package/registry/language-switch.json +1 -1
  166. package/registry/legal-page.json +1 -1
  167. package/registry/link-card.json +2 -2
  168. package/registry/list.json +1 -1
  169. package/registry/loader.json +1 -1
  170. package/registry/longform-page.json +1 -1
  171. package/registry/maintenance-screen.json +1 -1
  172. package/registry/map.json +2 -2
  173. package/registry/markdown.json +1 -1
  174. package/registry/marketing-layout.json +1 -1
  175. package/registry/marquee.json +1 -1
  176. package/registry/mascot-quote.json +1 -1
  177. package/registry/mascot-state.json +1 -1
  178. package/registry/mascots.json +1 -1
  179. package/registry/match-dialog.json +1 -1
  180. package/registry/media-capture.json +1 -1
  181. package/registry/media-gallery.json +1 -1
  182. package/registry/media-grid.json +2 -2
  183. package/registry/meta-row.json +1 -1
  184. package/registry/meter.json +1 -1
  185. package/registry/nav-bar.json +2 -2
  186. package/registry/next-actions.json +142 -0
  187. package/registry/number-ticker.json +1 -1
  188. package/registry/opening-hours.json +1 -1
  189. package/registry/page-header.json +1 -1
  190. package/registry/page-hero.json +2 -2
  191. package/registry/page-index.json +2 -2
  192. package/registry/page.json +1 -1
  193. package/registry/pagination.json +1 -1
  194. package/registry/photo-grid.json +1 -1
  195. package/registry/pointer-light.json +1 -1
  196. package/registry/popover.json +1 -1
  197. package/registry/pricing-page.json +1 -1
  198. package/registry/product-page.json +1 -1
  199. package/registry/profile-card.json +1 -1
  200. package/registry/progress.json +1 -1
  201. package/registry/promise-card.json +1 -1
  202. package/registry/radio-group.json +1 -1
  203. package/registry/rail-nav.json +2 -2
  204. package/registry/recipes.json +1 -1
  205. package/registry/router.json +1 -1
  206. package/registry/scroll-stage.json +1 -1
  207. package/registry/search-field.json +1 -1
  208. package/registry/section-backdrop.json +1 -1
  209. package/registry/separator.json +1 -1
  210. package/registry/service-card.json +2 -2
  211. package/registry/settings-layout.json +1 -1
  212. package/registry/showcase.json +1 -1
  213. package/registry/site-footer.json +2 -2
  214. package/registry/sites.json +1 -1
  215. package/registry/skeleton.json +1 -1
  216. package/registry/skip-link.json +1 -1
  217. package/registry/slider.json +1 -1
  218. package/registry/slot-picker.json +2 -2
  219. package/registry/slot.json +1 -1
  220. package/registry/specimen-tag.json +1 -1
  221. package/registry/spinner.json +1 -1
  222. package/registry/split-view.json +1 -1
  223. package/registry/stage-banner.json +1 -1
  224. package/registry/stat.json +1 -1
  225. package/registry/status-page.json +1 -1
  226. package/registry/step-progress.json +1 -1
  227. package/registry/step-showcase.json +1 -1
  228. package/registry/stepper.json +1 -1
  229. package/registry/sticky-action.json +1 -1
  230. package/registry/swipe-deck.json +1 -1
  231. package/registry/switch.json +1 -1
  232. package/registry/tabs.json +1 -1
  233. package/registry/tag-input.json +1 -1
  234. package/registry/tag-selector.json +1 -1
  235. package/registry/taped-photo.json +1 -1
  236. package/registry/terminal-replay.json +1 -1
  237. package/registry/text-style.json +378 -0
  238. package/registry/theme.json +1 -1
  239. package/registry/toast.json +1 -1
  240. package/registry/toc.json +1 -1
  241. package/registry/tooltip.json +1 -1
  242. package/registry/tree-view.json +1 -1
  243. package/registry/update-prompt.json +1 -1
  244. package/registry/upload-queue.json +1 -1
  245. package/registry/verified-badge.json +1 -1
  246. package/registry/vertical-video.json +2 -2
  247. package/registry/video-player.json +1 -1
  248. package/registry/video-trimmer.json +1 -1
  249. package/registry/virtual.json +1 -1
  250. package/registry/waveform.json +1 -1
  251. /package/dist/{chunk-IBDTAMPF.js.map → chunk-7JG3VR4C.js.map} +0 -0
@@ -0,0 +1,478 @@
1
+ {
2
+ "name": "chapter",
3
+ "type": "primitive",
4
+ "title": "Chapter",
5
+ "summary": "The chapters of a story on the Cahier's ruled lines (ADR-004): `ChapterIndex` (one ruled line per chapter, the number in the margin, the serif title the link, the latest marked « New »), `ChapterHead` and `ChapterFoot` for the top and bottom of a chapter's page. No boxes.",
6
+ "description": "The chapters of a story on the Cahier's ruled lines (ADR-004): `ChapterIndex` (one ruled line per chapter, the number in the margin, the serif title the link, the latest marked « New »), `ChapterHead` and `ChapterFoot` for the top and bottom of a chapter's page. No boxes.",
7
+ "status": "beta",
8
+ "deprecated": null,
9
+ "category": "blocks",
10
+ "platforms": "web",
11
+ "parity": {
12
+ "web": {
13
+ "state": "yes"
14
+ },
15
+ "native": {
16
+ "state": "n/a",
17
+ "reason": "A long read's index and page frame on a public site; an app lists its items with List.",
18
+ "use": "list"
19
+ },
20
+ "desktop": {
21
+ "state": "yes"
22
+ }
23
+ },
24
+ "whenToUse": [
25
+ "The chapters of a story or a series, read in order: a timeline you follow, never a feed of cards.",
26
+ "The head of a chapter's page (its number, title, period and reading time, the standfirst once) and its foot (previous, next, back to the story)."
27
+ ],
28
+ "whenNotToUse": [
29
+ {
30
+ "when": "Entries the reader browses or picks among, with a picture or an argument each.",
31
+ "use": "link-card"
32
+ },
33
+ {
34
+ "when": "The sections of the page being read.",
35
+ "use": "toc"
36
+ },
37
+ {
38
+ "when": "A documentation's index with page numbers.",
39
+ "use": "page-index"
40
+ }
41
+ ],
42
+ "bestPractices": [
43
+ "Short numbers in the margin (`01`, `§ 1`): the margin is 3 rem wide; the word « Chapter » belongs to the page's heading.",
44
+ "One line per chapter: the title and its period. Say what was faced and what was built once, on the chapter's page (`ChapterHead`'s standfirst); `detail` is for an index that truly needs a second line.",
45
+ "Mark only the latest chapter `isNew`, and only while it is new.",
46
+ "`ChapterIndex` rules its own lines so every row lands on one: put it in a section without `paper`, or pass `paper={false}` when the list starts on the page's module.",
47
+ "Give `ChapterHead` the two `versusLabels`: the arrow of the standfirst is drawn, a screen reader hears the labels.",
48
+ "Pass the router link as `linkAs` so internal chapters prefetch."
49
+ ],
50
+ "accessibility": {
51
+ "keyboard": [
52
+ {
53
+ "keys": "Tab",
54
+ "action": "Moves from one chapter's title to the next; the focus ring outlines the whole row."
55
+ }
56
+ ],
57
+ "notes": [
58
+ "`ChapterIndex` is an ordered list (`role=\"list\"`, kept by VoiceOver without bullets) of links named by their title; the « New » mark is read inside the link, the number and the period around it.",
59
+ "The whole row is the link's hit area (a stretched pseudo-element); only the title and the mark are its name.",
60
+ "`ChapterHead` renders a real `h1` (or `h2` with `level={2}`); the meta line's separator and the standfirst's arrow are hidden from assistive technology.",
61
+ "`ChapterFoot` is a `nav` named by `label` (« Chapters »); its links carry `rel=\"prev\"` and `rel=\"next\"`, their arrows are decorative.",
62
+ "Nothing moves: hover and focus underline the title in the hue, also under reduced motion."
63
+ ]
64
+ },
65
+ "related": [
66
+ "page",
67
+ "page-index",
68
+ "toc",
69
+ "footnote"
70
+ ],
71
+ "web": {
72
+ "entry": "@krizaka/ui/chapter",
73
+ "import": "import { ChapterIndex, ChapterHead, ChapterFoot } from \"@krizaka/ui/chapter\";",
74
+ "props": [
75
+ {
76
+ "component": "LinkComponent",
77
+ "description": "The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children.",
78
+ "props": []
79
+ },
80
+ {
81
+ "component": "ChapterFoot",
82
+ "description": "The foot of a chapter: the previous and the next on the ruled lines, then one line back to the story.",
83
+ "props": [
84
+ {
85
+ "name": "back",
86
+ "type": "{ href: string; label: ReactNode; }",
87
+ "default": null,
88
+ "description": "The line back to the story: `{ href: \"/\", label: \"Back to the story\" }`.",
89
+ "required": false
90
+ },
91
+ {
92
+ "name": "label",
93
+ "type": "string",
94
+ "default": "Chapters",
95
+ "description": "The accessible name of the navigation.",
96
+ "required": false
97
+ },
98
+ {
99
+ "name": "labels",
100
+ "type": "{ previous: ReactNode; next: ReactNode; }",
101
+ "default": "{ previous: \"Previous\", next: \"Next\" }",
102
+ "description": "The quiet words beside each link.",
103
+ "required": false
104
+ },
105
+ {
106
+ "name": "linkAs",
107
+ "type": "LinkComponent",
108
+ "default": null,
109
+ "description": "The app's router link (Next's `Link`).",
110
+ "required": false
111
+ },
112
+ {
113
+ "name": "next",
114
+ "type": "ChapterLink",
115
+ "default": null,
116
+ "description": "The chapter after this one (none on the latest).",
117
+ "required": false
118
+ },
119
+ {
120
+ "name": "paper",
121
+ "type": "ChapterPaper",
122
+ "default": "lines",
123
+ "description": "The ruling under the links.",
124
+ "required": false
125
+ },
126
+ {
127
+ "name": "previous",
128
+ "type": "ChapterLink",
129
+ "default": null,
130
+ "description": "The chapter before this one (none on the first).",
131
+ "required": false
132
+ }
133
+ ]
134
+ },
135
+ {
136
+ "component": "ChapterHead",
137
+ "description": "The head of a chapter: the number in the margin, the title, the period and the reading time, the standfirst once.",
138
+ "props": [
139
+ {
140
+ "name": "number",
141
+ "type": "ReactNode",
142
+ "default": null,
143
+ "description": "The chapter's number, hung in the margin: `01`, `Chapitre 1`.",
144
+ "required": true
145
+ },
146
+ {
147
+ "name": "title",
148
+ "type": "ReactNode",
149
+ "default": null,
150
+ "description": "The title: the page's `h1`.",
151
+ "required": true
152
+ },
153
+ {
154
+ "name": "built",
155
+ "type": "ReactNode",
156
+ "default": null,
157
+ "description": "What was built: its second half, after the arrow.",
158
+ "required": false
159
+ },
160
+ {
161
+ "name": "faced",
162
+ "type": "ReactNode",
163
+ "default": null,
164
+ "description": "What was faced: the first half of the standfirst.",
165
+ "required": false
166
+ },
167
+ {
168
+ "name": "level",
169
+ "type": "1 | 2",
170
+ "default": "1",
171
+ "description": "The level of the title.",
172
+ "required": false
173
+ },
174
+ {
175
+ "name": "period",
176
+ "type": "ReactNode",
177
+ "default": null,
178
+ "description": "When it happened: `janv. – mars 2026`.",
179
+ "required": false
180
+ },
181
+ {
182
+ "name": "readingTime",
183
+ "type": "ReactNode",
184
+ "default": null,
185
+ "description": "How long it reads: `8 min`.",
186
+ "required": false
187
+ },
188
+ {
189
+ "name": "versusLabels",
190
+ "type": "readonly [string, string]",
191
+ "default": null,
192
+ "description": "The words a screen reader says before each half (the arrow is drawn, not read): `[\"What I faced\", \"What we built\"]`.",
193
+ "required": false
194
+ }
195
+ ]
196
+ },
197
+ {
198
+ "component": "ChapterIndex",
199
+ "description": "The chapters of a story as a sober timeline: one ruled line each, the number in the margin, the title the link.",
200
+ "props": [
201
+ {
202
+ "name": "chapters",
203
+ "type": "readonly ChapterEntry[]",
204
+ "default": null,
205
+ "description": "The chapters, in reading order.",
206
+ "required": true
207
+ },
208
+ {
209
+ "name": "label",
210
+ "type": "string",
211
+ "default": null,
212
+ "description": "The accessible name of the list, when no heading names it (`aria-labelledby`).",
213
+ "required": false
214
+ },
215
+ {
216
+ "name": "linkAs",
217
+ "type": "LinkComponent",
218
+ "default": null,
219
+ "description": "The app's router link (Next's `Link`), so internal chapters prefetch.",
220
+ "required": false
221
+ },
222
+ {
223
+ "name": "newLabel",
224
+ "type": "ReactNode",
225
+ "default": "New",
226
+ "description": "The word of the latest chapter's mark.",
227
+ "required": false
228
+ },
229
+ {
230
+ "name": "paper",
231
+ "type": "ChapterPaper",
232
+ "default": "lines",
233
+ "description": "The ruling under the rows. `false` when the page is ruled already and the list starts on its module.",
234
+ "required": false
235
+ }
236
+ ]
237
+ }
238
+ ],
239
+ "examples": [
240
+ {
241
+ "name": "index",
242
+ "title": "The chapters of a story",
243
+ "description": "Eight chapters in reading order, one ruled line each, the latest marked « Nouveau ».",
244
+ "path": "examples/chapter/index.tsx",
245
+ "module": "@krizaka/ui/registry/examples/chapter/index",
246
+ "code": "import { ChapterIndex } from \"@krizaka/ui/chapter\";\n\nconst chapters = [\n [\"Pourquoi je construis Krizaka à Montréal\", \"16 oct.\"],\n [\"Un moteur d'IA sur un portable\", \"16 – 17 oct.\"],\n [\"Ce que coûtent une image et une vidéo : mes factures\", \"15 – 21 oct.\"],\n [\"D'un clip à un film : ce que nous avons raté d'abord\", \"17 – 21 oct.\"],\n [\"Une seule interface, du web au Mac\", \"17 – 21 oct.\"],\n [\"Les premiers produits : Yomizaka et Akinaza\", \"17 – 23 oct.\"],\n [\"Un résultat vide n'est pas un succès\", \"16 – 23 oct.\"],\n [\"Construire presque seul, avec des agents\", \"12 – 23 oct.\"],\n] as const;\n\n// The chapters of a story in reading order, one ruled line each: the number in the margin, the serif title is the link,\n// the period quiet on the right. The latest wears a small « Nouveau » in the hue.\nexport default function ChapterIndexStory() {\n return (\n <ChapterIndex\n label=\"Les chapitres\"\n newLabel=\"Nouveau\"\n chapters={chapters.map(([title, period], i) => ({\n number: String(i + 1).padStart(2, \"0\"),\n title,\n period,\n href: `#chapitre-${i + 1}`,\n isNew: i === chapters.length - 1,\n }))}\n />\n );\n}\n"
247
+ },
248
+ {
249
+ "name": "detail",
250
+ "title": "With a second line",
251
+ "description": "The optional `detail`: what was faced → what was built, on the next ruled line.",
252
+ "path": "examples/chapter/detail.tsx",
253
+ "module": "@krizaka/ui/registry/examples/chapter/detail",
254
+ "code": "import { ChapterIndex } from \"@krizaka/ui/chapter\";\n\n// The optional second line (`detail`): what was faced → what was built, on the next ruled line. Off by default — the\n// chapter's page says it once, as its standfirst.\nexport default function ChapterIndexDetail() {\n return (\n <ChapterIndex\n label=\"Chapters\"\n chapters={[\n { number: \"06\", title: \"The first products: Yomizaka and Akinaza\", period: \"Oct 17 – 23\", href: \"#ch-6\", detail: \"A pay stub nobody can read → a rule that cites its article\" },\n { number: \"07\", title: \"An empty result is not a success\", period: \"Oct 16 – 23\", href: \"#ch-7\", detail: \"A green screen with an empty ranking → a run never delivers nothing as a success\" },\n { number: \"08\", title: \"Building almost alone, with agents\", period: \"Oct 12 – 23\", href: \"#ch-8\", detail: \"Thirty agents, one night → rules written after every mistake\", isNew: true },\n ]}\n />\n );\n}\n"
255
+ },
256
+ {
257
+ "name": "head",
258
+ "title": "A chapter's head",
259
+ "description": "The number in the margin, the title, the period and the reading time, the standfirst once.",
260
+ "path": "examples/chapter/head.tsx",
261
+ "module": "@krizaka/ui/registry/examples/chapter/head",
262
+ "code": "import { ChapterHead } from \"@krizaka/ui/chapter\";\n\n// The head of a chapter's page: the number in the margin, the title, one quiet line (period · reading time), and the\n// faced → built line once, as the standfirst. The arrow is drawn; a screen reader hears the two labels instead.\nexport default function ChapterHeadStory() {\n return (\n <ChapterHead\n number=\"07\"\n title=\"Un résultat vide n'est pas un succès\"\n period=\"16 – 23 octobre 2026\"\n readingTime=\"9 min de lecture\"\n faced=\"Un écran vert avec un classement vide\"\n built=\"un run ne livre jamais un résultat vide comme un succès\"\n versusLabels={[\"Ce que j'ai affronté\", \"Ce qu'on a construit\"]}\n />\n );\n}\n"
263
+ },
264
+ {
265
+ "name": "foot",
266
+ "title": "A chapter's foot",
267
+ "description": "The previous and the next chapters on the lines, then one line back to the story.",
268
+ "path": "examples/chapter/foot.tsx",
269
+ "module": "@krizaka/ui/registry/examples/chapter/foot",
270
+ "code": "import { ChapterFoot } from \"@krizaka/ui/chapter\";\n\n// The foot of a chapter's page: the previous and the next chapters as two quiet links on the lines, then one line back\n// to the story. On the first chapter, leave `previous` out; on the latest, `next`.\nexport default function ChapterFootStory() {\n return (\n <ChapterFoot\n label=\"Chapitres\"\n labels={{ previous: \"précédent\", next: \"suivant\" }}\n previous={{ number: \"06\", title: \"Les premiers produits : Yomizaka et Akinaza\", href: \"#chapitre-6\" }}\n next={{ number: \"08\", title: \"Construire presque seul, avec des agents\", href: \"#chapitre-8\" }}\n back={{ href: \"#histoire\", label: \"Retour à l'histoire\" }}\n />\n );\n}\n"
271
+ },
272
+ {
273
+ "name": "reading-page",
274
+ "title": "The latest chapter, top and bottom",
275
+ "description": "Head and foot around the reading column; no next chapter yet.",
276
+ "path": "examples/chapter/reading-page.tsx",
277
+ "module": "@krizaka/ui/registry/examples/chapter/reading-page",
278
+ "code": "import { ChapterFoot, ChapterHead } from \"@krizaka/ui/chapter\";\n\n// The latest chapter, top and bottom: its head, the reading column, and a foot with no next chapter yet.\nexport default function ChapterReadingPage() {\n return (\n <article className=\"grid gap-8\">\n <ChapterHead\n number=\"08\"\n title=\"Construire presque seul, avec des agents\"\n period=\"12 – 23 octobre 2026\"\n readingTime=\"11 min de lecture\"\n faced=\"Une trentaine d'agents, une seule nuit\"\n built=\"des règles écrites après chaque erreur\"\n versusLabels={[\"Ce que j'ai affronté\", \"Ce qu'on a construit\"]}\n />\n <p className=\"max-w-prose text-fg-secondary\">Le texte du chapitre, dans sa colonne de lecture.</p>\n <ChapterFoot\n label=\"Chapitres\"\n labels={{ previous: \"précédent\", next: \"suivant\" }}\n previous={{ number: \"07\", title: \"Un résultat vide n'est pas un succès\", href: \"#chapitre-7\" }}\n back={{ href: \"#histoire\", label: \"Retour à l'histoire\" }}\n />\n </article>\n );\n}\n"
279
+ }
280
+ ]
281
+ },
282
+ "native": null,
283
+ "files": [
284
+ {
285
+ "path": "block-link.tsx",
286
+ "content": "// The links of the page blocks (PageHero's back link, LinkCard, ArgumentCard, ActionBar): an internal path renders\n// through the app's router link (`linkAs`, e.g. Next's `Link`), anything else is an `<a>` that opens in a new tab.\n// Shared by the block entries; never exported on its own.\nimport type * as React from \"react\";\n\n/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */\nexport type LinkComponent = React.ElementType<{ href: string; className?: string; children?: React.ReactNode; \"aria-label\"?: string; \"aria-describedby\"?: string }>;\n\n/** An address that leaves the app: an absolute URL or a protocol (`mailto:`). */\nexport const isExternal = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nexport function BlockLink({\n href,\n linkAs: Link = \"a\",\n external = isExternal(href),\n rel,\n ...props\n}: Omit<React.ComponentProps<\"a\">, \"href\"> & { href: string; linkAs?: LinkComponent; external?: boolean }) {\n if (external) return <a href={href} target=\"_blank\" rel={rel ?? \"noopener noreferrer\"} {...props} />;\n const Comp = Link as React.ElementType;\n return <Comp href={href} rel={rel} {...props} />;\n}\n"
287
+ },
288
+ {
289
+ "path": "chapter/chapter.tsx",
290
+ "content": "// Server-safe. The chapters of a story on the Cahier (ADR-004, blocks.css, krizaka-ui#617): a sober timeline, never a\n// stack of boxes. The rows sit on the notebook's ruled lines (`data-paper=\"lines\"`, the ruling of `Page`), the number\n// hangs in the margin beside its red rule (`data-margin`), the serif title is the link, the period is quiet.\n// - `ChapterIndex` — the chapters in reading order, one ruled line each (two at most), the latest marked « New »;\n// - `ChapterHead` — the head of a chapter's page: number in the margin, title, one quiet meta line, the standfirst;\n// - `ChapterFoot` — its foot: the previous and next chapters on the lines, then one line back to the story.\n// No border per row, no card, no button: the paper carries the rhythm. Every word arrives as a prop.\nimport type * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\n\n/** The notebook under the rows: `lines` (the ruling of `Page`, aligned on the rows) or `false` (the page's own paper). */\ntype ChapterPaper = \"lines\" | false;\n\nexport interface ChapterEntry {\n /** Its number, hung in the margin: `01`, `§ 1`. */\n number: React.ReactNode;\n /** The title, in the serif: the link. */\n title: React.ReactNode;\n /** Where it leads. */\n href: string;\n /** When it happened, quiet on the right (under the title on a phone): `janv. – mars 2026`, or a `<time>`. */\n period?: React.ReactNode;\n /** One more line under the title (what was faced → what was built). Off unless given: the chapter's page says it. */\n detail?: React.ReactNode;\n /** The latest chapter: a small mark after the title, in the hue (`newLabel`). */\n isNew?: boolean;\n /** The page the reader is on. */\n current?: boolean;\n}\n\n/** One ruled row: number in the margin, the title as the link (its hit area the whole row), an aside on the right. */\nfunction ChapterRow({\n number,\n title,\n href,\n aside,\n mark,\n detail,\n current,\n rel,\n linkAs,\n}: {\n number?: React.ReactNode;\n title: React.ReactNode;\n href: string;\n aside?: React.ReactNode;\n mark?: React.ReactNode;\n detail?: React.ReactNode;\n current?: boolean;\n rel?: string;\n linkAs?: LinkComponent;\n}) {\n return (\n <>\n <span className=\"kz-chapter-no\">{number}</span>\n <BlockLink href={href} linkAs={linkAs} rel={rel} className=\"kz-chapter-link\" aria-current={current ? \"page\" : undefined}>\n <span className=\"kz-chapter-title\">{title}</span>\n {mark && (\n <>\n {\" \"}\n <span className=\"kz-chapter-new\">{mark}</span>\n </>\n )}\n </BlockLink>\n {aside && (\n <>\n {/* A space, not a margin: in a narrow column the period runs on after the title, and starts flush when it wraps. */}{\" \"}\n <span className=\"kz-chapter-period\">{aside}</span>\n </>\n )}\n {detail && <span className=\"kz-chapter-detail\">{detail}</span>}\n </>\n );\n}\n\nexport type ChapterIndexProps = Omit<React.OlHTMLAttributes<HTMLOListElement>, \"children\"> & {\n /** The chapters, in reading order. */\n chapters: readonly ChapterEntry[];\n /** The word of the latest chapter's mark. @default \"New\" */\n newLabel?: React.ReactNode;\n /** The accessible name of the list, when no heading names it (`aria-labelledby`). */\n label?: string;\n /** The ruling under the rows. `false` when the page is ruled already and the list starts on its module. @default \"lines\" */\n paper?: ChapterPaper;\n /** The app's router link (Next's `Link`), so internal chapters prefetch. */\n linkAs?: LinkComponent;\n};\n\n/** The chapters of a story as a sober timeline: one ruled line each, the number in the margin, the title the link. */\nexport function ChapterIndex({ chapters, newLabel = \"New\", label, paper = \"lines\", linkAs, className, ...props }: ChapterIndexProps) {\n return (\n // `role=\"list\"`: a list without bullets is still a list to VoiceOver.\n <ol role=\"list\" aria-label={label} data-paper={paper || undefined} data-margin=\"\" className={cn(\"kz-chapters\", className)} {...props}>\n {chapters.map((c, i) => (\n <li key={i} className=\"kz-chapter-row\" data-new={c.isNew ? \"\" : undefined} data-detail={c.detail ? \"\" : undefined}>\n <ChapterRow number={c.number} title={c.title} href={c.href} aside={c.period} mark={c.isNew ? newLabel : undefined} detail={c.detail} current={c.current} linkAs={linkAs} />\n </li>\n ))}\n </ol>\n );\n}\n\nexport type ChapterHeadProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The chapter's number, hung in the margin: `01`, `Chapitre 1`. */\n number: React.ReactNode;\n /** The title: the page's `h1`. */\n title: React.ReactNode;\n /** When it happened: `janv. – mars 2026`. */\n period?: React.ReactNode;\n /** How long it reads: `8 min`. */\n readingTime?: React.ReactNode;\n /** What was faced: the first half of the standfirst. */\n faced?: React.ReactNode;\n /** What was built: its second half, after the arrow. */\n built?: React.ReactNode;\n /** The words a screen reader says before each half (the arrow is drawn, not read): `[\"What I faced\", \"What we built\"]`. */\n versusLabels?: readonly [string, string];\n /** The level of the title. @default 1 */\n level?: 1 | 2;\n};\n\n/** The head of a chapter: the number in the margin, the title, the period and the reading time, the standfirst once. */\nexport function ChapterHead({ number, title, period, readingTime, faced, built, versusLabels, level = 1, className, children, ...props }: ChapterHeadProps) {\n const H = level === 1 ? \"h1\" : \"h2\";\n const meta = [period, readingTime].filter((m) => m !== undefined && m !== null && m !== \"\");\n return (\n <header data-margin=\"\" className={cn(\"kz-chapter-head\", className)} {...props}>\n <p className=\"kz-chapter-no\">{number}</p>\n <H className=\"kz-chapter-head-title\">{title}</H>\n {meta.length > 0 && (\n <p className=\"kz-chapter-meta\">\n {meta.map((m, i) => (\n <span key={i}>\n {i > 0 && <span aria-hidden> · </span>}\n {m}\n </span>\n ))}\n </p>\n )}\n {faced && built && (\n <p className=\"kz-chapter-standfirst\">\n {versusLabels && <span className=\"kz-sr-only\">{versusLabels[0]}, </span>}\n {faced}\n <span className=\"kz-chapter-arrow\" aria-hidden>\n {\" → \"}\n </span>\n {versusLabels && <span className=\"kz-sr-only\">; {versusLabels[1]}, </span>}\n {built}\n </p>\n )}\n {children}\n </header>\n );\n}\n\nexport interface ChapterLink {\n /** Its number, in the margin: `02`. */\n number?: React.ReactNode;\n /** Its title, the link. */\n title: React.ReactNode;\n /** Where it leads. */\n href: string;\n}\n\nexport type ChapterFootProps = Omit<React.HTMLAttributes<HTMLElement>, \"children\"> & {\n /** The chapter before this one (none on the first). */\n previous?: ChapterLink;\n /** The chapter after this one (none on the latest). */\n next?: ChapterLink;\n /** The line back to the story: `{ href: \"/\", label: \"Back to the story\" }`. */\n back?: { href: string; label: React.ReactNode };\n /** The quiet words beside each link. @default { previous: \"Previous\", next: \"Next\" } */\n labels?: { previous: React.ReactNode; next: React.ReactNode };\n /** The accessible name of the navigation. @default \"Chapters\" */\n label?: string;\n /** The ruling under the links. @default \"lines\" */\n paper?: ChapterPaper;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n};\n\n/** The foot of a chapter: the previous and the next on the ruled lines, then one line back to the story. */\nexport function ChapterFoot({\n previous,\n next,\n back,\n labels = { previous: \"Previous\", next: \"Next\" },\n label = \"Chapters\",\n paper = \"lines\",\n linkAs,\n className,\n ...props\n}: ChapterFootProps) {\n return (\n <nav aria-label={label} data-paper={paper || undefined} data-margin=\"\" className={cn(\"kz-chapters kz-chapter-foot\", className)} {...props}>\n <ul role=\"list\">\n {previous && (\n <li className=\"kz-chapter-row\" data-dir=\"previous\">\n <ChapterRow\n number={previous.number}\n title={previous.title}\n href={previous.href}\n rel=\"prev\"\n linkAs={linkAs}\n aside={\n <>\n <span aria-hidden>← </span>\n {labels.previous}\n </>\n }\n />\n </li>\n )}\n {next && (\n <li className=\"kz-chapter-row\" data-dir=\"next\">\n <ChapterRow\n number={next.number}\n title={next.title}\n href={next.href}\n rel=\"next\"\n linkAs={linkAs}\n aside={\n <>\n {labels.next}\n <span aria-hidden> →</span>\n </>\n }\n />\n </li>\n )}\n </ul>\n {back && (\n <p className=\"kz-chapter-back\">\n <BlockLink href={back.href} linkAs={linkAs} className=\"kz-chapter-back-link\">\n <span aria-hidden>↩ </span>\n {back.label}\n </BlockLink>\n </p>\n )}\n </nav>\n );\n}\n"
291
+ },
292
+ {
293
+ "path": "chapter/index.ts",
294
+ "content": "/** @krizaka/ui/chapter — `ChapterIndex`, `ChapterHead`, `ChapterFoot`: the chapters of a story on the Cahier's ruled lines (krizaka-ui#617). Server-safe. */\nexport type { LinkComponent } from \"../block-link\";\nexport {\n type ChapterEntry,\n ChapterFoot,\n type ChapterFootProps,\n ChapterHead,\n type ChapterHeadProps,\n ChapterIndex,\n type ChapterIndexProps,\n type ChapterLink,\n} from \"./chapter\";\n"
295
+ },
296
+ {
297
+ "path": "chapter/meta.ts",
298
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Chapter\",\n summary:\n \"The chapters of a story on the Cahier's ruled lines (ADR-004): `ChapterIndex` (one ruled line per chapter, the number in the margin, the serif title the link, the latest marked « New »), `ChapterHead` and `ChapterFoot` for the top and bottom of a chapter's page. No boxes.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: {\n native: { status: \"n/a\", reason: \"A long read's index and page frame on a public site; an app lists its items with List.\", use: \"list\" },\n desktop: \"web\",\n },\n whenToUse: [\n \"The chapters of a story or a series, read in order: a timeline you follow, never a feed of cards.\",\n \"The head of a chapter's page (its number, title, period and reading time, the standfirst once) and its foot (previous, next, back to the story).\",\n ],\n whenNotToUse: [\n { when: \"Entries the reader browses or picks among, with a picture or an argument each.\", use: \"link-card\" },\n { when: \"The sections of the page being read.\", use: \"toc\" },\n { when: \"A documentation's index with page numbers.\", use: \"page-index\" },\n ],\n bestPractices: [\n \"Short numbers in the margin (`01`, `§ 1`): the margin is 3 rem wide; the word « Chapter » belongs to the page's heading.\",\n \"One line per chapter: the title and its period. Say what was faced and what was built once, on the chapter's page (`ChapterHead`'s standfirst); `detail` is for an index that truly needs a second line.\",\n \"Mark only the latest chapter `isNew`, and only while it is new.\",\n \"`ChapterIndex` rules its own lines so every row lands on one: put it in a section without `paper`, or pass `paper={false}` when the list starts on the page's module.\",\n \"Give `ChapterHead` the two `versusLabels`: the arrow of the standfirst is drawn, a screen reader hears the labels.\",\n \"Pass the router link as `linkAs` so internal chapters prefetch.\",\n ],\n accessibility: {\n keyboard: [{ keys: \"Tab\", action: \"Moves from one chapter's title to the next; the focus ring outlines the whole row.\" }],\n notes: [\n \"`ChapterIndex` is an ordered list (`role=\\\"list\\\"`, kept by VoiceOver without bullets) of links named by their title; the « New » mark is read inside the link, the number and the period around it.\",\n \"The whole row is the link's hit area (a stretched pseudo-element); only the title and the mark are its name.\",\n \"`ChapterHead` renders a real `h1` (or `h2` with `level={2}`); the meta line's separator and the standfirst's arrow are hidden from assistive technology.\",\n \"`ChapterFoot` is a `nav` named by `label` (« Chapters »); its links carry `rel=\\\"prev\\\"` and `rel=\\\"next\\\"`, their arrows are decorative.\",\n \"Nothing moves: hover and focus underline the title in the hue, also under reduced motion.\",\n ],\n },\n related: [\"page\", \"page-index\", \"toc\", \"footnote\"],\n web: {\n imports: [\"ChapterIndex\", \"ChapterHead\", \"ChapterFoot\"],\n examples: [\n { name: \"index\", title: \"The chapters of a story\", description: \"Eight chapters in reading order, one ruled line each, the latest marked « Nouveau ».\" },\n { name: \"detail\", title: \"With a second line\", description: \"The optional `detail`: what was faced → what was built, on the next ruled line.\" },\n { name: \"head\", title: \"A chapter's head\", description: \"The number in the margin, the title, the period and the reading time, the standfirst once.\" },\n { name: \"foot\", title: \"A chapter's foot\", description: \"The previous and the next chapters on the lines, then one line back to the story.\" },\n { name: \"reading-page\", title: \"The latest chapter, top and bottom\", description: \"Head and foot around the reading column; no next chapter yet.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
299
+ },
300
+ {
301
+ "path": "meta.ts",
302
+ "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 ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<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 | \"agenda\"\n | \"account-settings\"\n | \"drop-overlay\"\n | \"upload-queue\"\n | \"achievement\"\n | \"anatomy-plate\"\n | \"bird-plate\"\n | \"taped-photo\"\n | \"text-style\"\n | \"flock\"\n | \"elephant\"\n | \"footnote\"\n | \"page-index\"\n | \"chapter\"\n | \"specimen-tag\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-header\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"captcha\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"chip-row\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cover-hero\"\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 | \"docs-nav\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"engine-flow\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\n | \"glossary\"\n | \"glossary-term\"\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 | \"kpi-explained\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"waveform\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"language-switch\"\n | \"media-gallery\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"opening-hours\"\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 | \"router\"\n | \"rail-nav\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"service-card\"\n | \"settings-layout\"\n | \"account-menu\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot-picker\"\n | \"slot\"\n | \"spinner\"\n | \"sticky-action\"\n | \"splash-handoff\"\n | \"split-view\"\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 | \"update-prompt\"\n | \"verified-badge\"\n | \"vertical-video\"\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 * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\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 ui.krizaka.com/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 /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\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"
303
+ }
304
+ ],
305
+ "dependencies": [],
306
+ "registryDependencies": [
307
+ "cn"
308
+ ],
309
+ "demo": {
310
+ "path": "examples/chapter/index.tsx",
311
+ "content": "import { ChapterIndex } from \"@krizaka/ui/chapter\";\n\nconst chapters = [\n [\"Pourquoi je construis Krizaka à Montréal\", \"16 oct.\"],\n [\"Un moteur d'IA sur un portable\", \"16 – 17 oct.\"],\n [\"Ce que coûtent une image et une vidéo : mes factures\", \"15 – 21 oct.\"],\n [\"D'un clip à un film : ce que nous avons raté d'abord\", \"17 – 21 oct.\"],\n [\"Une seule interface, du web au Mac\", \"17 – 21 oct.\"],\n [\"Les premiers produits : Yomizaka et Akinaza\", \"17 – 23 oct.\"],\n [\"Un résultat vide n'est pas un succès\", \"16 – 23 oct.\"],\n [\"Construire presque seul, avec des agents\", \"12 – 23 oct.\"],\n] as const;\n\n// The chapters of a story in reading order, one ruled line each: the number in the margin, the serif title is the link,\n// the period quiet on the right. The latest wears a small « Nouveau » in the hue.\nexport default function ChapterIndexStory() {\n return (\n <ChapterIndex\n label=\"Les chapitres\"\n newLabel=\"Nouveau\"\n chapters={chapters.map(([title, period], i) => ({\n number: String(i + 1).padStart(2, \"0\"),\n title,\n period,\n href: `#chapitre-${i + 1}`,\n isNew: i === chapters.length - 1,\n }))}\n />\n );\n}\n"
312
+ },
313
+ "props": [
314
+ {
315
+ "component": "LinkComponent",
316
+ "description": "The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children.",
317
+ "props": []
318
+ },
319
+ {
320
+ "component": "ChapterFoot",
321
+ "description": "The foot of a chapter: the previous and the next on the ruled lines, then one line back to the story.",
322
+ "props": [
323
+ {
324
+ "name": "back",
325
+ "type": "{ href: string; label: ReactNode; }",
326
+ "default": null,
327
+ "description": "The line back to the story: `{ href: \"/\", label: \"Back to the story\" }`.",
328
+ "required": false
329
+ },
330
+ {
331
+ "name": "label",
332
+ "type": "string",
333
+ "default": "Chapters",
334
+ "description": "The accessible name of the navigation.",
335
+ "required": false
336
+ },
337
+ {
338
+ "name": "labels",
339
+ "type": "{ previous: ReactNode; next: ReactNode; }",
340
+ "default": "{ previous: \"Previous\", next: \"Next\" }",
341
+ "description": "The quiet words beside each link.",
342
+ "required": false
343
+ },
344
+ {
345
+ "name": "linkAs",
346
+ "type": "LinkComponent",
347
+ "default": null,
348
+ "description": "The app's router link (Next's `Link`).",
349
+ "required": false
350
+ },
351
+ {
352
+ "name": "next",
353
+ "type": "ChapterLink",
354
+ "default": null,
355
+ "description": "The chapter after this one (none on the latest).",
356
+ "required": false
357
+ },
358
+ {
359
+ "name": "paper",
360
+ "type": "ChapterPaper",
361
+ "default": "lines",
362
+ "description": "The ruling under the links.",
363
+ "required": false
364
+ },
365
+ {
366
+ "name": "previous",
367
+ "type": "ChapterLink",
368
+ "default": null,
369
+ "description": "The chapter before this one (none on the first).",
370
+ "required": false
371
+ }
372
+ ]
373
+ },
374
+ {
375
+ "component": "ChapterHead",
376
+ "description": "The head of a chapter: the number in the margin, the title, the period and the reading time, the standfirst once.",
377
+ "props": [
378
+ {
379
+ "name": "number",
380
+ "type": "ReactNode",
381
+ "default": null,
382
+ "description": "The chapter's number, hung in the margin: `01`, `Chapitre 1`.",
383
+ "required": true
384
+ },
385
+ {
386
+ "name": "title",
387
+ "type": "ReactNode",
388
+ "default": null,
389
+ "description": "The title: the page's `h1`.",
390
+ "required": true
391
+ },
392
+ {
393
+ "name": "built",
394
+ "type": "ReactNode",
395
+ "default": null,
396
+ "description": "What was built: its second half, after the arrow.",
397
+ "required": false
398
+ },
399
+ {
400
+ "name": "faced",
401
+ "type": "ReactNode",
402
+ "default": null,
403
+ "description": "What was faced: the first half of the standfirst.",
404
+ "required": false
405
+ },
406
+ {
407
+ "name": "level",
408
+ "type": "1 | 2",
409
+ "default": "1",
410
+ "description": "The level of the title.",
411
+ "required": false
412
+ },
413
+ {
414
+ "name": "period",
415
+ "type": "ReactNode",
416
+ "default": null,
417
+ "description": "When it happened: `janv. – mars 2026`.",
418
+ "required": false
419
+ },
420
+ {
421
+ "name": "readingTime",
422
+ "type": "ReactNode",
423
+ "default": null,
424
+ "description": "How long it reads: `8 min`.",
425
+ "required": false
426
+ },
427
+ {
428
+ "name": "versusLabels",
429
+ "type": "readonly [string, string]",
430
+ "default": null,
431
+ "description": "The words a screen reader says before each half (the arrow is drawn, not read): `[\"What I faced\", \"What we built\"]`.",
432
+ "required": false
433
+ }
434
+ ]
435
+ },
436
+ {
437
+ "component": "ChapterIndex",
438
+ "description": "The chapters of a story as a sober timeline: one ruled line each, the number in the margin, the title the link.",
439
+ "props": [
440
+ {
441
+ "name": "chapters",
442
+ "type": "readonly ChapterEntry[]",
443
+ "default": null,
444
+ "description": "The chapters, in reading order.",
445
+ "required": true
446
+ },
447
+ {
448
+ "name": "label",
449
+ "type": "string",
450
+ "default": null,
451
+ "description": "The accessible name of the list, when no heading names it (`aria-labelledby`).",
452
+ "required": false
453
+ },
454
+ {
455
+ "name": "linkAs",
456
+ "type": "LinkComponent",
457
+ "default": null,
458
+ "description": "The app's router link (Next's `Link`), so internal chapters prefetch.",
459
+ "required": false
460
+ },
461
+ {
462
+ "name": "newLabel",
463
+ "type": "ReactNode",
464
+ "default": "New",
465
+ "description": "The word of the latest chapter's mark.",
466
+ "required": false
467
+ },
468
+ {
469
+ "name": "paper",
470
+ "type": "ChapterPaper",
471
+ "default": "lines",
472
+ "description": "The ruling under the rows. `false` when the page is ruled already and the list starts on its module.",
473
+ "required": false
474
+ }
475
+ ]
476
+ }
477
+ ]
478
+ }
@@ -748,7 +748,7 @@
748
748
  },
749
749
  {
750
750
  "path": "meta.ts",
751
- "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 ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<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 | \"agenda\"\n | \"account-settings\"\n | \"drop-overlay\"\n | \"upload-queue\"\n | \"achievement\"\n | \"anatomy-plate\"\n | \"bird-plate\"\n | \"taped-photo\"\n | \"flock\"\n | \"elephant\"\n | \"footnote\"\n | \"page-index\"\n | \"specimen-tag\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-header\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"captcha\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"chip-row\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cover-hero\"\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 | \"docs-nav\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"engine-flow\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\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 | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"waveform\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"language-switch\"\n | \"media-gallery\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"opening-hours\"\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 | \"router\"\n | \"rail-nav\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"service-card\"\n | \"settings-layout\"\n | \"account-menu\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot-picker\"\n | \"slot\"\n | \"spinner\"\n | \"sticky-action\"\n | \"splash-handoff\"\n | \"split-view\"\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 | \"update-prompt\"\n | \"verified-badge\"\n | \"vertical-video\"\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 * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\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 ui.krizaka.com/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 /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\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"
751
+ "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 ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<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 | \"agenda\"\n | \"account-settings\"\n | \"drop-overlay\"\n | \"upload-queue\"\n | \"achievement\"\n | \"anatomy-plate\"\n | \"bird-plate\"\n | \"taped-photo\"\n | \"text-style\"\n | \"flock\"\n | \"elephant\"\n | \"footnote\"\n | \"page-index\"\n | \"chapter\"\n | \"specimen-tag\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-header\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"captcha\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"chip-row\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cover-hero\"\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 | \"docs-nav\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"engine-flow\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\n | \"glossary\"\n | \"glossary-term\"\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 | \"kpi-explained\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"waveform\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"language-switch\"\n | \"media-gallery\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"opening-hours\"\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 | \"router\"\n | \"rail-nav\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"service-card\"\n | \"settings-layout\"\n | \"account-menu\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot-picker\"\n | \"slot\"\n | \"spinner\"\n | \"sticky-action\"\n | \"splash-handoff\"\n | \"split-view\"\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 | \"update-prompt\"\n | \"verified-badge\"\n | \"vertical-video\"\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 * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\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 ui.krizaka.com/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 /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\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"
752
752
  }
753
753
  ],
754
754
  "dependencies": [