@krizaka/ui 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +17 -7
  2. package/dist/{button-Cgfkg_3y.d.ts → button-BMAO7dKF.d.ts} +29 -2
  3. package/dist/button.d.ts +32 -5
  4. package/dist/button.js +62 -9
  5. package/dist/button.js.map +1 -1
  6. package/dist/chip-link.d.ts +53 -0
  7. package/dist/chip-link.js +32 -0
  8. package/dist/chip-link.js.map +1 -0
  9. package/dist/chunk-43PTCDTL.js +31 -0
  10. package/dist/chunk-43PTCDTL.js.map +1 -0
  11. package/dist/chunk-6ISW63JI.js +11 -0
  12. package/dist/chunk-6ISW63JI.js.map +1 -0
  13. package/dist/chunk-HAQWKORZ.js +117 -0
  14. package/dist/chunk-HAQWKORZ.js.map +1 -0
  15. package/dist/{chunk-FHYZSO2S.js → chunk-R6SDS7JM.js} +5 -3
  16. package/dist/chunk-R6SDS7JM.js.map +1 -0
  17. package/dist/chunk-RUPNPMP3.js +103 -0
  18. package/dist/chunk-RUPNPMP3.js.map +1 -0
  19. package/dist/chunk-VDR2EP6K.js +49 -0
  20. package/dist/chunk-VDR2EP6K.js.map +1 -0
  21. package/dist/chunk-WEVYKZIF.js +33 -0
  22. package/dist/chunk-WEVYKZIF.js.map +1 -0
  23. package/dist/command.d.ts +7 -7
  24. package/dist/command.js +3 -2
  25. package/dist/command.js.map +1 -1
  26. package/dist/confirm-button.d.ts +1 -1
  27. package/dist/confirm-button.js +2 -1
  28. package/dist/confirm-button.js.map +1 -1
  29. package/dist/{dialog-GQ4Nvuyf.d.ts → dialog-D42QwYb_.d.ts} +11 -1
  30. package/dist/dialog.d.ts +1 -1
  31. package/dist/dialog.js +3 -2
  32. package/dist/dialog.js.map +1 -1
  33. package/dist/glow-card.d.ts +20 -0
  34. package/dist/glow-card.js +30 -0
  35. package/dist/glow-card.js.map +1 -0
  36. package/dist/index.d.ts +2 -20
  37. package/dist/index.js +7 -64
  38. package/dist/index.js.map +1 -1
  39. package/dist/iso.d.ts +400 -0
  40. package/dist/iso.js +363 -0
  41. package/dist/iso.js.map +1 -0
  42. package/dist/loader.d.ts +206 -0
  43. package/dist/loader.js +138 -0
  44. package/dist/loader.js.map +1 -0
  45. package/dist/marquee.d.ts +33 -0
  46. package/dist/marquee.js +52 -0
  47. package/dist/marquee.js.map +1 -0
  48. package/dist/meter.d.ts +146 -0
  49. package/dist/meter.js +107 -0
  50. package/dist/meter.js.map +1 -0
  51. package/dist/motion.css +394 -3
  52. package/dist/motion.d.ts +46 -0
  53. package/dist/motion.js +10 -0
  54. package/dist/motion.js.map +1 -0
  55. package/dist/nav-bar.d.ts +178 -0
  56. package/dist/nav-bar.js +157 -0
  57. package/dist/nav-bar.js.map +1 -0
  58. package/dist/number-ticker.d.ts +32 -0
  59. package/dist/number-ticker.js +61 -0
  60. package/dist/number-ticker.js.map +1 -0
  61. package/dist/progress.d.ts +56 -1
  62. package/dist/progress.js +72 -25
  63. package/dist/progress.js.map +1 -1
  64. package/dist/skeleton.d.ts +18 -3
  65. package/dist/skeleton.js +9 -1
  66. package/dist/skeleton.js.map +1 -1
  67. package/dist/spinner.d.ts +21 -5
  68. package/dist/spinner.js +13 -12
  69. package/dist/spinner.js.map +1 -1
  70. package/dist/stepper.d.ts +112 -0
  71. package/dist/stepper.js +67 -0
  72. package/dist/stepper.js.map +1 -0
  73. package/dist/tabs.d.ts +1 -1
  74. package/dist/tabs.js +14 -8
  75. package/dist/tabs.js.map +1 -1
  76. package/dist/theme.d.ts +1 -1
  77. package/dist/theme.js +2 -1
  78. package/dist/theme.js.map +1 -1
  79. package/dist/toast.d.ts +39 -4
  80. package/dist/toast.js +81 -7
  81. package/dist/toast.js.map +1 -1
  82. package/package.json +148 -8
  83. package/registry/alert.json +1 -1
  84. package/registry/avatar.json +1 -1
  85. package/registry/badge.json +1 -1
  86. package/registry/button.json +232 -26
  87. package/registry/card.json +1 -1
  88. package/registry/checkbox.json +1 -1
  89. package/registry/chip-link.json +158 -0
  90. package/registry/chip.json +1 -1
  91. package/registry/command.json +1 -1
  92. package/registry/confirm-button.json +91 -7
  93. package/registry/countdown.json +1 -1
  94. package/registry/dialog.json +15 -7
  95. package/registry/dropdown-menu.json +1 -1
  96. package/registry/empty-state.json +1 -1
  97. package/registry/examples/button/error.js +11 -0
  98. package/registry/examples/button/error.tsx +10 -0
  99. package/registry/examples/button/loading.tsx +1 -1
  100. package/registry/examples/button/save-flow.d.ts +4 -0
  101. package/registry/examples/button/save-flow.js +28 -0
  102. package/registry/examples/button/save-flow.tsx +24 -0
  103. package/registry/examples/button/signature.d.ts +4 -0
  104. package/registry/examples/button/signature.js +17 -0
  105. package/registry/examples/button/signature.tsx +19 -0
  106. package/registry/examples/button/upload-progress.d.ts +4 -0
  107. package/registry/examples/button/upload-progress.js +23 -0
  108. package/registry/examples/button/upload-progress.tsx +26 -0
  109. package/registry/examples/chip-link/filters.d.ts +4 -0
  110. package/registry/examples/chip-link/filters.js +12 -0
  111. package/registry/examples/chip-link/filters.tsx +17 -0
  112. package/registry/examples/dialog/left.d.ts +4 -0
  113. package/registry/examples/dialog/left.js +22 -0
  114. package/registry/examples/dialog/left.tsx +32 -0
  115. package/registry/examples/glow-card/link-card.d.ts +4 -0
  116. package/registry/examples/glow-card/link-card.js +14 -0
  117. package/registry/examples/glow-card/link-card.tsx +13 -0
  118. package/registry/examples/glow-card/plans.d.ts +4 -0
  119. package/registry/examples/glow-card/plans.js +25 -0
  120. package/registry/examples/glow-card/plans.tsx +26 -0
  121. package/registry/examples/glow-card/spotlight.d.ts +4 -0
  122. package/registry/examples/glow-card/spotlight.js +14 -0
  123. package/registry/examples/glow-card/spotlight.tsx +15 -0
  124. package/registry/examples/iso/compose.d.ts +4 -0
  125. package/registry/examples/iso/compose.js +22 -0
  126. package/registry/examples/iso/compose.tsx +19 -0
  127. package/registry/examples/iso/orazaka-hero.d.ts +4 -0
  128. package/registry/examples/iso/orazaka-hero.js +26 -0
  129. package/registry/examples/iso/orazaka-hero.tsx +21 -0
  130. package/registry/examples/iso/orochia-hero.d.ts +4 -0
  131. package/registry/examples/iso/orochia-hero.js +29 -0
  132. package/registry/examples/iso/orochia-hero.tsx +23 -0
  133. package/registry/examples/iso/still.d.ts +4 -0
  134. package/registry/examples/iso/still.js +21 -0
  135. package/registry/examples/iso/still.tsx +15 -0
  136. package/registry/examples/loader/endings.d.ts +4 -0
  137. package/registry/examples/loader/endings.js +14 -0
  138. package/registry/examples/loader/endings.tsx +11 -0
  139. package/registry/examples/loader/fetch-flow.d.ts +4 -0
  140. package/registry/examples/loader/fetch-flow.js +24 -0
  141. package/registry/examples/loader/fetch-flow.tsx +24 -0
  142. package/registry/examples/loader/minimal.d.ts +4 -0
  143. package/registry/examples/loader/minimal.js +11 -0
  144. package/registry/examples/loader/minimal.tsx +6 -0
  145. package/registry/examples/loader/overlay.d.ts +4 -0
  146. package/registry/examples/loader/overlay.js +16 -0
  147. package/registry/examples/loader/overlay.tsx +13 -0
  148. package/registry/examples/loader/region-handover.d.ts +4 -0
  149. package/registry/examples/loader/region-handover.js +42 -0
  150. package/registry/examples/loader/region-handover.tsx +29 -0
  151. package/registry/examples/loader/upload-eta.d.ts +4 -0
  152. package/registry/examples/loader/upload-eta.js +11 -0
  153. package/registry/examples/loader/upload-eta.tsx +10 -0
  154. package/registry/examples/marquee/cards.d.ts +4 -0
  155. package/registry/examples/marquee/cards.js +23 -0
  156. package/registry/examples/marquee/cards.tsx +21 -0
  157. package/registry/examples/marquee/logos.d.ts +4 -0
  158. package/registry/examples/marquee/logos.js +12 -0
  159. package/registry/examples/marquee/logos.tsx +17 -0
  160. package/registry/examples/meter/balance.d.ts +4 -0
  161. package/registry/examples/meter/balance.js +14 -0
  162. package/registry/examples/meter/balance.tsx +15 -0
  163. package/registry/examples/meter/quota.d.ts +4 -0
  164. package/registry/examples/meter/quota.js +15 -0
  165. package/registry/examples/meter/quota.tsx +12 -0
  166. package/registry/examples/meter/split.d.ts +4 -0
  167. package/registry/examples/meter/split.js +18 -0
  168. package/registry/examples/meter/split.tsx +18 -0
  169. package/registry/examples/nav-bar/app-bar.d.ts +4 -0
  170. package/registry/examples/nav-bar/app-bar.js +22 -0
  171. package/registry/examples/nav-bar/app-bar.tsx +21 -0
  172. package/registry/examples/nav-bar/menu-open.d.ts +4 -0
  173. package/registry/examples/nav-bar/menu-open.js +26 -0
  174. package/registry/examples/nav-bar/menu-open.tsx +26 -0
  175. package/registry/examples/nav-bar/site.d.ts +4 -0
  176. package/registry/examples/nav-bar/site.js +31 -0
  177. package/registry/examples/nav-bar/site.tsx +36 -0
  178. package/registry/examples/number-ticker/gradient.d.ts +4 -0
  179. package/registry/examples/number-ticker/gradient.js +11 -0
  180. package/registry/examples/number-ticker/gradient.tsx +10 -0
  181. package/registry/examples/number-ticker/live-total.d.ts +4 -0
  182. package/registry/examples/number-ticker/live-total.js +17 -0
  183. package/registry/examples/number-ticker/live-total.tsx +21 -0
  184. package/registry/examples/number-ticker/stats.d.ts +4 -0
  185. package/registry/examples/number-ticker/stats.js +19 -0
  186. package/registry/examples/number-ticker/stats.tsx +23 -0
  187. package/registry/examples/progress/buffer.d.ts +4 -0
  188. package/registry/examples/progress/buffer.js +11 -0
  189. package/registry/examples/progress/buffer.tsx +10 -0
  190. package/registry/examples/progress/statuses.d.ts +4 -0
  191. package/registry/examples/progress/statuses.js +14 -0
  192. package/registry/examples/progress/statuses.tsx +11 -0
  193. package/registry/examples/progress/steps.d.ts +4 -0
  194. package/registry/examples/progress/steps.js +14 -0
  195. package/registry/examples/progress/steps.tsx +13 -0
  196. package/registry/examples/progress/upload.d.ts +4 -0
  197. package/registry/examples/progress/upload.js +11 -0
  198. package/registry/examples/progress/upload.tsx +10 -0
  199. package/registry/examples/skeleton/paragraph.d.ts +4 -0
  200. package/registry/examples/skeleton/paragraph.js +14 -0
  201. package/registry/examples/skeleton/paragraph.tsx +12 -0
  202. package/registry/examples/skeleton/shape-of-content.d.ts +4 -0
  203. package/registry/examples/skeleton/shape-of-content.js +23 -0
  204. package/registry/examples/skeleton/shape-of-content.tsx +26 -0
  205. package/registry/examples/spinner/default.js +5 -2
  206. package/registry/examples/spinner/default.tsx +7 -1
  207. package/registry/examples/spinner/sizes.js +2 -1
  208. package/registry/examples/spinner/sizes.tsx +2 -0
  209. package/registry/examples/spinner/value.d.ts +4 -0
  210. package/registry/examples/spinner/value.js +11 -0
  211. package/registry/examples/spinner/value.tsx +12 -0
  212. package/registry/examples/stepper/checkout.d.ts +4 -0
  213. package/registry/examples/stepper/checkout.js +22 -0
  214. package/registry/examples/stepper/checkout.tsx +17 -0
  215. package/registry/examples/stepper/failed.d.ts +4 -0
  216. package/registry/examples/stepper/failed.js +26 -0
  217. package/registry/examples/stepper/failed.tsx +21 -0
  218. package/registry/examples/stepper/job.d.ts +4 -0
  219. package/registry/examples/stepper/job.js +32 -0
  220. package/registry/examples/stepper/job.tsx +28 -0
  221. package/registry/examples/toast/grouped.d.ts +4 -0
  222. package/registry/examples/toast/grouped.js +21 -0
  223. package/registry/examples/toast/grouped.tsx +23 -0
  224. package/registry/examples/toast/undo.d.ts +4 -0
  225. package/registry/examples/toast/undo.js +29 -0
  226. package/registry/examples/toast/undo.tsx +30 -0
  227. package/registry/examples/toast/upload-progress.d.ts +4 -0
  228. package/registry/examples/toast/upload-progress.js +29 -0
  229. package/registry/examples/toast/upload-progress.tsx +31 -0
  230. package/registry/field.json +1 -1
  231. package/registry/glow-card.json +162 -0
  232. package/registry/index.json +191 -20
  233. package/registry/iso.json +1355 -0
  234. package/registry/kbd.json +1 -1
  235. package/registry/loader.json +399 -0
  236. package/registry/marquee.json +225 -0
  237. package/registry/mascots.json +3 -3
  238. package/registry/meter.json +324 -0
  239. package/registry/nav-bar.json +480 -0
  240. package/registry/number-ticker.json +219 -0
  241. package/registry/page-header.json +1 -1
  242. package/registry/popover.json +1 -1
  243. package/registry/progress.json +116 -11
  244. package/registry/radio-group.json +1 -1
  245. package/registry/section-backdrop.json +1 -1
  246. package/registry/separator.json +1 -1
  247. package/registry/skeleton.json +69 -13
  248. package/registry/slider.json +1 -1
  249. package/registry/slot.json +1 -1
  250. package/registry/spinner.json +91 -27
  251. package/registry/stat.json +1 -1
  252. package/registry/stepper.json +174 -0
  253. package/registry/switch.json +1 -1
  254. package/registry/tabs.json +11 -7
  255. package/registry/theme.json +3 -3
  256. package/registry/toast.json +41 -13
  257. package/registry/tooltip.json +1 -1
  258. package/dist/chunk-FHYZSO2S.js.map +0 -1
  259. package/dist/chunk-PESA2R4U.js +0 -51
  260. package/dist/chunk-PESA2R4U.js.map +0 -1
  261. package/registry/examples/spinner/muted.js +0 -11
  262. package/registry/examples/spinner/muted.tsx +0 -6
  263. /package/registry/examples/{spinner/muted.d.ts → button/error.d.ts} +0 -0
@@ -2,8 +2,8 @@
2
2
  "name": "skeleton",
3
3
  "type": "primitive",
4
4
  "title": "Skeleton",
5
- "summary": "A placeholder the shape of what is loading: a line, a circle or a block.",
6
- "description": "A placeholder the shape of what is loading: a line, a circle or a block.",
5
+ "summary": "A placeholder the shape of what is loading — lines, circles, blocks, or the real component turned into its own skeleton (`SkeletonShape`) — crossed by one reflection of the brand light.",
6
+ "description": "A placeholder the shape of what is loading — lines, circles, blocks, or the real component turned into its own skeleton (`SkeletonShape`) — crossed by one reflection of the brand light.",
7
7
  "status": "stable",
8
8
  "category": "feedback",
9
9
  "platforms": "both",
@@ -25,28 +25,32 @@
25
25
  }
26
26
  ],
27
27
  "bestPractices": [
28
- "Compose the shapes like the content: an avatar circle, two lines, a media block.",
28
+ "Best: `SkeletonShape` around the real component with placeholder words — its exact layout, nothing to keep in sync.",
29
+ "Otherwise compose the shapes like the content: an avatar circle, two lines (`SkeletonText`), a media block.",
30
+ "Let `LoadingRegion` (`@krizaka/ui/loader`) decide when to show them: after a short delay, with words when the wait lasts.",
29
31
  "Show skeletons only after a short delay on fast connections, and never for more than a few seconds without a message."
30
32
  ],
31
33
  "accessibility": {
32
34
  "keyboard": [],
33
35
  "notes": [
34
36
  "Decorative and hidden from assistive technology: announce the wait elsewhere (`aria-busy` on the region, a `Spinner`).",
35
- "The pulse stops under `prefers-reduced-motion`."
37
+ "`SkeletonShape` is also `inert`: the placeholder component can hold no focus.",
38
+ "The reflection stops under `prefers-reduced-motion`: the shapes rest, tinted."
36
39
  ]
37
40
  },
38
41
  "related": [
42
+ "loader",
39
43
  "spinner",
40
44
  "empty-state",
41
45
  "card"
42
46
  ],
43
47
  "web": {
44
48
  "entry": "@krizaka/ui/skeleton",
45
- "import": "import { Skeleton } from \"@krizaka/ui/skeleton\";",
49
+ "import": "import { Skeleton, SkeletonText, SkeletonShape } from \"@krizaka/ui/skeleton\";",
46
50
  "props": [
47
51
  {
48
52
  "component": "Skeleton",
49
- "description": "A pulsing shape the size of what is loading. Announce the wait elsewhere (aria-busy on the region, a Spinner).",
53
+ "description": "A shape the size of what is loading. Announce the wait elsewhere (`LoadingRegion` does it, with `aria-busy`).",
50
54
  "props": [
51
55
  {
52
56
  "name": "shape",
@@ -56,9 +60,43 @@
56
60
  "required": false
57
61
  }
58
62
  ]
63
+ },
64
+ {
65
+ "component": "SkeletonShape",
66
+ "description": "The skeleton of the real thing: render your component with placeholder data inside, and its text, images and icons\nbecome skeleton shapes in place — the exact layout, nothing to draw twice. Inert and hidden from assistive technology.",
67
+ "props": []
68
+ },
69
+ {
70
+ "component": "SkeletonText",
71
+ "description": "A paragraph on its way: `lines` lines, the last one shorter — like text that wraps.",
72
+ "props": [
73
+ {
74
+ "name": "lines",
75
+ "type": "number",
76
+ "default": "3",
77
+ "description": "How many lines.",
78
+ "required": false
79
+ }
80
+ ]
59
81
  }
60
82
  ],
61
83
  "examples": [
84
+ {
85
+ "name": "shape-of-content",
86
+ "title": "The shape of the content",
87
+ "description": "`SkeletonShape` around the real card with placeholder words, above the loaded card: the same layout.",
88
+ "path": "examples/skeleton/shape-of-content.tsx",
89
+ "module": "@krizaka/ui/registry/examples/skeleton/shape-of-content",
90
+ "code": "import { SkeletonShape } from \"@krizaka/ui/skeleton\";\n\nfunction CreatorCard({ name, line }: { name: string; line: string }) {\n return (\n <div className=\"flex items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-4\">\n <span data-skeleton aria-hidden className=\"h-10 w-10 shrink-0 rounded-full bg-accent-soft\" />\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-fg\">{name}</p>\n <p className=\"text-sm text-fg-secondary\">{line}</p>\n </div>\n </div>\n );\n}\n\n// The skeleton of the real card: the same component with placeholder words, its text and media turned into shapes.\n// The layout is exact — nothing drawn twice, nothing jumps when the data arrives. `data-skeleton` marks any other shape.\nexport default function SkeletonShapeOfContent() {\n return (\n <div className=\"flex w-full max-w-sm flex-col gap-3\">\n <SkeletonShape>\n <CreatorCard name=\"Placeholder name\" line=\"Placeholder supporters line\" />\n </SkeletonShape>\n <CreatorCard name=\"Eric Moreau\" line=\"1,204 supporters · Paris\" />\n </div>\n );\n}\n"
91
+ },
92
+ {
93
+ "name": "paragraph",
94
+ "title": "A paragraph",
95
+ "description": "A title and `SkeletonText`: lines that wrap, one reflection across them all.",
96
+ "path": "examples/skeleton/paragraph.tsx",
97
+ "module": "@krizaka/ui/registry/examples/skeleton/paragraph",
98
+ "code": "import { Skeleton, SkeletonText } from \"@krizaka/ui/skeleton\";\n\n// An article on its way: a title, then `SkeletonText` — lines that wrap, the last one shorter. One reflection crosses\n// all of them together, like light across a page, rather than each one pulsing on its own.\nexport default function SkeletonParagraph() {\n return (\n <div className=\"flex w-full max-w-sm flex-col gap-3\">\n <Skeleton className=\"h-5 w-2/3\" />\n <SkeletonText lines={4} />\n </div>\n );\n}\n"
99
+ },
62
100
  {
63
101
  "name": "text",
64
102
  "title": "Text",
@@ -160,19 +198,19 @@
160
198
  "files": [
161
199
  {
162
200
  "path": "meta.ts",
163
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"kbd\"\n | \"mascots\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
201
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
164
202
  },
165
203
  {
166
204
  "path": "skeleton/index.ts",
167
- "content": "/** @krizaka/ui/skeleton — `Skeleton`, `skeletonVariants`. Server-safe. */\nexport { Skeleton, type SkeletonProps, skeletonVariants } from \"./skeleton\";\n"
205
+ "content": "/** @krizaka/ui/skeleton — `Skeleton`, `SkeletonText`, `SkeletonShape` (the skeleton of the real component), `skeletonVariants`. Server-safe. */\nexport { Skeleton, type SkeletonProps, SkeletonShape, type SkeletonShapeProps, SkeletonText, type SkeletonTextProps, skeletonVariants } from \"./skeleton\";\n"
168
206
  },
169
207
  {
170
208
  "path": "skeleton/meta.ts",
171
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Skeleton\",\n summary: \"A placeholder the shape of what is loading: a line, a circle or a block.\",\n status: \"stable\",\n category: \"feedback\",\n platforms: \"both\",\n whenToUse: [\"While a list, a card or a profile loads, in its final layout: the page does not jump when it arrives.\"],\n whenNotToUse: [\n { when: \"For an action the user is waiting on (a save).\", use: \"spinner\" },\n { when: \"When there is nothing to load.\", use: \"empty-state\" },\n { when: \"For the progress of a task.\", use: \"progress\" },\n ],\n bestPractices: [\n \"Compose the shapes like the content: an avatar circle, two lines, a media block.\",\n \"Show skeletons only after a short delay on fast connections, and never for more than a few seconds without a message.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative and hidden from assistive technology: announce the wait elsewhere (`aria-busy` on the region, a `Spinner`).\",\n \"The pulse stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"spinner\", \"empty-state\", \"card\"],\n web: {\n imports: [\"Skeleton\"],\n examples: [\n { name: \"text\", title: \"Text\", description: \"A line of text.\" },\n { name: \"circle\", title: \"Circle\", description: \"An avatar.\" },\n { name: \"rect\", title: \"Rect\", description: \"A media or a block.\" },\n { name: \"composition\", title: \"Composition\", description: \"Shapes composed into a card being loaded.\" },\n ],\n },\n native: {\n imports: [\"Skeleton\"],\n differences: [\"`width` and `height` are props (points or a percentage), not classes.\"],\n examples: [{ name: \"shapes\", title: \"Shapes\", description: \"`text`, `circle` and `rect`, composed.\" }],\n },\n} satisfies ComponentMeta;\n"
209
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Skeleton\",\n summary:\n \"A placeholder the shape of what is loading — lines, circles, blocks, or the real component turned into its own skeleton (`SkeletonShape`) — crossed by one reflection of the brand light.\",\n status: \"stable\",\n category: \"feedback\",\n platforms: \"both\",\n whenToUse: [\"While a list, a card or a profile loads, in its final layout: the page does not jump when it arrives.\"],\n whenNotToUse: [\n { when: \"For an action the user is waiting on (a save).\", use: \"spinner\" },\n { when: \"When there is nothing to load.\", use: \"empty-state\" },\n { when: \"For the progress of a task.\", use: \"progress\" },\n ],\n bestPractices: [\n \"Best: `SkeletonShape` around the real component with placeholder words — its exact layout, nothing to keep in sync.\",\n \"Otherwise compose the shapes like the content: an avatar circle, two lines (`SkeletonText`), a media block.\",\n \"Let `LoadingRegion` (`@krizaka/ui/loader`) decide when to show them: after a short delay, with words when the wait lasts.\",\n \"Show skeletons only after a short delay on fast connections, and never for more than a few seconds without a message.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative and hidden from assistive technology: announce the wait elsewhere (`aria-busy` on the region, a `Spinner`).\",\n \"`SkeletonShape` is also `inert`: the placeholder component can hold no focus.\",\n \"The reflection stops under `prefers-reduced-motion`: the shapes rest, tinted.\",\n ],\n },\n related: [\"loader\", \"spinner\", \"empty-state\", \"card\"],\n web: {\n imports: [\"Skeleton\", \"SkeletonText\", \"SkeletonShape\"],\n examples: [\n { name: \"shape-of-content\", title: \"The shape of the content\", description: \"`SkeletonShape` around the real card with placeholder words, above the loaded card: the same layout.\" },\n { name: \"paragraph\", title: \"A paragraph\", description: \"A title and `SkeletonText`: lines that wrap, one reflection across them all.\" },\n { name: \"text\", title: \"Text\", description: \"A line of text.\" },\n { name: \"circle\", title: \"Circle\", description: \"An avatar.\" },\n { name: \"rect\", title: \"Rect\", description: \"A media or a block.\" },\n { name: \"composition\", title: \"Composition\", description: \"Shapes composed into a card being loaded.\" },\n ],\n },\n native: {\n imports: [\"Skeleton\"],\n differences: [\"`width` and `height` are props (points or a percentage), not classes.\"],\n examples: [{ name: \"shapes\", title: \"Shapes\", description: \"`text`, `circle` and `rect`, composed.\" }],\n },\n} satisfies ComponentMeta;\n"
172
210
  },
173
211
  {
174
212
  "path": "skeleton/skeleton.tsx",
175
- "content": "// Server-safe. A placeholder for content on its way: decorative, so hidden from assistive technology.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const skeletonVariants = tv({\n base: \"block bg-surface-3 motion-safe:animate-pulse\",\n variants: {\n shape: { text: \"h-3 w-full rounded-sm\", circle: \"aspect-square h-10 rounded-full\", rect: \"h-24 w-full rounded-lg\" },\n },\n defaultVariants: { shape: \"text\" },\n});\n\nexport type SkeletonProps = React.ComponentProps<\"span\"> & {\n /**\n * text (a line) · circle (an avatar; give it a height) · rect (a media; give it a height).\n * @default \"text\"\n */\n shape?: VariantProps<typeof skeletonVariants>[\"shape\"];\n};\n\n/** A pulsing shape the size of what is loading. Announce the wait elsewhere (aria-busy on the region, a Spinner). */\nexport function Skeleton({ shape, className, ...props }: SkeletonProps) {\n return <span aria-hidden data-shape={shape ?? \"text\"} className={skeletonVariants({ shape, className })} {...props} />;\n}\n"
213
+ "content": "// Server-safe. A placeholder for content on its way: decorative, so hidden from assistive technology. One reflection of\n// the brand light crosses every skeleton of the page together (`background-attachment: fixed`), instead of each one\n// pulsing on its own; under reduced motion they rest, tinted.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const skeletonVariants = tv({\n base: \"kz-skeleton block bg-surface-3\",\n variants: {\n shape: { text: \"h-3 w-full rounded-sm\", circle: \"aspect-square h-10 rounded-full\", rect: \"h-24 w-full rounded-lg\" },\n },\n defaultVariants: { shape: \"text\" },\n});\n\nexport type SkeletonProps = React.ComponentProps<\"span\"> & {\n /**\n * text (a line) · circle (an avatar; give it a height) · rect (a media; give it a height).\n * @default \"text\"\n */\n shape?: VariantProps<typeof skeletonVariants>[\"shape\"];\n};\n\n/** A shape the size of what is loading. Announce the wait elsewhere (`LoadingRegion` does it, with `aria-busy`). */\nexport function Skeleton({ shape, className, ...props }: SkeletonProps) {\n return <span aria-hidden data-shape={shape ?? \"text\"} className={skeletonVariants({ shape, className })} {...props} />;\n}\n\nexport type SkeletonTextProps = React.ComponentProps<\"span\"> & {\n /**\n * How many lines.\n * @default 3\n */\n lines?: number;\n};\n\n/** A paragraph on its way: `lines` lines, the last one shorter — like text that wraps. */\nexport function SkeletonText({ lines = 3, className, ...props }: SkeletonTextProps) {\n return (\n <span aria-hidden className={[\"flex w-full flex-col gap-2\", className].filter(Boolean).join(\" \")} {...props}>\n {Array.from({ length: lines }, (_, i) => (\n <Skeleton key={i} className={i === lines - 1 && lines > 1 ? \"w-3/5\" : undefined} />\n ))}\n </span>\n );\n}\n\nexport type SkeletonShapeProps = React.ComponentProps<\"div\">;\n\n/**\n * The skeleton of the real thing: render your component with placeholder data inside, and its text, images and icons\n * become skeleton shapes in place — the exact layout, nothing to draw twice. Inert and hidden from assistive technology.\n */\nexport function SkeletonShape({ className, children, ...props }: SkeletonShapeProps) {\n return (\n <div aria-hidden inert className={[\"kz-skeleton-shape\", className].filter(Boolean).join(\" \")} {...props}>\n {children}\n </div>\n );\n}\n"
176
214
  }
177
215
  ],
178
216
  "dependencies": [
@@ -180,13 +218,13 @@
180
218
  ],
181
219
  "registryDependencies": [],
182
220
  "demo": {
183
- "path": "examples/skeleton/text.tsx",
184
- "content": "import { Skeleton } from \"@krizaka/ui/skeleton\";\n\nexport default function SkeletonText() {\n return <Skeleton />;\n}\n"
221
+ "path": "examples/skeleton/shape-of-content.tsx",
222
+ "content": "import { SkeletonShape } from \"@krizaka/ui/skeleton\";\n\nfunction CreatorCard({ name, line }: { name: string; line: string }) {\n return (\n <div className=\"flex items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-4\">\n <span data-skeleton aria-hidden className=\"h-10 w-10 shrink-0 rounded-full bg-accent-soft\" />\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-fg\">{name}</p>\n <p className=\"text-sm text-fg-secondary\">{line}</p>\n </div>\n </div>\n );\n}\n\n// The skeleton of the real card: the same component with placeholder words, its text and media turned into shapes.\n// The layout is exact — nothing drawn twice, nothing jumps when the data arrives. `data-skeleton` marks any other shape.\nexport default function SkeletonShapeOfContent() {\n return (\n <div className=\"flex w-full max-w-sm flex-col gap-3\">\n <SkeletonShape>\n <CreatorCard name=\"Placeholder name\" line=\"Placeholder supporters line\" />\n </SkeletonShape>\n <CreatorCard name=\"Eric Moreau\" line=\"1,204 supporters · Paris\" />\n </div>\n );\n}\n"
185
223
  },
186
224
  "props": [
187
225
  {
188
226
  "component": "Skeleton",
189
- "description": "A pulsing shape the size of what is loading. Announce the wait elsewhere (aria-busy on the region, a Spinner).",
227
+ "description": "A shape the size of what is loading. Announce the wait elsewhere (`LoadingRegion` does it, with `aria-busy`).",
190
228
  "props": [
191
229
  {
192
230
  "name": "shape",
@@ -196,6 +234,24 @@
196
234
  "required": false
197
235
  }
198
236
  ]
237
+ },
238
+ {
239
+ "component": "SkeletonShape",
240
+ "description": "The skeleton of the real thing: render your component with placeholder data inside, and its text, images and icons\nbecome skeleton shapes in place — the exact layout, nothing to draw twice. Inert and hidden from assistive technology.",
241
+ "props": []
242
+ },
243
+ {
244
+ "component": "SkeletonText",
245
+ "description": "A paragraph on its way: `lines` lines, the last one shorter — like text that wraps.",
246
+ "props": [
247
+ {
248
+ "name": "lines",
249
+ "type": "number",
250
+ "default": "3",
251
+ "description": "How many lines.",
252
+ "required": false
253
+ }
254
+ ]
199
255
  }
200
256
  ]
201
257
  }
@@ -171,7 +171,7 @@
171
171
  "files": [
172
172
  {
173
173
  "path": "meta.ts",
174
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"kbd\"\n | \"mascots\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
174
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
175
175
  },
176
176
  {
177
177
  "path": "slider/index.ts",
@@ -73,7 +73,7 @@
73
73
  "files": [
74
74
  {
75
75
  "path": "meta.ts",
76
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"kbd\"\n | \"mascots\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
76
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
77
77
  },
78
78
  {
79
79
  "path": "slot/index.ts",
@@ -2,8 +2,8 @@
2
2
  "name": "spinner",
3
3
  "type": "primitive",
4
4
  "title": "Spinner",
5
- "summary": "An indeterminate wait: a turning ring named by `label`.",
6
- "description": "An indeterminate wait: a turning ring named by `label`.",
5
+ "summary": "The Krizaka spinner: light traced around a hexagon, its node breathing — indeterminate, or growing to a `value`; still and legible under reduced motion.",
6
+ "description": "The Krizaka spinner: light traced around a hexagon, its node breathing — indeterminate, or growing to a `value`; still and legible under reduced motion.",
7
7
  "status": "stable",
8
8
  "category": "feedback",
9
9
  "platforms": "both",
@@ -16,9 +16,13 @@
16
16
  "use": "skeleton"
17
17
  },
18
18
  {
19
- "when": "When the progress is known.",
19
+ "when": "When the progress is known and the wait is long: a bar says how far.",
20
20
  "use": "progress"
21
21
  },
22
+ {
23
+ "when": "When the wait may be instant (it would flash), or ends with a result to announce.",
24
+ "use": "loader"
25
+ },
22
26
  {
23
27
  "when": "Inside a button that runs an action: set `loading` on the button.",
24
28
  "use": "button"
@@ -26,27 +30,49 @@
26
30
  ],
27
31
  "bestPractices": [
28
32
  "One spinner per region: never a page full of them.",
29
- "Pass `label` translated (“Loading”, “Searching”)."
33
+ "Pass `label` translated (“Loading”, “Searching”) and, when you know it, `value`: a growing trace reassures more than a loop.",
34
+ "A wait that may be instant, that ends in success or failure, or that can last: use `Loader`, which waits before showing itself and hands over to a skeleton."
30
35
  ],
31
36
  "accessibility": {
32
37
  "keyboard": [],
33
38
  "notes": [
34
39
  "`role=\"status\"` named by `label`: announced politely.",
35
- "It stops turning under `prefers-reduced-motion`."
40
+ "Under `prefers-reduced-motion` it rests on a designed still frame: a third of the hexagon traced and the node lit — never a frozen blur."
36
41
  ]
37
42
  },
38
43
  "related": [
44
+ "loader",
39
45
  "skeleton",
40
46
  "progress",
41
47
  "button"
42
48
  ],
43
49
  "web": {
44
50
  "entry": "@krizaka/ui/spinner",
45
- "import": "import { Spinner } from \"@krizaka/ui/spinner\";",
51
+ "import": "import { Spinner, LoaderGlyph } from \"@krizaka/ui/spinner\";",
46
52
  "props": [
53
+ {
54
+ "component": "LoaderGlyph",
55
+ "description": "The glyph alone (decorative): the Spinner, the Loader and the Button's `loading` draw it.",
56
+ "props": [
57
+ {
58
+ "name": "state",
59
+ "type": "LoaderGlyphState",
60
+ "default": "loading",
61
+ "description": "loading · success · error.",
62
+ "required": false
63
+ },
64
+ {
65
+ "name": "value",
66
+ "type": "number | null",
67
+ "default": null,
68
+ "description": "Between 0 and 1: a determinate wait, the trace grows to it. Absent: indeterminate.",
69
+ "required": false
70
+ }
71
+ ]
72
+ },
47
73
  {
48
74
  "component": "Spinner",
49
- "description": "A turning ring with `role=\"status\"` (it stops turning under prefers-reduced-motion).",
75
+ "description": "The Krizaka spinner: a hexagon traced by a segment of light around a breathing node, in `currentColor` (the accent by\ndefault). `role=\"status\"`, named by `label`. Under prefers-reduced-motion it rests — a segment and the node lit.",
50
76
  "props": [
51
77
  {
52
78
  "name": "label",
@@ -61,6 +87,13 @@
61
87
  "default": "md",
62
88
  "description": "sm · md · lg.",
63
89
  "required": false
90
+ },
91
+ {
92
+ "name": "value",
93
+ "type": "number | null",
94
+ "default": null,
95
+ "description": "Between 0 and 1: the trace grows to the value (a determinate wait). Absent: it travels around.",
96
+ "required": false
64
97
  }
65
98
  ]
66
99
  }
@@ -68,27 +101,27 @@
68
101
  "examples": [
69
102
  {
70
103
  "name": "default",
71
- "title": "Default",
72
- "description": "Named by `label`.",
104
+ "title": "Searching in place",
105
+ "description": "A short wait beside its words: the light travels, the node breathes.",
73
106
  "path": "examples/spinner/default.tsx",
74
107
  "module": "@krizaka/ui/registry/examples/spinner/default",
75
- "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\nexport default function SpinnerDefault() {\n return <Spinner label=\"Loading\" />;\n}\n"
108
+ "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\n// A short wait in place: light travels around the hexagon, the node breathes. Still and legible under reduced motion.\nexport default function SpinnerDefault() {\n return (\n <p className=\"flex items-center gap-2 text-sm text-fg-secondary\">\n <Spinner label=\"Searching\" size=\"sm\" />\n <span aria-hidden>Searching your library…</span>\n </p>\n );\n}\n"
109
+ },
110
+ {
111
+ "name": "value",
112
+ "title": "A determinate wait",
113
+ "description": "`value` from 0 to 1: the trace grows to what is done, in the same footprint.",
114
+ "path": "examples/spinner/value.tsx",
115
+ "module": "@krizaka/ui/registry/examples/spinner/value",
116
+ "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\n// `value` (0–1): the trace grows to what is done — a determinate wait in the same small footprint.\nexport default function SpinnerValue() {\n return (\n <div className=\"flex items-center gap-4\">\n {[0.25, 0.6, 0.9].map((value) => (\n <Spinner key={value} label={`Uploading, ${Math.round(value * 100)} %`} value={value} size=\"lg\" />\n ))}\n </div>\n );\n}\n"
76
117
  },
77
118
  {
78
119
  "name": "sizes",
79
- "title": "Sizes",
80
- "description": "`sm`, `md`, `lg`.",
120
+ "title": "Sizes and colour",
121
+ "description": "The minimal API: `size` sm · md · lg; the colour is `currentColor`.",
81
122
  "path": "examples/spinner/sizes.tsx",
82
123
  "module": "@krizaka/ui/registry/examples/spinner/sizes",
83
- "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\nexport default function SpinnerSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Spinner label=\"Loading\" size=\"sm\" />\n <Spinner label=\"Loading\" size=\"md\" />\n <Spinner label=\"Loading\" size=\"lg\" />\n </div>\n );\n}\n"
84
- },
85
- {
86
- "name": "muted",
87
- "title": "Muted",
88
- "description": "The colour follows `className`.",
89
- "path": "examples/spinner/muted.tsx",
90
- "module": "@krizaka/ui/registry/examples/spinner/muted",
91
- "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\n// The colour follows `className` (here a text role).\nexport default function SpinnerMuted() {\n return <Spinner label=\"Loading\" className=\"text-fg-secondary\" />;\n}\n"
124
+ "code": "import { Spinner } from \"@krizaka/ui/spinner\";\n\n// The minimal API, last: `size` sm · md · lg; the colour is `currentColor` (the accent by default, or `className`).\nexport default function SpinnerSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Spinner label=\"Loading\" size=\"sm\" />\n <Spinner label=\"Loading\" size=\"md\" />\n <Spinner label=\"Loading\" size=\"lg\" />\n <Spinner label=\"Loading\" size=\"lg\" className=\"text-fg-secondary\" />\n </div>\n );\n}\n"
92
125
  }
93
126
  ]
94
127
  },
@@ -138,19 +171,23 @@
138
171
  "files": [
139
172
  {
140
173
  "path": "meta.ts",
141
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"kbd\"\n | \"mascots\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
174
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
175
+ },
176
+ {
177
+ "path": "spinner/glyph.tsx",
178
+ "content": "// The Krizaka loader glyph, server-safe: a hexagon (the marks' shape) traced by a segment of light around a node that\n// breathes; determinate, it fills to its value; done, the trace closes and a check (or a cross) draws itself. Animated by\n// motion.css (`kz-loader-*`); still under prefers-reduced-motion — a segment at rest and the node lit, never blank.\nimport type * as React from \"react\";\n\n/** The hexagon, pointy top, radius 9 around (12, 12). */\nconst HEX = \"M12 3 19.79 7.5 19.79 16.5 12 21 4.21 16.5 4.21 7.5Z\";\n\n/** What the glyph shows: a wait (with an optional value), or how it ended. */\nexport type LoaderGlyphState = \"loading\" | \"success\" | \"error\";\n\nexport interface LoaderGlyphProps extends Omit<React.ComponentProps<\"svg\">, \"children\"> {\n /** loading · success · error. */\n state?: LoaderGlyphState;\n /** Between 0 and 1: a determinate wait, the trace grows to it. Absent: indeterminate. */\n value?: number | null;\n}\n\n/** The glyph alone (decorative): the Spinner, the Loader and the Button's `loading` draw it. */\nexport function LoaderGlyph({ state = \"loading\", value, ...props }: LoaderGlyphProps) {\n const known = typeof value === \"number\" && Number.isFinite(value);\n const fill = state !== \"loading\" ? 100 : known ? Math.round(Math.min(1, Math.max(0, value)) * 100) : null;\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden data-state={state} data-progress={fill === null ? undefined : fill} {...props}>\n <path d={HEX} stroke=\"currentColor\" strokeOpacity=\"0.22\" strokeWidth=\"2.2\" strokeLinejoin=\"round\" />\n <path\n d={HEX}\n pathLength={100}\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n className=\"kz-loader-trace\"\n strokeDasharray={fill === null ? undefined : `${fill} ${100 - fill}`}\n />\n {state === \"loading\" && <circle cx=\"12\" cy=\"12\" r=\"2.6\" fill=\"currentColor\" className=\"kz-loader-node\" />}\n {state === \"success\" && <path d=\"M8.3 12.3l2.6 2.6 4.9-5.3\" pathLength={20} stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"kz-loader-mark\" />}\n {state === \"error\" && <path d=\"M9.4 9.4l5.2 5.2M14.6 9.4l-5.2 5.2\" pathLength={20} stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" className=\"kz-loader-mark\" />}\n </svg>\n );\n}\n"
142
179
  },
143
180
  {
144
181
  "path": "spinner/index.ts",
145
- "content": "/** @krizaka/ui/spinner — `Spinner`, `spinnerVariants`. Server-safe. */\nexport { Spinner, type SpinnerProps, spinnerVariants } from \"./spinner\";\n"
182
+ "content": "/** @krizaka/ui/spinner — `Spinner`, `spinnerVariants`, and the glyph alone (`LoaderGlyph`). Server-safe. */\nexport { LoaderGlyph, type LoaderGlyphProps, type LoaderGlyphState } from \"./glyph\";\nexport { Spinner, type SpinnerProps, spinnerVariants } from \"./spinner\";\n"
146
183
  },
147
184
  {
148
185
  "path": "spinner/meta.ts",
149
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Spinner\",\n summary: \"An indeterminate wait: a turning ring named by `label`.\",\n status: \"stable\",\n category: \"feedback\",\n platforms: \"both\",\n whenToUse: [\"For a short wait of unknown length: a search, a refresh, an infinite list loading more.\"],\n whenNotToUse: [\n { when: \"When the layout of what is loading is known.\", use: \"skeleton\" },\n { when: \"When the progress is known.\", use: \"progress\" },\n { when: \"Inside a button that runs an action: set `loading` on the button.\", use: \"button\" },\n ],\n bestPractices: [\"One spinner per region: never a page full of them.\", \"Pass `label` translated (“Loading”, “Searching”).\"],\n accessibility: {\n keyboard: [],\n notes: [\"`role=\\\"status\\\"` named by `label`: announced politely.\", \"It stops turning under `prefers-reduced-motion`.\"],\n },\n related: [\"skeleton\", \"progress\", \"button\"],\n web: {\n imports: [\"Spinner\"],\n examples: [\n { name: \"default\", title: \"Default\", description: \"Named by `label`.\" },\n { name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md`, `lg`.\" },\n { name: \"muted\", title: \"Muted\", description: \"The colour follows `className`.\" },\n ],\n },\n native: {\n imports: [\"Spinner\"],\n differences: [\"The platform's `ActivityIndicator`, in the accent.\"],\n examples: [{ name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md`, `lg`, named by `label`.\" }],\n },\n} satisfies ComponentMeta;\n"
186
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Spinner\",\n summary: \"The Krizaka spinner: light traced around a hexagon, its node breathing — indeterminate, or growing to a `value`; still and legible under reduced motion.\",\n status: \"stable\",\n category: \"feedback\",\n platforms: \"both\",\n whenToUse: [\"For a short wait of unknown length: a search, a refresh, an infinite list loading more.\"],\n whenNotToUse: [\n { when: \"When the layout of what is loading is known.\", use: \"skeleton\" },\n { when: \"When the progress is known and the wait is long: a bar says how far.\", use: \"progress\" },\n { when: \"When the wait may be instant (it would flash), or ends with a result to announce.\", use: \"loader\" },\n { when: \"Inside a button that runs an action: set `loading` on the button.\", use: \"button\" },\n ],\n bestPractices: [\n \"One spinner per region: never a page full of them.\",\n \"Pass `label` translated (“Loading”, “Searching”) and, when you know it, `value`: a growing trace reassures more than a loop.\",\n \"A wait that may be instant, that ends in success or failure, or that can last: use `Loader`, which waits before showing itself and hands over to a skeleton.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"`role=\\\"status\\\"` named by `label`: announced politely.\",\n \"Under `prefers-reduced-motion` it rests on a designed still frame: a third of the hexagon traced and the node lit — never a frozen blur.\",\n ],\n },\n related: [\"loader\", \"skeleton\", \"progress\", \"button\"],\n web: {\n imports: [\"Spinner\", \"LoaderGlyph\"],\n examples: [\n { name: \"default\", title: \"Searching in place\", description: \"A short wait beside its words: the light travels, the node breathes.\" },\n { name: \"value\", title: \"A determinate wait\", description: \"`value` from 0 to 1: the trace grows to what is done, in the same footprint.\" },\n { name: \"sizes\", title: \"Sizes and colour\", description: \"The minimal API: `size` sm · md · lg; the colour is `currentColor`.\" },\n ],\n },\n native: {\n imports: [\"Spinner\"],\n differences: [\"The platform's `ActivityIndicator`, in the accent.\"],\n examples: [{ name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md`, `lg`, named by `label`.\" }],\n },\n} satisfies ComponentMeta;\n"
150
187
  },
151
188
  {
152
189
  "path": "spinner/spinner.tsx",
153
- "content": "// Server-safe. An indeterminate wait: an SVG ring, announced as a status with its `label`.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const spinnerVariants = tv({\n base: \"shrink-0 text-accent motion-safe:animate-spin\",\n variants: { size: { sm: \"h-4 w-4\", md: \"h-6 w-6\", lg: \"h-10 w-10\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type SpinnerProps = Omit<React.ComponentProps<\"svg\">, \"children\"> &\n VariantProps<typeof spinnerVariants> & {\n /**\n * sm · md · lg.\n * @default \"md\"\n */\n size?: VariantProps<typeof spinnerVariants>[\"size\"];\n /** The accessible name of the wait, e.g. \"Loading\" — passed translated. */\n label: string;\n };\n\n/** A turning ring with `role=\"status\"` (it stops turning under prefers-reduced-motion). */\nexport function Spinner({ label, size, className, ...props }: SpinnerProps) {\n return (\n <svg\n role=\"status\"\n aria-label={label}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n data-size={size ?? \"md\"}\n className={spinnerVariants({ size, className })}\n {...props}\n >\n <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeOpacity=\"0.2\" strokeWidth=\"3\" />\n <path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n"
190
+ "content": "// Server-safe. An indeterminate wait: the Krizaka loader glyph (a hexagon traced by light around a node), announced as\n// a status with its `label`. For a wait with a delay, a value, an ETA or an ending, see `@krizaka/ui/loader`.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { LoaderGlyph } from \"./glyph\";\n\nexport const spinnerVariants = tv({\n base: \"shrink-0 text-accent\",\n variants: { size: { sm: \"h-4 w-4\", md: \"h-6 w-6\", lg: \"h-10 w-10\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type SpinnerProps = Omit<React.ComponentProps<\"svg\">, \"children\"> &\n VariantProps<typeof spinnerVariants> & {\n /**\n * sm · md · lg.\n * @default \"md\"\n */\n size?: VariantProps<typeof spinnerVariants>[\"size\"];\n /** The accessible name of the wait, e.g. \"Loading\" — passed translated. */\n label: string;\n /** Between 0 and 1: the trace grows to the value (a determinate wait). Absent: it travels around. */\n value?: number | null;\n };\n\n/**\n * The Krizaka spinner: a hexagon traced by a segment of light around a breathing node, in `currentColor` (the accent by\n * default). `role=\"status\"`, named by `label`. Under prefers-reduced-motion it rests — a segment and the node lit.\n */\nexport function Spinner({ label, size, value, className, ...props }: SpinnerProps) {\n return (\n <LoaderGlyph\n role=\"status\"\n aria-label={label}\n aria-hidden={undefined}\n value={value}\n data-size={size ?? \"md\"}\n className={spinnerVariants({ size, className })}\n {...props}\n />\n );\n}\n"
154
191
  }
155
192
  ],
156
193
  "dependencies": [
@@ -159,12 +196,32 @@
159
196
  "registryDependencies": [],
160
197
  "demo": {
161
198
  "path": "examples/spinner/default.tsx",
162
- "content": "import { Spinner } from \"@krizaka/ui/spinner\";\n\nexport default function SpinnerDefault() {\n return <Spinner label=\"Loading\" />;\n}\n"
199
+ "content": "import { Spinner } from \"@krizaka/ui/spinner\";\n\n// A short wait in place: light travels around the hexagon, the node breathes. Still and legible under reduced motion.\nexport default function SpinnerDefault() {\n return (\n <p className=\"flex items-center gap-2 text-sm text-fg-secondary\">\n <Spinner label=\"Searching\" size=\"sm\" />\n <span aria-hidden>Searching your library…</span>\n </p>\n );\n}\n"
163
200
  },
164
201
  "props": [
202
+ {
203
+ "component": "LoaderGlyph",
204
+ "description": "The glyph alone (decorative): the Spinner, the Loader and the Button's `loading` draw it.",
205
+ "props": [
206
+ {
207
+ "name": "state",
208
+ "type": "LoaderGlyphState",
209
+ "default": "loading",
210
+ "description": "loading · success · error.",
211
+ "required": false
212
+ },
213
+ {
214
+ "name": "value",
215
+ "type": "number | null",
216
+ "default": null,
217
+ "description": "Between 0 and 1: a determinate wait, the trace grows to it. Absent: indeterminate.",
218
+ "required": false
219
+ }
220
+ ]
221
+ },
165
222
  {
166
223
  "component": "Spinner",
167
- "description": "A turning ring with `role=\"status\"` (it stops turning under prefers-reduced-motion).",
224
+ "description": "The Krizaka spinner: a hexagon traced by a segment of light around a breathing node, in `currentColor` (the accent by\ndefault). `role=\"status\"`, named by `label`. Under prefers-reduced-motion it rests — a segment and the node lit.",
168
225
  "props": [
169
226
  {
170
227
  "name": "label",
@@ -179,6 +236,13 @@
179
236
  "default": "md",
180
237
  "description": "sm · md · lg.",
181
238
  "required": false
239
+ },
240
+ {
241
+ "name": "value",
242
+ "type": "number | null",
243
+ "default": null,
244
+ "description": "Between 0 and 1: the trace grows to the value (a determinate wait). Absent: it travels around.",
245
+ "required": false
182
246
  }
183
247
  ]
184
248
  }
@@ -134,7 +134,7 @@
134
134
  "files": [
135
135
  {
136
136
  "path": "meta.ts",
137
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"kbd\"\n | \"mascots\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
137
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"alert\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"cn\"\n | \"command\"\n | \"confirm-button\"\n | \"countdown\"\n | \"dialog\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"field\"\n | \"glow-card\"\n | \"iso\"\n | \"kbd\"\n | \"loader\"\n | \"marquee\"\n | \"mascots\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page-header\"\n | \"popover\"\n | \"progress\"\n | \"radio-group\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"stat\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"theme\"\n | \"toast\"\n | \"tooltip\"\n | \"txt\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/** The group of the catalogue. */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
138
138
  },
139
139
  {
140
140
  "path": "stat/index.ts",