@krizaka/ui 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +17 -7
  2. package/dist/{button-Cgfkg_3y.d.ts → button-BMAO7dKF.d.ts} +29 -2
  3. package/dist/button.d.ts +32 -5
  4. package/dist/button.js +62 -9
  5. package/dist/button.js.map +1 -1
  6. package/dist/chip-link.d.ts +53 -0
  7. package/dist/chip-link.js +32 -0
  8. package/dist/chip-link.js.map +1 -0
  9. package/dist/chunk-43PTCDTL.js +31 -0
  10. package/dist/chunk-43PTCDTL.js.map +1 -0
  11. package/dist/chunk-6ISW63JI.js +11 -0
  12. package/dist/chunk-6ISW63JI.js.map +1 -0
  13. package/dist/chunk-HAQWKORZ.js +117 -0
  14. package/dist/chunk-HAQWKORZ.js.map +1 -0
  15. package/dist/{chunk-FHYZSO2S.js → chunk-R6SDS7JM.js} +5 -3
  16. package/dist/chunk-R6SDS7JM.js.map +1 -0
  17. package/dist/chunk-RUPNPMP3.js +103 -0
  18. package/dist/chunk-RUPNPMP3.js.map +1 -0
  19. package/dist/chunk-VDR2EP6K.js +49 -0
  20. package/dist/chunk-VDR2EP6K.js.map +1 -0
  21. package/dist/chunk-WEVYKZIF.js +33 -0
  22. package/dist/chunk-WEVYKZIF.js.map +1 -0
  23. package/dist/command.d.ts +7 -7
  24. package/dist/command.js +3 -2
  25. package/dist/command.js.map +1 -1
  26. package/dist/confirm-button.d.ts +1 -1
  27. package/dist/confirm-button.js +2 -1
  28. package/dist/confirm-button.js.map +1 -1
  29. package/dist/{dialog-GQ4Nvuyf.d.ts → dialog-D42QwYb_.d.ts} +11 -1
  30. package/dist/dialog.d.ts +1 -1
  31. package/dist/dialog.js +3 -2
  32. package/dist/dialog.js.map +1 -1
  33. package/dist/glow-card.d.ts +20 -0
  34. package/dist/glow-card.js +30 -0
  35. package/dist/glow-card.js.map +1 -0
  36. package/dist/index.d.ts +2 -20
  37. package/dist/index.js +7 -64
  38. package/dist/index.js.map +1 -1
  39. package/dist/iso.d.ts +400 -0
  40. package/dist/iso.js +363 -0
  41. package/dist/iso.js.map +1 -0
  42. package/dist/loader.d.ts +206 -0
  43. package/dist/loader.js +138 -0
  44. package/dist/loader.js.map +1 -0
  45. package/dist/marquee.d.ts +33 -0
  46. package/dist/marquee.js +52 -0
  47. package/dist/marquee.js.map +1 -0
  48. package/dist/meter.d.ts +146 -0
  49. package/dist/meter.js +107 -0
  50. package/dist/meter.js.map +1 -0
  51. package/dist/motion.css +394 -3
  52. package/dist/motion.d.ts +46 -0
  53. package/dist/motion.js +10 -0
  54. package/dist/motion.js.map +1 -0
  55. package/dist/nav-bar.d.ts +178 -0
  56. package/dist/nav-bar.js +157 -0
  57. package/dist/nav-bar.js.map +1 -0
  58. package/dist/number-ticker.d.ts +32 -0
  59. package/dist/number-ticker.js +61 -0
  60. package/dist/number-ticker.js.map +1 -0
  61. package/dist/progress.d.ts +56 -1
  62. package/dist/progress.js +72 -25
  63. package/dist/progress.js.map +1 -1
  64. package/dist/skeleton.d.ts +18 -3
  65. package/dist/skeleton.js +9 -1
  66. package/dist/skeleton.js.map +1 -1
  67. package/dist/spinner.d.ts +21 -5
  68. package/dist/spinner.js +13 -12
  69. package/dist/spinner.js.map +1 -1
  70. package/dist/stepper.d.ts +112 -0
  71. package/dist/stepper.js +67 -0
  72. package/dist/stepper.js.map +1 -0
  73. package/dist/tabs.d.ts +1 -1
  74. package/dist/tabs.js +14 -8
  75. package/dist/tabs.js.map +1 -1
  76. package/dist/theme.d.ts +1 -1
  77. package/dist/theme.js +2 -1
  78. package/dist/theme.js.map +1 -1
  79. package/dist/toast.d.ts +39 -4
  80. package/dist/toast.js +81 -7
  81. package/dist/toast.js.map +1 -1
  82. package/package.json +148 -8
  83. package/registry/alert.json +1 -1
  84. package/registry/avatar.json +1 -1
  85. package/registry/badge.json +1 -1
  86. package/registry/button.json +232 -26
  87. package/registry/card.json +1 -1
  88. package/registry/checkbox.json +1 -1
  89. package/registry/chip-link.json +158 -0
  90. package/registry/chip.json +1 -1
  91. package/registry/command.json +1 -1
  92. package/registry/confirm-button.json +91 -7
  93. package/registry/countdown.json +1 -1
  94. package/registry/dialog.json +15 -7
  95. package/registry/dropdown-menu.json +1 -1
  96. package/registry/empty-state.json +1 -1
  97. package/registry/examples/button/error.js +11 -0
  98. package/registry/examples/button/error.tsx +10 -0
  99. package/registry/examples/button/loading.tsx +1 -1
  100. package/registry/examples/button/save-flow.d.ts +4 -0
  101. package/registry/examples/button/save-flow.js +28 -0
  102. package/registry/examples/button/save-flow.tsx +24 -0
  103. package/registry/examples/button/signature.d.ts +4 -0
  104. package/registry/examples/button/signature.js +17 -0
  105. package/registry/examples/button/signature.tsx +19 -0
  106. package/registry/examples/button/upload-progress.d.ts +4 -0
  107. package/registry/examples/button/upload-progress.js +23 -0
  108. package/registry/examples/button/upload-progress.tsx +26 -0
  109. package/registry/examples/chip-link/filters.d.ts +4 -0
  110. package/registry/examples/chip-link/filters.js +12 -0
  111. package/registry/examples/chip-link/filters.tsx +17 -0
  112. package/registry/examples/dialog/left.d.ts +4 -0
  113. package/registry/examples/dialog/left.js +22 -0
  114. package/registry/examples/dialog/left.tsx +32 -0
  115. package/registry/examples/glow-card/link-card.d.ts +4 -0
  116. package/registry/examples/glow-card/link-card.js +14 -0
  117. package/registry/examples/glow-card/link-card.tsx +13 -0
  118. package/registry/examples/glow-card/plans.d.ts +4 -0
  119. package/registry/examples/glow-card/plans.js +25 -0
  120. package/registry/examples/glow-card/plans.tsx +26 -0
  121. package/registry/examples/glow-card/spotlight.d.ts +4 -0
  122. package/registry/examples/glow-card/spotlight.js +14 -0
  123. package/registry/examples/glow-card/spotlight.tsx +15 -0
  124. package/registry/examples/iso/compose.d.ts +4 -0
  125. package/registry/examples/iso/compose.js +22 -0
  126. package/registry/examples/iso/compose.tsx +19 -0
  127. package/registry/examples/iso/orazaka-hero.d.ts +4 -0
  128. package/registry/examples/iso/orazaka-hero.js +26 -0
  129. package/registry/examples/iso/orazaka-hero.tsx +21 -0
  130. package/registry/examples/iso/orochia-hero.d.ts +4 -0
  131. package/registry/examples/iso/orochia-hero.js +29 -0
  132. package/registry/examples/iso/orochia-hero.tsx +23 -0
  133. package/registry/examples/iso/still.d.ts +4 -0
  134. package/registry/examples/iso/still.js +21 -0
  135. package/registry/examples/iso/still.tsx +15 -0
  136. package/registry/examples/loader/endings.d.ts +4 -0
  137. package/registry/examples/loader/endings.js +14 -0
  138. package/registry/examples/loader/endings.tsx +11 -0
  139. package/registry/examples/loader/fetch-flow.d.ts +4 -0
  140. package/registry/examples/loader/fetch-flow.js +24 -0
  141. package/registry/examples/loader/fetch-flow.tsx +24 -0
  142. package/registry/examples/loader/minimal.d.ts +4 -0
  143. package/registry/examples/loader/minimal.js +11 -0
  144. package/registry/examples/loader/minimal.tsx +6 -0
  145. package/registry/examples/loader/overlay.d.ts +4 -0
  146. package/registry/examples/loader/overlay.js +16 -0
  147. package/registry/examples/loader/overlay.tsx +13 -0
  148. package/registry/examples/loader/region-handover.d.ts +4 -0
  149. package/registry/examples/loader/region-handover.js +42 -0
  150. package/registry/examples/loader/region-handover.tsx +29 -0
  151. package/registry/examples/loader/upload-eta.d.ts +4 -0
  152. package/registry/examples/loader/upload-eta.js +11 -0
  153. package/registry/examples/loader/upload-eta.tsx +10 -0
  154. package/registry/examples/marquee/cards.d.ts +4 -0
  155. package/registry/examples/marquee/cards.js +23 -0
  156. package/registry/examples/marquee/cards.tsx +21 -0
  157. package/registry/examples/marquee/logos.d.ts +4 -0
  158. package/registry/examples/marquee/logos.js +12 -0
  159. package/registry/examples/marquee/logos.tsx +17 -0
  160. package/registry/examples/meter/balance.d.ts +4 -0
  161. package/registry/examples/meter/balance.js +14 -0
  162. package/registry/examples/meter/balance.tsx +15 -0
  163. package/registry/examples/meter/quota.d.ts +4 -0
  164. package/registry/examples/meter/quota.js +15 -0
  165. package/registry/examples/meter/quota.tsx +12 -0
  166. package/registry/examples/meter/split.d.ts +4 -0
  167. package/registry/examples/meter/split.js +18 -0
  168. package/registry/examples/meter/split.tsx +18 -0
  169. package/registry/examples/nav-bar/app-bar.d.ts +4 -0
  170. package/registry/examples/nav-bar/app-bar.js +22 -0
  171. package/registry/examples/nav-bar/app-bar.tsx +21 -0
  172. package/registry/examples/nav-bar/menu-open.d.ts +4 -0
  173. package/registry/examples/nav-bar/menu-open.js +26 -0
  174. package/registry/examples/nav-bar/menu-open.tsx +26 -0
  175. package/registry/examples/nav-bar/site.d.ts +4 -0
  176. package/registry/examples/nav-bar/site.js +31 -0
  177. package/registry/examples/nav-bar/site.tsx +36 -0
  178. package/registry/examples/number-ticker/gradient.d.ts +4 -0
  179. package/registry/examples/number-ticker/gradient.js +11 -0
  180. package/registry/examples/number-ticker/gradient.tsx +10 -0
  181. package/registry/examples/number-ticker/live-total.d.ts +4 -0
  182. package/registry/examples/number-ticker/live-total.js +17 -0
  183. package/registry/examples/number-ticker/live-total.tsx +21 -0
  184. package/registry/examples/number-ticker/stats.d.ts +4 -0
  185. package/registry/examples/number-ticker/stats.js +19 -0
  186. package/registry/examples/number-ticker/stats.tsx +23 -0
  187. package/registry/examples/progress/buffer.d.ts +4 -0
  188. package/registry/examples/progress/buffer.js +11 -0
  189. package/registry/examples/progress/buffer.tsx +10 -0
  190. package/registry/examples/progress/statuses.d.ts +4 -0
  191. package/registry/examples/progress/statuses.js +14 -0
  192. package/registry/examples/progress/statuses.tsx +11 -0
  193. package/registry/examples/progress/steps.d.ts +4 -0
  194. package/registry/examples/progress/steps.js +14 -0
  195. package/registry/examples/progress/steps.tsx +13 -0
  196. package/registry/examples/progress/upload.d.ts +4 -0
  197. package/registry/examples/progress/upload.js +11 -0
  198. package/registry/examples/progress/upload.tsx +10 -0
  199. package/registry/examples/skeleton/paragraph.d.ts +4 -0
  200. package/registry/examples/skeleton/paragraph.js +14 -0
  201. package/registry/examples/skeleton/paragraph.tsx +12 -0
  202. package/registry/examples/skeleton/shape-of-content.d.ts +4 -0
  203. package/registry/examples/skeleton/shape-of-content.js +23 -0
  204. package/registry/examples/skeleton/shape-of-content.tsx +26 -0
  205. package/registry/examples/spinner/default.js +5 -2
  206. package/registry/examples/spinner/default.tsx +7 -1
  207. package/registry/examples/spinner/sizes.js +2 -1
  208. package/registry/examples/spinner/sizes.tsx +2 -0
  209. package/registry/examples/spinner/value.d.ts +4 -0
  210. package/registry/examples/spinner/value.js +11 -0
  211. package/registry/examples/spinner/value.tsx +12 -0
  212. package/registry/examples/stepper/checkout.d.ts +4 -0
  213. package/registry/examples/stepper/checkout.js +22 -0
  214. package/registry/examples/stepper/checkout.tsx +17 -0
  215. package/registry/examples/stepper/failed.d.ts +4 -0
  216. package/registry/examples/stepper/failed.js +26 -0
  217. package/registry/examples/stepper/failed.tsx +21 -0
  218. package/registry/examples/stepper/job.d.ts +4 -0
  219. package/registry/examples/stepper/job.js +32 -0
  220. package/registry/examples/stepper/job.tsx +28 -0
  221. package/registry/examples/toast/grouped.d.ts +4 -0
  222. package/registry/examples/toast/grouped.js +21 -0
  223. package/registry/examples/toast/grouped.tsx +23 -0
  224. package/registry/examples/toast/undo.d.ts +4 -0
  225. package/registry/examples/toast/undo.js +29 -0
  226. package/registry/examples/toast/undo.tsx +30 -0
  227. package/registry/examples/toast/upload-progress.d.ts +4 -0
  228. package/registry/examples/toast/upload-progress.js +29 -0
  229. package/registry/examples/toast/upload-progress.tsx +31 -0
  230. package/registry/field.json +1 -1
  231. package/registry/glow-card.json +162 -0
  232. package/registry/index.json +191 -20
  233. package/registry/iso.json +1355 -0
  234. package/registry/kbd.json +1 -1
  235. package/registry/loader.json +399 -0
  236. package/registry/marquee.json +225 -0
  237. package/registry/mascots.json +3 -3
  238. package/registry/meter.json +324 -0
  239. package/registry/nav-bar.json +480 -0
  240. package/registry/number-ticker.json +219 -0
  241. package/registry/page-header.json +1 -1
  242. package/registry/popover.json +1 -1
  243. package/registry/progress.json +116 -11
  244. package/registry/radio-group.json +1 -1
  245. package/registry/section-backdrop.json +1 -1
  246. package/registry/separator.json +1 -1
  247. package/registry/skeleton.json +69 -13
  248. package/registry/slider.json +1 -1
  249. package/registry/slot.json +1 -1
  250. package/registry/spinner.json +91 -27
  251. package/registry/stat.json +1 -1
  252. package/registry/stepper.json +174 -0
  253. package/registry/switch.json +1 -1
  254. package/registry/tabs.json +11 -7
  255. package/registry/theme.json +3 -3
  256. package/registry/toast.json +41 -13
  257. package/registry/tooltip.json +1 -1
  258. package/dist/chunk-FHYZSO2S.js.map +0 -1
  259. package/dist/chunk-PESA2R4U.js +0 -51
  260. package/dist/chunk-PESA2R4U.js.map +0 -1
  261. package/registry/examples/spinner/muted.js +0 -11
  262. package/registry/examples/spinner/muted.tsx +0 -6
  263. /package/registry/examples/{spinner/muted.d.ts → button/error.d.ts} +0 -0
@@ -0,0 +1,225 @@
1
+ {
2
+ "name": "marquee",
3
+ "type": "primitive",
4
+ "title": "Marquee",
5
+ "summary": "A band of items that slides forever — at a speed in pixels per second, paused by the pointer, the focus or its own pause button, read once by assistive technology, and a still row you scroll under reduced motion.",
6
+ "description": "A band of items that slides forever — at a speed in pixels per second, paused by the pointer, the focus or its own pause button, read once by assistive technology, and a still row you scroll under reduced motion.",
7
+ "status": "beta",
8
+ "category": "data-display",
9
+ "platforms": "web",
10
+ "whenToUse": [
11
+ "A band of logos, names or short quotes on a landing page: proof at a glance.",
12
+ "Short cards that need no comparison (testimonials, recent payouts) — one band per page."
13
+ ],
14
+ "whenNotToUse": [
15
+ {
16
+ "when": "For content the reader must read in order or act on: a list stays still."
17
+ },
18
+ {
19
+ "when": "For a few items that fit on one line: show them, still."
20
+ }
21
+ ],
22
+ "bestPractices": [
23
+ "Keep it slow (24–40 px/s) and short; one band per page.",
24
+ "Items that make sense alone: the reader catches them in any order.",
25
+ "Pass `label`, `pauseLabel` and `playLabel` translated: the band is a named region with a real pause button."
26
+ ],
27
+ "accessibility": {
28
+ "keyboard": [
29
+ {
30
+ "keys": "Tab",
31
+ "action": "Focuses the band (it pauses; under reduced motion the arrows scroll it), the links inside, then the pause button."
32
+ },
33
+ {
34
+ "keys": "Enter / Space",
35
+ "action": "On the pause button: stops or restarts the band."
36
+ }
37
+ ],
38
+ "notes": [
39
+ "A `section` named by `label`; the items are a list, and the second copy (for the loop) is hidden from assistive technology.",
40
+ "The pause button (`aria-pressed`) meets WCAG 2.2.2: motion that lasts can be stopped.",
41
+ "Under `prefers-reduced-motion` the band is still, its copy removed, and it scrolls by hand; off screen it pauses (MotionObserver)."
42
+ ]
43
+ },
44
+ "related": [
45
+ "glow-card",
46
+ "number-ticker"
47
+ ],
48
+ "web": {
49
+ "entry": "@krizaka/ui/marquee",
50
+ "import": "import { Marquee } from \"@krizaka/ui/marquee\";",
51
+ "props": [
52
+ {
53
+ "component": "Marquee",
54
+ "description": "A band of items sliding forever — pausable, accessible, still under reduced motion.",
55
+ "props": [
56
+ {
57
+ "name": "children",
58
+ "type": "ReactNode",
59
+ "default": null,
60
+ "description": "The items: logos, words, cards.",
61
+ "required": true
62
+ },
63
+ {
64
+ "name": "label",
65
+ "type": "string",
66
+ "default": null,
67
+ "description": "The accessible name of the band (“Our partners”) — passed translated.",
68
+ "required": true
69
+ },
70
+ {
71
+ "name": "pauseLabel",
72
+ "type": "string",
73
+ "default": null,
74
+ "description": "The pause button's name (“Pause the band”) — passed translated.",
75
+ "required": true
76
+ },
77
+ {
78
+ "name": "playLabel",
79
+ "type": "string",
80
+ "default": null,
81
+ "description": "The same button's name once paused (“Play the band”) — passed translated.",
82
+ "required": true
83
+ },
84
+ {
85
+ "name": "defaultPaused",
86
+ "type": "boolean",
87
+ "default": "false",
88
+ "description": "Starts paused.",
89
+ "required": false
90
+ },
91
+ {
92
+ "name": "direction",
93
+ "type": "\"right\" | \"left\"",
94
+ "default": "left",
95
+ "description": "Which way it slides.",
96
+ "required": false
97
+ },
98
+ {
99
+ "name": "fade",
100
+ "type": "boolean",
101
+ "default": "true",
102
+ "description": "The edges fade into the page.",
103
+ "required": false
104
+ },
105
+ {
106
+ "name": "speed",
107
+ "type": "number",
108
+ "default": "40",
109
+ "description": "Pixels per second.",
110
+ "required": false
111
+ }
112
+ ]
113
+ }
114
+ ],
115
+ "examples": [
116
+ {
117
+ "name": "logos",
118
+ "title": "A band of names",
119
+ "description": "Paused by the pointer, the focus or its button; a still row under reduced motion.",
120
+ "path": "examples/marquee/logos.tsx",
121
+ "module": "@krizaka/ui/registry/examples/marquee/logos",
122
+ "code": "import { Marquee } from \"@krizaka/ui/marquee\";\n\nconst names = [\"Postgres\", \"Ollama\", \"Spring\", \"Kafka\", \"Keycloak\", \"Qdrant\", \"MinIO\", \"Grafana\"];\n\n// A band of names at 40 px/s: it pauses under the pointer or the focus, and its pause button (top right, on hover or\n// focus) stops it for good. Under reduced motion it is a still row you scroll.\nexport default function MarqueeLogos() {\n return (\n <Marquee label=\"Built on open source\" pauseLabel=\"Pause the band\" playLabel=\"Play the band\" className=\"w-full max-w-2xl\">\n {names.map((name) => (\n <span key={name} className=\"font-display text-lg font-semibold text-fg-secondary\">\n {name}\n </span>\n ))}\n </Marquee>\n );\n}\n"
123
+ },
124
+ {
125
+ "name": "cards",
126
+ "title": "Testimonials",
127
+ "description": "Cards sliding the other way, slower; each one read once.",
128
+ "path": "examples/marquee/cards.tsx",
129
+ "module": "@krizaka/ui/registry/examples/marquee/cards",
130
+ "code": "import { Marquee } from \"@krizaka/ui/marquee\";\n\nconst quotes = [\n { who: \"Maya, illustrator\", text: \"My supporters see exactly where their tip goes.\" },\n { who: \"Eric, filmmaker\", text: \"I sold a cut to the highest bidder, live.\" },\n { who: \"Sam, musician\", text: \"90 % to me — and it shows on every payout.\" },\n];\n\n// Cards sliding the other way, slower; each card's text is read once (the copy is hidden from assistive technology).\nexport default function MarqueeCards() {\n return (\n <Marquee label=\"What creators say\" pauseLabel=\"Pause\" playLabel=\"Play\" direction=\"right\" speed={24} className=\"w-full max-w-2xl\">\n {quotes.map((q) => (\n <figure key={q.who} className=\"w-72 rounded-xl border border-border-default bg-surface-1 p-4\">\n <blockquote className=\"text-sm text-fg\">“{q.text}”</blockquote>\n <figcaption className=\"mt-2 text-xs text-fg-secondary\">{q.who}</figcaption>\n </figure>\n ))}\n </Marquee>\n );\n}\n"
131
+ }
132
+ ]
133
+ },
134
+ "native": null,
135
+ "files": [
136
+ {
137
+ "path": "marquee/index.ts",
138
+ "content": "/** @krizaka/ui/marquee — `Marquee`: a band that slides, pausable (pointer, focus, a button), still under reduced motion. Client. */\nexport { Marquee, type MarqueeProps } from \"./marquee\";\n"
139
+ },
140
+ {
141
+ "path": "marquee/marquee.tsx",
142
+ "content": "// Client (a pause button, a measure). A band that slides — and that anyone can stop: it pauses under the pointer and\n// while something inside has the focus, it carries a visible pause button (WCAG 2.2.2: motion longer than 5 s can be\n// paused), its speed is in pixels per second whatever its width (a long band does not race), its copy is hidden from\n// assistive technology (each item is read once), and under reduced motion it is a still row you scroll by hand.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\n\nexport type MarqueeProps = Omit<React.ComponentProps<\"section\">, \"children\"> & {\n /** The accessible name of the band (“Our partners”) — passed translated. */\n label: string;\n /** The pause button's name (“Pause the band”) — passed translated. */\n pauseLabel: string;\n /** The same button's name once paused (“Play the band”) — passed translated. */\n playLabel: string;\n /** The items: logos, words, cards. */\n children: React.ReactNode;\n /**\n * Pixels per second.\n * @default 40\n */\n speed?: number;\n /**\n * Which way it slides.\n * @default \"left\"\n */\n direction?: \"left\" | \"right\";\n /**\n * The edges fade into the page.\n * @default true\n */\n fade?: boolean;\n /** Starts paused. */\n defaultPaused?: boolean;\n};\n\n/** A band of items sliding forever — pausable, accessible, still under reduced motion. */\nexport function Marquee({ label, pauseLabel, playLabel, children, speed = 40, direction = \"left\", fade = true, defaultPaused = false, className, ...props }: MarqueeProps) {\n const [paused, setPaused] = React.useState(defaultPaused);\n const track = React.useRef<HTMLDivElement>(null);\n React.useEffect(() => {\n const el = track.current;\n if (!el) return;\n const measure = () => el.style.setProperty(\"--kz-marquee-duration\", `${Math.max(4, el.scrollWidth / 2 / Math.max(1, speed))}s`);\n measure();\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n return () => ro.disconnect();\n }, [speed]);\n return (\n <section aria-label={label} data-paused={paused ? \"\" : undefined} data-direction={direction} data-fade={fade ? \"\" : undefined} className={cn(\"kz-band group/marquee relative\", className)} {...props}>\n {/* Focusable: the keyboard stops the band (focus-within) and, still under reduced motion, scrolls the row. */}\n <div\n tabIndex={0}\n className=\"kz-band-viewport overflow-hidden rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {/* Each copy ends with the gap: two copies are exactly twice one, so the loop never jumps. */}\n <div ref={track} className=\"kz-marquee kz-loop\">\n <ul className=\"flex shrink-0 items-center gap-(--kz-band-gap) pr-(--kz-band-gap)\">{wrap(children)}</ul>\n <ul aria-hidden className=\"kz-band-copy flex shrink-0 items-center gap-(--kz-band-gap) pr-(--kz-band-gap)\">\n {wrap(children)}\n </ul>\n </div>\n </div>\n <button\n type=\"button\"\n aria-pressed={paused}\n aria-label={paused ? playLabel : pauseLabel}\n onClick={() => setPaused(!paused)}\n className=\"absolute right-1 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full border border-border-default bg-surface-1/90 text-fg-secondary opacity-0 shadow-sm transition-opacity hover:text-fg focus-visible:opacity-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring group-hover/marquee:opacity-100 aria-pressed:opacity-100 motion-reduce:hidden\"\n >\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"currentColor\" aria-hidden>\n {paused ? <path d=\"M8 5.5v13l10-6.5Z\" /> : <path d=\"M7 5h3.5v14H7zM13.5 5H17v14h-3.5z\" />}\n </svg>\n </button>\n </section>\n );\n}\n\nfunction wrap(children: React.ReactNode) {\n return React.Children.map(children, (child) => <li className=\"shrink-0\">{child}</li>);\n}\n"
143
+ },
144
+ {
145
+ "path": "marquee/meta.ts",
146
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Marquee\",\n summary:\n \"A band of items that slides forever — at a speed in pixels per second, paused by the pointer, the focus or its own pause button, read once by assistive technology, and a still row you scroll under reduced motion.\",\n status: \"beta\",\n category: \"data-display\",\n platforms: \"web\",\n whenToUse: [\n \"A band of logos, names or short quotes on a landing page: proof at a glance.\",\n \"Short cards that need no comparison (testimonials, recent payouts) — one band per page.\",\n ],\n whenNotToUse: [\n { when: \"For content the reader must read in order or act on: a list stays still.\" },\n { when: \"For a few items that fit on one line: show them, still.\" },\n ],\n bestPractices: [\n \"Keep it slow (24–40 px/s) and short; one band per page.\",\n \"Items that make sense alone: the reader catches them in any order.\",\n \"Pass `label`, `pauseLabel` and `playLabel` translated: the band is a named region with a real pause button.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Tab\", action: \"Focuses the band (it pauses; under reduced motion the arrows scroll it), the links inside, then the pause button.\" },\n { keys: \"Enter / Space\", action: \"On the pause button: stops or restarts the band.\" },\n ],\n notes: [\n \"A `section` named by `label`; the items are a list, and the second copy (for the loop) is hidden from assistive technology.\",\n \"The pause button (`aria-pressed`) meets WCAG 2.2.2: motion that lasts can be stopped.\",\n \"Under `prefers-reduced-motion` the band is still, its copy removed, and it scrolls by hand; off screen it pauses (MotionObserver).\",\n ],\n },\n related: [\"glow-card\", \"number-ticker\"],\n web: {\n imports: [\"Marquee\"],\n examples: [\n { name: \"logos\", title: \"A band of names\", description: \"Paused by the pointer, the focus or its button; a still row under reduced motion.\" },\n { name: \"cards\", title: \"Testimonials\", description: \"Cards sliding the other way, slower; each one read once.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
147
+ },
148
+ {
149
+ "path": "meta.ts",
150
+ "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"
151
+ }
152
+ ],
153
+ "dependencies": [],
154
+ "registryDependencies": [
155
+ "cn"
156
+ ],
157
+ "demo": {
158
+ "path": "examples/marquee/logos.tsx",
159
+ "content": "import { Marquee } from \"@krizaka/ui/marquee\";\n\nconst names = [\"Postgres\", \"Ollama\", \"Spring\", \"Kafka\", \"Keycloak\", \"Qdrant\", \"MinIO\", \"Grafana\"];\n\n// A band of names at 40 px/s: it pauses under the pointer or the focus, and its pause button (top right, on hover or\n// focus) stops it for good. Under reduced motion it is a still row you scroll.\nexport default function MarqueeLogos() {\n return (\n <Marquee label=\"Built on open source\" pauseLabel=\"Pause the band\" playLabel=\"Play the band\" className=\"w-full max-w-2xl\">\n {names.map((name) => (\n <span key={name} className=\"font-display text-lg font-semibold text-fg-secondary\">\n {name}\n </span>\n ))}\n </Marquee>\n );\n}\n"
160
+ },
161
+ "props": [
162
+ {
163
+ "component": "Marquee",
164
+ "description": "A band of items sliding forever — pausable, accessible, still under reduced motion.",
165
+ "props": [
166
+ {
167
+ "name": "children",
168
+ "type": "ReactNode",
169
+ "default": null,
170
+ "description": "The items: logos, words, cards.",
171
+ "required": true
172
+ },
173
+ {
174
+ "name": "label",
175
+ "type": "string",
176
+ "default": null,
177
+ "description": "The accessible name of the band (“Our partners”) — passed translated.",
178
+ "required": true
179
+ },
180
+ {
181
+ "name": "pauseLabel",
182
+ "type": "string",
183
+ "default": null,
184
+ "description": "The pause button's name (“Pause the band”) — passed translated.",
185
+ "required": true
186
+ },
187
+ {
188
+ "name": "playLabel",
189
+ "type": "string",
190
+ "default": null,
191
+ "description": "The same button's name once paused (“Play the band”) — passed translated.",
192
+ "required": true
193
+ },
194
+ {
195
+ "name": "defaultPaused",
196
+ "type": "boolean",
197
+ "default": "false",
198
+ "description": "Starts paused.",
199
+ "required": false
200
+ },
201
+ {
202
+ "name": "direction",
203
+ "type": "\"right\" | \"left\"",
204
+ "default": "left",
205
+ "description": "Which way it slides.",
206
+ "required": false
207
+ },
208
+ {
209
+ "name": "fade",
210
+ "type": "boolean",
211
+ "default": "true",
212
+ "description": "The edges fade into the page.",
213
+ "required": false
214
+ },
215
+ {
216
+ "name": "speed",
217
+ "type": "number",
218
+ "default": "40",
219
+ "description": "Pixels per second.",
220
+ "required": false
221
+ }
222
+ ]
223
+ }
224
+ ]
225
+ }
@@ -77,7 +77,7 @@
77
77
  },
78
78
  {
79
79
  "name": "mood",
80
- "type": "\"success\" | \"empty\" | \"error\" | \"welcome\" | \"thinking\"",
80
+ "type": "\"success\" | \"error\" | \"empty\" | \"welcome\" | \"thinking\"",
81
81
  "default": "welcome",
82
82
  "description": "What the moment is: `welcome` (onboarding), `thinking` (a long task running), `success` (it is done), `empty` (nothing here yet), `error` (it failed).",
83
83
  "required": false
@@ -247,7 +247,7 @@
247
247
  },
248
248
  {
249
249
  "path": "meta.ts",
250
- "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"
250
+ "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"
251
251
  }
252
252
  ],
253
253
  "dependencies": [],
@@ -282,7 +282,7 @@
282
282
  },
283
283
  {
284
284
  "name": "mood",
285
- "type": "\"success\" | \"empty\" | \"error\" | \"welcome\" | \"thinking\"",
285
+ "type": "\"success\" | \"error\" | \"empty\" | \"welcome\" | \"thinking\"",
286
286
  "default": "welcome",
287
287
  "description": "What the moment is: `welcome` (onboarding), `thinking` (a long task running), `success` (it is done), `empty` (nothing here yet), `error` (it failed).",
288
288
  "required": false