@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
package/README.md CHANGED
@@ -124,18 +124,19 @@ light or a product's — is a set of tokens.
124
124
  | :-- | :-- | :-- |
125
125
  | `@krizaka/ui/cn` | `cn(...classes)` — clsx + tailwind-merge (the engine `tailwind-variants` ships) | server |
126
126
  | `@krizaka/ui/slot` | `Slot`, `VisuallyHidden` (Radix) | server |
127
- | `@krizaka/ui/button` | `Button` (`variant` primary·gradient·secondary·outline·ghost·danger, `size` sm·md·lg·icon, `shape` rounded·pill, `loading`, `asChild`), `IconButton` (`label` required), `buttonVariants` | server |
127
+ | `@krizaka/ui/button` | `Button` (`variant` primary·gradient·secondary·outline·ghost·danger, `size` sm·md·lg·icon, `shape` rounded·pill·cut, `loading` (the loader glyph; `indicator={false}` to draw your own), `progress`, `status` success·error, `announce`, `shimmer`, `magnetic`, `asChild`), `IconButton` (`label` required; the glyph takes the icon's place), `buttonVariants` | server |
128
128
  | `@krizaka/ui/badge` | `Badge` (`tone` neutral·accent·success·warning·danger·scrim, `size` sm·md, `dot`, `pulse`), `badgeVariants` | server |
129
129
  | `@krizaka/ui/avatar` | `Avatar` (`src`, `alt`, `fallback`, `size` xs·sm·md·lg·xl), `Avatar.Group` (`max`) | client |
130
130
  | `@krizaka/ui/field` | `Field.Root/Label/Hint/Error`, `Input`, `Textarea`, `Select` (native, styled) — `invalid` | server |
131
131
  | `@krizaka/ui/theme` | `ThemeScript` (in `<head>`, no flash), `ThemeProvider`, `useTheme()` → `{ mode, setMode, theme, setTheme }`, `ThemeToggle` (`label`) | client |
132
- | `@krizaka/ui/skeleton` | `Skeleton` (`shape` text·circle·rect) | server |
132
+ | `@krizaka/ui/skeleton` | `Skeleton` (`shape` text·circle·rect), `SkeletonText` (`lines`), `SkeletonShape` (the real component turned into its skeleton; `data-skeleton` marks a shape) — one brand reflection across the page | server |
133
133
  | `@krizaka/ui/empty-state` | `EmptyState` (`icon`, `title`, `description`, `action`) | server |
134
- | `@krizaka/ui/spinner` | `Spinner` (`label`, `size`) | server |
134
+ | `@krizaka/ui/spinner` | `Spinner` (`label`, `size`, `value`): the Krizaka glyph (a hexagon traced by light around a node), `LoaderGlyph` (`state` loading·success·error, `value`) | server |
135
+ | `@krizaka/ui/loader` | `Loader` (`label`, `status` loading·success·error, `successLabel`, `errorLabel`, `value`, `eta`, `delay` 250, `minDuration` 600, `endDuration` 1600, `variant` inline·block·overlay·page), `LoadingRegion` (`loading`, `skeleton`, `label`, `slowLabel`, `slowAfter`), `useDelayedWait` | client |
135
136
  | `@krizaka/ui/countdown` | `Countdown` (`target`, `label`, `showLabel`, `units`, `size`, `urgentBelowMs`, `skewMs`), `useCountdown`, `splitDuration` | client |
136
137
  | `@krizaka/ui/card` | `Card.Root` (`asChild`, `reveal`, `radius` md·lg·xl, `tone` default·elevated·glass, `interactive`), `Card.Media` (`aspect` video·square·portrait·auto), `Card.Image` (`fallback`), `Card.Overlay` (`corner`), `Card.Body` (`padding`), `Card.Title` (`as`), `Card.Description`, `Card.Stat` (`label`), `Card.Footer`; `card` (the slots) | server |
137
- | `@krizaka/ui/dialog` | `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close` (`placement` center·bottom·right, `size` sm·md·lg, `closeLabel` required unless `hideClose`, `dismissible={false}`: no close on Escape or outside click), `Sheet` (= `placement="bottom"`), `AlertDialog` (`title`, `description`, `confirmLabel`, `cancelLabel`, `tone` danger·primary, async `onConfirm`, `trigger`) | client |
138
- | `@krizaka/ui/toast` | `Toaster` (sonner styled by roles: `label`, `closeLabel`, `position`, `closeButton`; `richColors` off), `toast` (`.success/.warning/.error/.info/.custom`…) | client |
138
+ | `@krizaka/ui/dialog` | `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close` (`placement` center·bottom·right·left, `size` sm·md·lg, `closeLabel` required unless `hideClose`, `dismissible={false}`: no close on Escape or outside click), `Sheet` (= `placement="bottom"`), `AlertDialog` (`title`, `description`, `confirmLabel`, `cancelLabel`, `tone` danger·primary, async `onConfirm`, `trigger`) | client |
139
+ | `@krizaka/ui/toast` | `Toaster` (sonner styled by roles: `label`, `closeLabel`, `position`, `closeButton`; `richColors` off; a hairline counts each toast's time left), `toast` (`.success/.warning/.error/.info/.custom`…), `undoToast` (`undoLabel`, `onUndo`, `onCommit`), `progressToast` (`id`, `value`, `doneMessage`), `groupedToast` (`key`, `countLabel`) | client |
139
140
  | `@krizaka/ui/popover` | `Popover.Root/Trigger/Anchor/Content/Close` | client |
140
141
  | `@krizaka/ui/dropdown-menu` | `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group` (`Item` `tone` default·danger), `menu` (the slots) | client |
141
142
  | `@krizaka/ui/tooltip` | `Tooltip` (`content`, `delayDuration` 300, `side`, `open`/`defaultOpen`), `TooltipProvider` | client |
@@ -144,17 +145,26 @@ light or a product's — is a set of tokens.
144
145
  | `@krizaka/ui/alert` | `Alert` (`tone` info·success·warning·danger, `icon`, `title`, `action`; `role="alert"` for danger and warning, `status` otherwise), `alertVariants` | server |
145
146
  | `@krizaka/ui/separator` | `Separator` (`orientation`, `decorative`, Radix), `separatorVariants` | server |
146
147
  | `@krizaka/ui/kbd` | `Kbd` (`size` sm·md), `kbdVariants` | server |
147
- | `@krizaka/ui/tabs` | `Tabs.Root/List/Trigger/Content` (`variant` underline·segmented·pills on the root, `orientation`, `value`/`defaultValue` + `onValueChange`), `tabs` (the slots) | client |
148
+ | `@krizaka/ui/tabs` | `Tabs.Root/List/Trigger/Content` (`variant` underline·segmented·pills on the root, `orientation`, `value`/`defaultValue` + `onValueChange`; one light glides to the active tab), `tabs` (the slots) | client |
149
+ | `@krizaka/ui/nav-bar` | `NavBar.Root` (`label`, `compactAfter`, `floating`) `/Brand/Items/Link` (`active`) `/Menu` (`label`, `columns`) `/MenuLink` (`title`, `description`, `icon`) `/Actions/Mobile` (`openLabel`, `closeLabel`) `/MobileLink`, `navBar` (the slots) | client |
148
150
  | `@krizaka/ui/chip` | `Chip` (`selected`/`defaultSelected` + `onSelectedChange`, `size` sm·md; `removable` + `removeLabel` + `onRemove`), `Chip.Group` (`type` single·multiple, `label`, `required` for single), `chip` (the slots) | client |
151
+ | `@krizaka/ui/chip-link` | `ChipLink` (a filter that is a link: `selected`, `current`, `size`, `asChild`), `chipVariants` (`size`, `selected`) | server |
149
152
  | `@krizaka/ui/switch` | `Switch` (`checked`/`defaultChecked` + `onCheckedChange`, `label` unless a visible label names it, `size` sm·md, `invalid`), `switchVariants` | client |
150
153
  | `@krizaka/ui/slider` | `Slider` (`value`: a number or a `[min, max]` range, `onValueChange`, `onValueCommit`, `label`, `thumbLabels`, `formatValue` → aria-valuetext, `showLabel`, `origin`), `slider` (the slots) | client |
151
154
  | `@krizaka/ui/checkbox` | `Checkbox` (`checked` true·false·"indeterminate" + `onCheckedChange`, `invalid`, its words as children) — sits in a `Field` | client |
152
155
  | `@krizaka/ui/radio-group` | `RadioGroup.Root` (`label`, `orientation`, `invalid`), `RadioGroup.Item` (a dot and its words), `RadioGroup.Card` (a whole card is the radio) | client |
153
156
  | `@krizaka/ui/command` | `Command.Root` (`label`, `shouldFilter`…) `/Input` (`placeholder`, `trailing`) `/List` (`label`) `/Empty` (`emptyLabel`) `/Loading/Group/Item/Separator/Shortcut` on cmdk; `CommandDialog` (`label`, `open`/`onOpenChange`, `footer`, `contentProps`) | client |
154
157
  | `@krizaka/ui/confirm-button` | `ConfirmButton` (two presses: `confirmLabel`, `onConfirm`, `timeoutMs` 4000, `label` for an icon, `armedContent`; the `Button` variants), `confirmArmed` | client |
155
- | `@krizaka/ui/progress` | `Progress` (`variant` bar·ring, `size` sm·md·lg, `value`/`max`, indeterminate without a value, `label`, `valueText`, a ring's centre as children) | server |
158
+ | `@krizaka/ui/progress` | `Progress` (`variant` bar·ring, `size` sm·md·lg, `value`/`max`, indeterminate without a value, `label`, `valueText`, `showLabel`, `eta`, `buffer`, `segments`, `status` active·paused·error, a ring's centre as children) | server |
159
+ | `@krizaka/ui/meter` | `Meter` (`label`, `value`/`min`/`max`, `valueText`, `thresholds`, `optimum` low·high, `parts` with their legend, `variant` bar·ring, `size`, `showLabel`), `MeterValue` | server |
160
+ | `@krizaka/ui/stepper` | `Stepper` (`steps`: `title`, `description`, `status`, `value`, `content`; `current`, `labels` of the states, `orientation`) | server |
161
+ | `@krizaka/ui/number-ticker` | `NumberTicker` (`value`, `locale`, `format`, `trigger` view·change, `duration`, `announce`, `align`) | client |
162
+ | `@krizaka/ui/glow-card` | `GlowCard` (`glow` border·spotlight·both, `alive`, `asChild`) | client |
163
+ | `@krizaka/ui/marquee` | `Marquee` (`label`, `pauseLabel`, `playLabel`, `speed` px/s, `direction`, `fade`, `defaultPaused`) | client |
156
164
  | `@krizaka/ui/section-backdrop` | `SectionBackdrop` (`as`, `direction` down·up, `dome`, `grid`, `media` blurred for depth of field, `animated`): a page section on the brand's section gradient — see `@krizaka/tokens` BRAND.md, "Visual language" | server |
157
165
  | `@krizaka/ui/mascots` | `Mascot` (`bird` falcon·owl·duck·flamingo·pigeon, `mood` welcome·thinking·success·empty·error, `tone` brand·own, `size`, `animated`, `title`), `MASCOT_BIRDS`, `MASCOT_MOODS`: the Krizaka flock, in the host brand's accent — one bird at a moment that matters (onboarding, a long wait, a success, an empty list, an error); native `Mascot` in `/native` | client |
166
+ | `@krizaka/ui/iso` | the isometric kit: `IsoHub` (a hero driven by data: `layers`, `dome`, `satellites`, `highlight`), `IsoScene`, `IsoPrism`, `IsoStack`, `IsoDome`, `IsoTile`, `IsoLink`, `IsoNode`, `IsoLabel`, the projection helpers — static SVG animated by motion.css | server |
167
+ | `@krizaka/ui/motion` | `MotionObserver` (reveal, off-screen pause, settle, spotlight, magnetism) and `RotatingWord` (no layout shift) | client |
158
168
  | `@krizaka/ui` | the marks, `MotionObserver`, `RotatingWord`, `cx` (unchanged) | client |
159
169
  | `@krizaka/ui/native` | React Native: `ThemeProvider`/`useTheme`, `Txt`, `Button`, `IconButton`, `Card.*`, `Badge`, `Chip` (+ `Group`), `Avatar` (+ `Group`), `Skeleton`, `EmptyState`, `Spinner`, `Countdown`, `Segmented`, `Progress`, `Toaster`/`toast`, the marks — see [React Native](#react-native) | native |
160
170
  | `@krizaka/ui/motion.css`, `@krizaka/ui/tailwind.css` | the motion signature; the `@source` of the primitives' classes | CSS |
@@ -20,6 +20,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
20
20
  shape: {
21
21
  rounded: "rounded-lg";
22
22
  pill: "rounded-full";
23
+ cut: "rounded-[10px] [corner-shape:bevel]";
23
24
  };
24
25
  }, undefined, string, {
25
26
  variant: {
@@ -39,6 +40,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
39
40
  shape: {
40
41
  rounded: "rounded-lg";
41
42
  pill: "rounded-full";
43
+ cut: "rounded-[10px] [corner-shape:bevel]";
42
44
  };
43
45
  }, undefined, tailwind_variants.TVReturnTypeLike<{
44
46
  variant: {
@@ -58,6 +60,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
58
60
  shape: {
59
61
  rounded: "rounded-lg";
60
62
  pill: "rounded-full";
63
+ cut: "rounded-[10px] [corner-shape:bevel]";
61
64
  };
62
65
  }, undefined>>;
63
66
  type ButtonVariants = VariantProps<typeof buttonVariants>;
@@ -74,14 +77,38 @@ type ButtonProps = React.ComponentProps<"button"> & ButtonVariants & {
74
77
  */
75
78
  size?: ButtonVariants["size"];
76
79
  /**
77
- * rounded · pill.
80
+ * rounded · pill · cut (45° corners, the Krizaka chamfer).
78
81
  * @default "rounded"
79
82
  */
80
83
  shape?: ButtonVariants["shape"];
81
84
  /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */
82
85
  asChild?: boolean;
83
- /** Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one. */
86
+ /**
87
+ * Disables and signals the wait (aria-busy, data-loading) and shows the Krizaka loader glyph before the label; the
88
+ * button keeps its colour.
89
+ */
84
90
  loading?: boolean;
91
+ /**
92
+ * Between 0 and 1, while `loading`: the glyph's trace and a light fill across the button grow to the value (an
93
+ * upload, an export).
94
+ */
95
+ progress?: number | null;
96
+ /**
97
+ * How the action ended: `success` draws a check, `error` a cross (and the button shakes its head once); a ring of
98
+ * the status colour opens around it. Clear it to return to rest.
99
+ */
100
+ status?: "success" | "error" | null;
101
+ /** A message announced politely by assistive technology when it changes (“Saved”, “Upload failed”) — passed translated. */
102
+ announce?: string;
103
+ /**
104
+ * Shows the glyph while `loading` / with a `status`. `false`: you draw your own.
105
+ * @default true
106
+ */
107
+ indicator?: boolean;
108
+ /** A sheen of light crosses the button on hover — for the one action a page is about. */
109
+ shimmer?: boolean;
110
+ /** The button leans a few pixels toward a fine pointer (driven by `MotionObserver`; never under reduced motion or on touch). */
111
+ magnetic?: boolean;
85
112
  };
86
113
 
87
114
  export type { ButtonProps as B, ButtonVariants as a };
package/dist/button.d.ts CHANGED
@@ -20,6 +20,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
20
20
  shape: {
21
21
  rounded: "rounded-lg";
22
22
  pill: "rounded-full";
23
+ cut: "rounded-[10px] [corner-shape:bevel]";
23
24
  };
24
25
  }, undefined, string, {
25
26
  variant: {
@@ -39,6 +40,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
39
40
  shape: {
40
41
  rounded: "rounded-lg";
41
42
  pill: "rounded-full";
43
+ cut: "rounded-[10px] [corner-shape:bevel]";
42
44
  };
43
45
  }, undefined, tailwind_variants.TVReturnTypeLike<{
44
46
  variant: {
@@ -58,6 +60,7 @@ declare const buttonVariants: tailwind_variants.TVReturnType<{
58
60
  shape: {
59
61
  rounded: "rounded-lg";
60
62
  pill: "rounded-full";
63
+ cut: "rounded-[10px] [corner-shape:bevel]";
61
64
  };
62
65
  }, undefined>>;
63
66
  type ButtonVariants = VariantProps<typeof buttonVariants>;
@@ -74,16 +77,40 @@ type ButtonProps = React.ComponentProps<"button"> & ButtonVariants & {
74
77
  */
75
78
  size?: ButtonVariants["size"];
76
79
  /**
77
- * rounded · pill.
80
+ * rounded · pill · cut (45° corners, the Krizaka chamfer).
78
81
  * @default "rounded"
79
82
  */
80
83
  shape?: ButtonVariants["shape"];
81
84
  /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */
82
85
  asChild?: boolean;
83
- /** Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one. */
86
+ /**
87
+ * Disables and signals the wait (aria-busy, data-loading) and shows the Krizaka loader glyph before the label; the
88
+ * button keeps its colour.
89
+ */
84
90
  loading?: boolean;
91
+ /**
92
+ * Between 0 and 1, while `loading`: the glyph's trace and a light fill across the button grow to the value (an
93
+ * upload, an export).
94
+ */
95
+ progress?: number | null;
96
+ /**
97
+ * How the action ended: `success` draws a check, `error` a cross (and the button shakes its head once); a ring of
98
+ * the status colour opens around it. Clear it to return to rest.
99
+ */
100
+ status?: "success" | "error" | null;
101
+ /** A message announced politely by assistive technology when it changes (“Saved”, “Upload failed”) — passed translated. */
102
+ announce?: string;
103
+ /**
104
+ * Shows the glyph while `loading` / with a `status`. `false`: you draw your own.
105
+ * @default true
106
+ */
107
+ indicator?: boolean;
108
+ /** A sheen of light crosses the button on hover — for the one action a page is about. */
109
+ shimmer?: boolean;
110
+ /** The button leans a few pixels toward a fine pointer (driven by `MotionObserver`; never under reduced motion or on touch). */
111
+ magnetic?: boolean;
85
112
  };
86
- declare function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }: ButtonProps): React.JSX.Element;
113
+ declare function Button({ asChild, loading, progress, status, announce, indicator, shimmer, magnetic, variant, size, shape, className, disabled, children, ...props }: ButtonProps): React.JSX.Element;
87
114
  type IconButtonProps = Omit<ButtonProps, "size"> & {
88
115
  /** The accessible name (and tooltip) of the button — required, passed translated. */
89
116
  label: string;
@@ -93,7 +120,7 @@ type IconButtonProps = Omit<ButtonProps, "size"> & {
93
120
  */
94
121
  size?: ButtonProps["size"];
95
122
  };
96
- /** An icon-only button: `label` is required, it is its accessible name. */
97
- declare function IconButton({ label, size, ...props }: IconButtonProps): React.JSX.Element;
123
+ /** An icon-only button: `label` is required, it is its accessible name. Loading or with a status, the glyph takes the icon's place. */
124
+ declare function IconButton({ label, size, children, ...props }: IconButtonProps): React.JSX.Element;
98
125
 
99
126
  export { Button, type ButtonProps, type ButtonVariants, IconButton, type IconButtonProps, buttonVariants };
package/dist/button.js CHANGED
@@ -1,9 +1,13 @@
1
+ import {
2
+ LoaderGlyph
3
+ } from "./chunk-43PTCDTL.js";
4
+
1
5
  // src/button/button.tsx
2
6
  import { Slot } from "radix-ui";
3
7
  import { tv } from "tailwind-variants";
4
- import { jsx } from "react/jsx-runtime";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
9
  var buttonVariants = tv({
6
- base: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]",
10
+ base: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98] [&[data-loading]]:opacity-100",
7
11
  variants: {
8
12
  variant: {
9
13
  primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover",
@@ -17,28 +21,77 @@ var buttonVariants = tv({
17
21
  danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20"
18
22
  },
19
23
  size: { sm: "h-8 px-3 text-xs", md: "h-10 px-4 text-sm", lg: "h-12 px-6 text-sm", icon: "h-10 w-10" },
20
- shape: { rounded: "rounded-lg", pill: "rounded-full" }
24
+ // `cut`: the Krizaka chamfer — 45° corners (CSS corner-shape, where supported; rounded elsewhere). The border, the
25
+ // focus ring and the hover follow the cut, unlike a clip-path.
26
+ shape: { rounded: "rounded-lg", pill: "rounded-full", cut: "rounded-[10px] [corner-shape:bevel]" }
21
27
  },
22
28
  defaultVariants: { variant: "secondary", size: "md", shape: "rounded" }
23
29
  });
24
- function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }) {
30
+ var STATUS_TEXT = { success: "text-fg-success", error: "text-fg-danger" };
31
+ function Button({
32
+ asChild,
33
+ loading,
34
+ progress,
35
+ status,
36
+ announce,
37
+ indicator = true,
38
+ shimmer,
39
+ magnetic,
40
+ variant,
41
+ size,
42
+ shape,
43
+ className,
44
+ disabled,
45
+ children,
46
+ ...props
47
+ }) {
25
48
  const Comp = asChild ? Slot.Root : "button";
26
- return /* @__PURE__ */ jsx(
49
+ const known = loading && typeof progress === "number" && Number.isFinite(progress);
50
+ const ratio = known ? Math.min(1, Math.max(0, progress)) : 0;
51
+ const glyph = indicator && !asChild && (loading || status) ? status ?? "loading" : null;
52
+ const filled = variant === "primary" || variant === "gradient";
53
+ const button = /* @__PURE__ */ jsx(
27
54
  Comp,
28
55
  {
29
56
  type: asChild ? void 0 : "button",
30
57
  "aria-busy": loading || void 0,
31
58
  "data-loading": loading ? "" : void 0,
32
59
  "data-variant": variant ?? "secondary",
60
+ "data-kz-status": status ?? void 0,
61
+ "data-kz-magnetic": magnetic ? "" : void 0,
33
62
  disabled: asChild ? void 0 : disabled || loading,
34
63
  "aria-disabled": asChild && (disabled || loading) ? true : void 0,
35
- className: buttonVariants({ variant, size, shape, className }),
36
- ...props
64
+ className: buttonVariants({
65
+ variant,
66
+ size,
67
+ shape,
68
+ className: [known && "relative isolate overflow-hidden", shimmer && "kz-sheen", className].filter(Boolean).join(" ") || void 0
69
+ }),
70
+ ...props,
71
+ children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ known && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 -z-10 bg-current opacity-15 transition-[width] duration-500", style: { width: `${ratio * 100}%` } }),
73
+ glyph && /* @__PURE__ */ jsx(
74
+ "span",
75
+ {
76
+ "aria-hidden": true,
77
+ "data-slot": "indicator",
78
+ className: "-my-1 grid grid-cols-[1fr] transition-[grid-template-columns,margin,opacity] duration-300 starting:-mr-2 starting:grid-cols-[0fr] starting:opacity-0",
79
+ children: /* @__PURE__ */ jsx("span", { className: "overflow-hidden", children: /* @__PURE__ */ jsx(LoaderGlyph, { state: glyph, value: known ? ratio : null, className: ["h-4 w-4", glyph !== "loading" && !filled && STATUS_TEXT[glyph]].filter(Boolean).join(" ") }) })
80
+ }
81
+ ),
82
+ children
83
+ ] })
37
84
  }
38
85
  );
86
+ if (announce === void 0 || asChild) return button;
87
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
88
+ button,
89
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: announce })
90
+ ] });
39
91
  }
40
- function IconButton({ label, size = "icon", ...props }) {
41
- return /* @__PURE__ */ jsx(Button, { "aria-label": label, title: label, size, ...props });
92
+ function IconButton({ label, size = "icon", children, ...props }) {
93
+ const swap = props.indicator !== false && !props.asChild && (props.loading || props.status);
94
+ return /* @__PURE__ */ jsx(Button, { "aria-label": label, title: label, size, ...props, children: swap ? null : children });
42
95
  }
43
96
  export {
44
97
  Button,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/button/button.tsx"],"sourcesContent":["// Server-safe: no hook, no context. `buttonVariants` styles a <Link> or a Server Component without the component.\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const buttonVariants = tv({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 \" +\n \"disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]\",\n variants: {\n variant: {\n primary: \"bg-accent text-on-accent shadow-sm hover:bg-accent-hover\",\n // The brand's signature: accent → accent-2 (on-accent reads on both stops, tested in @krizaka/tokens).\n gradient: \"bg-linear-to-r from-accent to-accent-2 text-on-accent shadow-sm hover:brightness-110\",\n secondary: \"border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3\",\n outline: \"border border-border-default bg-transparent text-fg hover:border-accent hover:text-fg-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The border and the tint carry the danger; the label stays the primary text role, legible on the tint at rest\n // and hovered over every surface (`text-fg-danger` is for text set directly on a surface).\n danger: \"border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20\",\n },\n size: { sm: \"h-8 px-3 text-xs\", md: \"h-10 px-4 text-sm\", lg: \"h-12 px-6 text-sm\", icon: \"h-10 w-10\" },\n shape: { rounded: \"rounded-lg\", pill: \"rounded-full\" },\n },\n defaultVariants: { variant: \"secondary\", size: \"md\", shape: \"rounded\" },\n});\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n ButtonVariants & {\n /**\n * The look: `primary` (the accent), `gradient` (accent → accent-2), `secondary` (a raised surface), `outline`,\n * `ghost`, `danger`.\n * @default \"secondary\"\n */\n variant?: ButtonVariants[\"variant\"];\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"md\"\n */\n size?: ButtonVariants[\"size\"];\n /**\n * rounded · pill.\n * @default \"rounded\"\n */\n shape?: ButtonVariants[\"shape\"];\n /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one. */\n loading?: boolean;\n };\n\nexport function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }: ButtonProps) {\n const Comp = asChild ? Slot.Root : \"button\";\n return (\n <Comp\n type={asChild ? undefined : \"button\"}\n aria-busy={loading || undefined}\n data-loading={loading ? \"\" : undefined}\n data-variant={variant ?? \"secondary\"}\n disabled={asChild ? undefined : disabled || loading}\n aria-disabled={asChild && (disabled || loading) ? true : undefined}\n className={buttonVariants({ variant, size, shape, className })}\n {...props}\n />\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"size\"> & {\n /** The accessible name (and tooltip) of the button — required, passed translated. */\n label: string;\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"icon\"\n */\n size?: ButtonProps[\"size\"];\n};\n\n/** An icon-only button: `label` is required, it is its accessible name. */\nexport function IconButton({ label, size = \"icon\", ...props }: IconButtonProps) {\n return <Button aria-label={label} title={label} size={size} {...props} />;\n}\n"],"mappings":";AACA,SAAS,YAAY;AAErB,SAAS,UAA6B;AAsDlC;AApDG,IAAM,iBAAiB,GAAG;AAAA,EAC/B,MACE;AAAA,EAGF,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA;AAAA,MAET,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA;AAAA;AAAA,MAGP,QAAQ;AAAA,IACV;AAAA,IACA,MAAM,EAAE,IAAI,oBAAoB,IAAI,qBAAqB,IAAI,qBAAqB,MAAM,YAAY;AAAA,IACpG,OAAO,EAAE,SAAS,cAAc,MAAM,eAAe;AAAA,EACvD;AAAA,EACA,iBAAiB,EAAE,SAAS,aAAa,MAAM,MAAM,OAAO,UAAU;AACxE,CAAC;AA4BM,SAAS,OAAO,EAAE,SAAS,SAAS,SAAS,MAAM,OAAO,WAAW,UAAU,GAAG,MAAM,GAAgB;AAC7G,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,UAAU,SAAY;AAAA,MAC5B,aAAW,WAAW;AAAA,MACtB,gBAAc,UAAU,KAAK;AAAA,MAC7B,gBAAc,WAAW;AAAA,MACzB,UAAU,UAAU,SAAY,YAAY;AAAA,MAC5C,iBAAe,YAAY,YAAY,WAAW,OAAO;AAAA,MACzD,WAAW,eAAe,EAAE,SAAS,MAAM,OAAO,UAAU,CAAC;AAAA,MAC5D,GAAG;AAAA;AAAA,EACN;AAEJ;AAaO,SAAS,WAAW,EAAE,OAAO,OAAO,QAAQ,GAAG,MAAM,GAAoB;AAC9E,SAAO,oBAAC,UAAO,cAAY,OAAO,OAAO,OAAO,MAAa,GAAG,OAAO;AACzE;","names":[]}
1
+ {"version":3,"sources":["../src/button/button.tsx"],"sourcesContent":["// Server-safe: no hook, no context. `buttonVariants` styles a <Link> or a Server Component without the component.\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { LoaderGlyph } from \"../spinner/glyph\";\n\nexport const buttonVariants = tv({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 \" +\n \"disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98] \" +\n // Busy is not unavailable: a loading button keeps its colour (it is disabled only so it is not pressed twice).\n \"[&[data-loading]]:opacity-100\",\n variants: {\n variant: {\n primary: \"bg-accent text-on-accent shadow-sm hover:bg-accent-hover\",\n // The brand's signature: accent → accent-2 (on-accent reads on both stops, tested in @krizaka/tokens).\n gradient: \"bg-linear-to-r from-accent to-accent-2 text-on-accent shadow-sm hover:brightness-110\",\n secondary: \"border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3\",\n outline: \"border border-border-default bg-transparent text-fg hover:border-accent hover:text-fg-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The border and the tint carry the danger; the label stays the primary text role, legible on the tint at rest\n // and hovered over every surface (`text-fg-danger` is for text set directly on a surface).\n danger: \"border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20\",\n },\n size: { sm: \"h-8 px-3 text-xs\", md: \"h-10 px-4 text-sm\", lg: \"h-12 px-6 text-sm\", icon: \"h-10 w-10\" },\n // `cut`: the Krizaka chamfer — 45° corners (CSS corner-shape, where supported; rounded elsewhere). The border, the\n // focus ring and the hover follow the cut, unlike a clip-path.\n shape: { rounded: \"rounded-lg\", pill: \"rounded-full\", cut: \"rounded-[10px] [corner-shape:bevel]\" },\n },\n defaultVariants: { variant: \"secondary\", size: \"md\", shape: \"rounded\" },\n});\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n ButtonVariants & {\n /**\n * The look: `primary` (the accent), `gradient` (accent → accent-2), `secondary` (a raised surface), `outline`,\n * `ghost`, `danger`.\n * @default \"secondary\"\n */\n variant?: ButtonVariants[\"variant\"];\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"md\"\n */\n size?: ButtonVariants[\"size\"];\n /**\n * rounded · pill · cut (45° corners, the Krizaka chamfer).\n * @default \"rounded\"\n */\n shape?: ButtonVariants[\"shape\"];\n /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */\n asChild?: boolean;\n /**\n * Disables and signals the wait (aria-busy, data-loading) and shows the Krizaka loader glyph before the label; the\n * button keeps its colour.\n */\n loading?: boolean;\n /**\n * Between 0 and 1, while `loading`: the glyph's trace and a light fill across the button grow to the value (an\n * upload, an export).\n */\n progress?: number | null;\n /**\n * How the action ended: `success` draws a check, `error` a cross (and the button shakes its head once); a ring of\n * the status colour opens around it. Clear it to return to rest.\n */\n status?: \"success\" | \"error\" | null;\n /** A message announced politely by assistive technology when it changes (“Saved”, “Upload failed”) — passed translated. */\n announce?: string;\n /**\n * Shows the glyph while `loading` / with a `status`. `false`: you draw your own.\n * @default true\n */\n indicator?: boolean;\n /** A sheen of light crosses the button on hover — for the one action a page is about. */\n shimmer?: boolean;\n /** The button leans a few pixels toward a fine pointer (driven by `MotionObserver`; never under reduced motion or on touch). */\n magnetic?: boolean;\n };\n\nconst STATUS_TEXT = { success: \"text-fg-success\", error: \"text-fg-danger\" } as const;\n\nexport function Button({\n asChild,\n loading,\n progress,\n status,\n announce,\n indicator = true,\n shimmer,\n magnetic,\n variant,\n size,\n shape,\n className,\n disabled,\n children,\n ...props\n}: ButtonProps) {\n const Comp = asChild ? Slot.Root : \"button\";\n const known = loading && typeof progress === \"number\" && Number.isFinite(progress);\n const ratio = known ? Math.min(1, Math.max(0, progress)) : 0;\n const glyph = indicator && !asChild && (loading || status) ? (status ?? \"loading\") : null;\n // On a filled variant the glyph stays in the label's colour; on the others it takes the status colour.\n const filled = variant === \"primary\" || variant === \"gradient\";\n const button = (\n <Comp\n type={asChild ? undefined : \"button\"}\n aria-busy={loading || undefined}\n data-loading={loading ? \"\" : undefined}\n data-variant={variant ?? \"secondary\"}\n data-kz-status={status ?? undefined}\n data-kz-magnetic={magnetic ? \"\" : undefined}\n disabled={asChild ? undefined : disabled || loading}\n aria-disabled={asChild && (disabled || loading) ? true : undefined}\n className={buttonVariants({\n variant,\n size,\n shape,\n className: [known && \"relative isolate overflow-hidden\", shimmer && \"kz-sheen\", className].filter(Boolean).join(\" \") || undefined,\n })}\n {...props}\n >\n {asChild ? (\n children\n ) : (\n <>\n {known && (\n <span aria-hidden className=\"absolute inset-y-0 left-0 -z-10 bg-current opacity-15 transition-[width] duration-500\" style={{ width: `${ratio * 100}%` }} />\n )}\n {glyph && (\n <span\n aria-hidden\n data-slot=\"indicator\"\n className=\"-my-1 grid grid-cols-[1fr] transition-[grid-template-columns,margin,opacity] duration-300 starting:-mr-2 starting:grid-cols-[0fr] starting:opacity-0\"\n >\n <span className=\"overflow-hidden\">\n <LoaderGlyph state={glyph} value={known ? ratio : null} className={[\"h-4 w-4\", glyph !== \"loading\" && !filled && STATUS_TEXT[glyph]].filter(Boolean).join(\" \")} />\n </span>\n </span>\n )}\n {children}\n </>\n )}\n </Comp>\n );\n if (announce === undefined || asChild) return button;\n return (\n <>\n {button}\n <span role=\"status\" className=\"sr-only\">\n {announce}\n </span>\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"size\"> & {\n /** The accessible name (and tooltip) of the button — required, passed translated. */\n label: string;\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"icon\"\n */\n size?: ButtonProps[\"size\"];\n};\n\n/** An icon-only button: `label` is required, it is its accessible name. Loading or with a status, the glyph takes the icon's place. */\nexport function IconButton({ label, size = \"icon\", children, ...props }: IconButtonProps) {\n const swap = props.indicator !== false && !props.asChild && (props.loading || props.status);\n return (\n <Button aria-label={label} title={label} size={size} {...props}>\n {swap ? null : children}\n </Button>\n );\n}\n"],"mappings":";;;;;AACA,SAAS,YAAY;AAErB,SAAS,UAA6B;AA+H9B,mBAEI,KAFJ;AA3HD,IAAM,iBAAiB,GAAG;AAAA,EAC/B,MACE;AAAA,EAKF,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA;AAAA,MAET,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA;AAAA;AAAA,MAGP,QAAQ;AAAA,IACV;AAAA,IACA,MAAM,EAAE,IAAI,oBAAoB,IAAI,qBAAqB,IAAI,qBAAqB,MAAM,YAAY;AAAA;AAAA;AAAA,IAGpG,OAAO,EAAE,SAAS,cAAc,MAAM,gBAAgB,KAAK,sCAAsC;AAAA,EACnG;AAAA,EACA,iBAAiB,EAAE,SAAS,aAAa,MAAM,MAAM,OAAO,UAAU;AACxE,CAAC;AAoDD,IAAM,cAAc,EAAE,SAAS,mBAAmB,OAAO,iBAAiB;AAEnE,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AACd,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,QAAM,QAAQ,WAAW,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ;AACjF,QAAM,QAAQ,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC,IAAI;AAC3D,QAAM,QAAQ,aAAa,CAAC,YAAY,WAAW,UAAW,UAAU,YAAa;AAErF,QAAM,SAAS,YAAY,aAAa,YAAY;AACpD,QAAM,SACJ;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,UAAU,SAAY;AAAA,MAC5B,aAAW,WAAW;AAAA,MACtB,gBAAc,UAAU,KAAK;AAAA,MAC7B,gBAAc,WAAW;AAAA,MACzB,kBAAgB,UAAU;AAAA,MAC1B,oBAAkB,WAAW,KAAK;AAAA,MAClC,UAAU,UAAU,SAAY,YAAY;AAAA,MAC5C,iBAAe,YAAY,YAAY,WAAW,OAAO;AAAA,MACzD,WAAW,eAAe;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,CAAC,SAAS,oCAAoC,WAAW,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAAA,MAC1H,CAAC;AAAA,MACA,GAAG;AAAA,MAEH,oBACC,WAEA,iCACG;AAAA,iBACC,oBAAC,UAAK,eAAW,MAAC,WAAU,yFAAwF,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG;AAAA,QAE1J,SACC;AAAA,UAAC;AAAA;AAAA,YACC,eAAW;AAAA,YACX,aAAU;AAAA,YACV,WAAU;AAAA,YAEV,8BAAC,UAAK,WAAU,mBACd,8BAAC,eAAY,OAAO,OAAO,OAAO,QAAQ,QAAQ,MAAM,WAAW,CAAC,WAAW,UAAU,aAAa,CAAC,UAAU,YAAY,KAAK,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAG,GAClK;AAAA;AAAA,QACF;AAAA,QAED;AAAA,SACH;AAAA;AAAA,EAEJ;AAEF,MAAI,aAAa,UAAa,QAAS,QAAO;AAC9C,SACE,iCACG;AAAA;AAAA,IACD,oBAAC,UAAK,MAAK,UAAS,WAAU,WAC3B,oBACH;AAAA,KACF;AAEJ;AAaO,SAAS,WAAW,EAAE,OAAO,OAAO,QAAQ,UAAU,GAAG,MAAM,GAAoB;AACxF,QAAM,OAAO,MAAM,cAAc,SAAS,CAAC,MAAM,YAAY,MAAM,WAAW,MAAM;AACpF,SACE,oBAAC,UAAO,cAAY,OAAO,OAAO,OAAO,MAAa,GAAG,OACtD,iBAAO,OAAO,UACjB;AAEJ;","names":[]}
@@ -0,0 +1,53 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import { VariantProps } from 'tailwind-variants';
3
+ import * as React from 'react';
4
+
5
+ declare const chipVariants: tailwind_variants.TVReturnType<{
6
+ size: {
7
+ sm: "h-7 px-3 text-xs";
8
+ md: "h-9 px-3.5 text-xs";
9
+ };
10
+ selected: {
11
+ false: "border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg";
12
+ true: "border-accent bg-accent-soft text-fg";
13
+ };
14
+ }, undefined, string, {
15
+ size: {
16
+ sm: "h-7 px-3 text-xs";
17
+ md: "h-9 px-3.5 text-xs";
18
+ };
19
+ selected: {
20
+ false: "border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg";
21
+ true: "border-accent bg-accent-soft text-fg";
22
+ };
23
+ }, undefined, tailwind_variants.TVReturnTypeLike<{
24
+ size: {
25
+ sm: "h-7 px-3 text-xs";
26
+ md: "h-9 px-3.5 text-xs";
27
+ };
28
+ selected: {
29
+ false: "border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg";
30
+ true: "border-accent bg-accent-soft text-fg";
31
+ };
32
+ }, undefined>>;
33
+ type ChipVariantsProps = VariantProps<typeof chipVariants>;
34
+ type ChipLinkProps = React.ComponentProps<"a"> & {
35
+ /**
36
+ * sm · md.
37
+ * @default "md"
38
+ */
39
+ size?: ChipVariantsProps["size"];
40
+ /** The current filter: the accent, `aria-current` (`page` by default) and `data-state="on"`. */
41
+ selected?: boolean;
42
+ /**
43
+ * What `aria-current` says when selected: `page` (a filter that is a page) or `true`.
44
+ * @default "page"
45
+ */
46
+ current?: "page" | "true";
47
+ /** Renders your router's `<Link>` with the chip's look. */
48
+ asChild?: boolean;
49
+ };
50
+ /** A filter that is a link, with the look of `Chip`. */
51
+ declare function ChipLink({ size, selected, current, asChild, className, ...props }: ChipLinkProps): React.JSX.Element;
52
+
53
+ export { ChipLink, type ChipLinkProps, type ChipVariantsProps, chipVariants };
@@ -0,0 +1,32 @@
1
+ // src/chip-link/chip-link.tsx
2
+ import { Slot } from "radix-ui";
3
+ import { tv } from "tailwind-variants";
4
+ import { jsx } from "react/jsx-runtime";
5
+ var chipVariants = tv({
6
+ base: "inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border font-semibold no-underline transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
7
+ variants: {
8
+ size: { sm: "h-7 px-3 text-xs", md: "h-9 px-3.5 text-xs" },
9
+ selected: {
10
+ false: "border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg",
11
+ true: "border-accent bg-accent-soft text-fg"
12
+ }
13
+ },
14
+ defaultVariants: { size: "md", selected: false }
15
+ });
16
+ function ChipLink({ size, selected = false, current = "page", asChild, className, ...props }) {
17
+ const Comp = asChild ? Slot.Root : "a";
18
+ return /* @__PURE__ */ jsx(
19
+ Comp,
20
+ {
21
+ "data-state": selected ? "on" : "off",
22
+ "aria-current": selected ? current : void 0,
23
+ className: chipVariants({ size, selected, className }),
24
+ ...props
25
+ }
26
+ );
27
+ }
28
+ export {
29
+ ChipLink,
30
+ chipVariants
31
+ };
32
+ //# sourceMappingURL=chip-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chip-link/chip-link.tsx"],"sourcesContent":["// Server-safe: no hook, no context. A chip that is a link — a filter that is an address (`/explore?tag=night`): the look\n// of `Chip`, the current filter marked by `selected` (`aria-current`, the accent), usable in a Server Component.\n// `chipVariants` styles anything else (a router link you render yourself).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const chipVariants = tv({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border font-semibold no-underline transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n variants: {\n size: { sm: \"h-7 px-3 text-xs\", md: \"h-9 px-3.5 text-xs\" },\n selected: {\n false: \"border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg\",\n true: \"border-accent bg-accent-soft text-fg\",\n },\n },\n defaultVariants: { size: \"md\", selected: false },\n});\n\nexport type ChipVariantsProps = VariantProps<typeof chipVariants>;\n\nexport type ChipLinkProps = React.ComponentProps<\"a\"> & {\n /**\n * sm · md.\n * @default \"md\"\n */\n size?: ChipVariantsProps[\"size\"];\n /** The current filter: the accent, `aria-current` (`page` by default) and `data-state=\"on\"`. */\n selected?: boolean;\n /**\n * What `aria-current` says when selected: `page` (a filter that is a page) or `true`.\n * @default \"page\"\n */\n current?: \"page\" | \"true\";\n /** Renders your router's `<Link>` with the chip's look. */\n asChild?: boolean;\n};\n\n/** A filter that is a link, with the look of `Chip`. */\nexport function ChipLink({ size, selected = false, current = \"page\", asChild, className, ...props }: ChipLinkProps) {\n const Comp = asChild ? Slot.Root : \"a\";\n return (\n <Comp\n data-state={selected ? \"on\" : \"off\"}\n aria-current={selected ? current : undefined}\n className={chipVariants({ size, selected, className })}\n {...props}\n />\n );\n}\n"],"mappings":";AAGA,SAAS,YAAY;AAErB,SAAS,UAA6B;AAuClC;AArCG,IAAM,eAAe,GAAG;AAAA,EAC7B,MACE;AAAA,EAEF,UAAU;AAAA,IACR,MAAM,EAAE,IAAI,oBAAoB,IAAI,qBAAqB;AAAA,IACzD,UAAU;AAAA,MACR,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,MAAM,UAAU,MAAM;AACjD,CAAC;AAsBM,SAAS,SAAS,EAAE,MAAM,WAAW,OAAO,UAAU,QAAQ,SAAS,WAAW,GAAG,MAAM,GAAkB;AAClH,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,cAAY,WAAW,OAAO;AAAA,MAC9B,gBAAc,WAAW,UAAU;AAAA,MACnC,WAAW,aAAa,EAAE,MAAM,UAAU,UAAU,CAAC;AAAA,MACpD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,31 @@
1
+ // src/spinner/glyph.tsx
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ var HEX = "M12 3 19.79 7.5 19.79 16.5 12 21 4.21 16.5 4.21 7.5Z";
4
+ function LoaderGlyph({ state = "loading", value, ...props }) {
5
+ const known = typeof value === "number" && Number.isFinite(value);
6
+ const fill = state !== "loading" ? 100 : known ? Math.round(Math.min(1, Math.max(0, value)) * 100) : null;
7
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, "data-state": state, "data-progress": fill === null ? void 0 : fill, ...props, children: [
8
+ /* @__PURE__ */ jsx("path", { d: HEX, stroke: "currentColor", strokeOpacity: "0.22", strokeWidth: "2.2", strokeLinejoin: "round" }),
9
+ /* @__PURE__ */ jsx(
10
+ "path",
11
+ {
12
+ d: HEX,
13
+ pathLength: 100,
14
+ stroke: "currentColor",
15
+ strokeWidth: "2.2",
16
+ strokeLinejoin: "round",
17
+ strokeLinecap: "round",
18
+ className: "kz-loader-trace",
19
+ strokeDasharray: fill === null ? void 0 : `${fill} ${100 - fill}`
20
+ }
21
+ ),
22
+ state === "loading" && /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2.6", fill: "currentColor", className: "kz-loader-node" }),
23
+ state === "success" && /* @__PURE__ */ jsx("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" }),
24
+ state === "error" && /* @__PURE__ */ jsx("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" })
25
+ ] });
26
+ }
27
+
28
+ export {
29
+ LoaderGlyph
30
+ };
31
+ //# sourceMappingURL=chunk-43PTCDTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/spinner/glyph.tsx"],"sourcesContent":["// 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"],"mappings":";AAuBI,SACE,KADF;AAjBJ,IAAM,MAAM;AAaL,SAAS,YAAY,EAAE,QAAQ,WAAW,OAAO,GAAG,MAAM,GAAqB;AACpF,QAAM,QAAQ,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAChE,QAAM,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC,IAAI,GAAG,IAAI;AACrG,SACE,qBAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,eAAW,MAAC,cAAY,OAAO,iBAAe,SAAS,OAAO,SAAY,MAAO,GAAG,OACvH;AAAA,wBAAC,UAAK,GAAG,KAAK,QAAO,gBAAe,eAAc,QAAO,aAAY,OAAM,gBAAe,SAAQ;AAAA,IAClG;AAAA,MAAC;AAAA;AAAA,QACC,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,QAAO;AAAA,QACP,aAAY;AAAA,QACZ,gBAAe;AAAA,QACf,eAAc;AAAA,QACd,WAAU;AAAA,QACV,iBAAiB,SAAS,OAAO,SAAY,GAAG,IAAI,IAAI,MAAM,IAAI;AAAA;AAAA,IACpE;AAAA,IACC,UAAU,aAAa,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,OAAM,MAAK,gBAAe,WAAU,kBAAiB;AAAA,IACtG,UAAU,aAAa,oBAAC,UAAK,GAAE,6BAA4B,YAAY,IAAI,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,WAAU,kBAAiB;AAAA,IAC3L,UAAU,WAAW,oBAAC,UAAK,GAAE,sCAAqC,YAAY,IAAI,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,WAAU,kBAAiB;AAAA,KAC9K;AAEJ;","names":[]}
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ // src/cx.ts
4
+ function cx(...parts) {
5
+ return parts.filter(Boolean).join(" ");
6
+ }
7
+
8
+ export {
9
+ cx
10
+ };
11
+ //# sourceMappingURL=chunk-6ISW63JI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cx.ts"],"sourcesContent":["/** Joins class names, skipping the falsy ones. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(\" \");\n}\n"],"mappings":";;;AACO,SAAS,MAAM,OAAyD;AAC7E,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG;AACvC;","names":[]}
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ // src/motion/MotionObserver.tsx
4
+ import { useEffect } from "react";
5
+ var SCOPES = ".kz-backdrop, .kz-iso, [data-motion-scope], main > section, main > header, footer";
6
+ var REVEAL = "[data-reveal]:not(.kz-in)";
7
+ var MAGNETIC = "[data-kz-magnetic]";
8
+ var SETTLE_MS = 1200;
9
+ function MotionObserver({ settle = true, pauseOffscreen = true } = {}) {
10
+ useEffect(() => {
11
+ const root = document.documentElement;
12
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
13
+ let settleTimer;
14
+ const ready = () => settleTimer = setTimeout(() => root.setAttribute("data-motion", "ready"), SETTLE_MS);
15
+ if (settle && !reduce && root.getAttribute("data-motion") !== "ready") {
16
+ root.setAttribute("data-motion", "settling");
17
+ if (document.readyState === "complete") ready();
18
+ else window.addEventListener("load", ready, { once: true });
19
+ } else root.setAttribute("data-motion", "ready");
20
+ const revealIo = reduce ? null : new IntersectionObserver(
21
+ (entries) => {
22
+ for (const entry of entries) {
23
+ if (!entry.isIntersecting) continue;
24
+ entry.target.classList.add("kz-in");
25
+ revealIo.unobserve(entry.target);
26
+ }
27
+ },
28
+ { rootMargin: "0px 0px -8% 0px", threshold: 0.12 }
29
+ );
30
+ const scopeIo = pauseOffscreen ? new IntersectionObserver(
31
+ (entries) => {
32
+ for (const entry of entries) entry.target.toggleAttribute("data-offscreen", !entry.isIntersecting);
33
+ },
34
+ { rootMargin: "120px 0px" }
35
+ ) : null;
36
+ const reveal = (el) => revealIo ? revealIo.observe(el) : el.classList.add("kz-in");
37
+ const watch = (scope) => {
38
+ scope.querySelectorAll(REVEAL).forEach(reveal);
39
+ if (scopeIo) scope.querySelectorAll(SCOPES).forEach((el) => scopeIo.observe(el));
40
+ };
41
+ watch(document);
42
+ const mo = new MutationObserver((records) => {
43
+ for (const record of records)
44
+ record.addedNodes.forEach((node) => {
45
+ if (!(node instanceof Element)) return;
46
+ if (node.matches(REVEAL)) reveal(node);
47
+ if (scopeIo && node.matches(SCOPES)) scopeIo.observe(node);
48
+ watch(node);
49
+ });
50
+ });
51
+ mo.observe(document.body, { childList: true, subtree: true });
52
+ return () => {
53
+ clearTimeout(settleTimer);
54
+ window.removeEventListener("load", ready);
55
+ revealIo?.disconnect();
56
+ scopeIo?.disconnect();
57
+ mo.disconnect();
58
+ };
59
+ }, [settle, pauseOffscreen]);
60
+ useEffect(() => {
61
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
62
+ const fine = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
63
+ let magnet = null;
64
+ const release = () => {
65
+ magnet?.style.removeProperty("--kz-mag-x");
66
+ magnet?.style.removeProperty("--kz-mag-y");
67
+ magnet = null;
68
+ };
69
+ const move = (e) => {
70
+ const target = e.target;
71
+ const card = target?.closest?.(".kz-spotlight");
72
+ if (card) {
73
+ const box2 = card.getBoundingClientRect();
74
+ card.style.setProperty("--spot-x", `${e.clientX - box2.left}px`);
75
+ card.style.setProperty("--spot-y", `${e.clientY - box2.top}px`);
76
+ }
77
+ if (reduce || !fine) return;
78
+ const el = target?.closest?.(MAGNETIC);
79
+ if (el !== magnet) release();
80
+ if (!el) return;
81
+ magnet = el;
82
+ const box = el.getBoundingClientRect();
83
+ const pull = (offset, half) => `${(Math.max(-1, Math.min(1, offset / half)) * 6).toFixed(2)}px`;
84
+ el.style.setProperty("--kz-mag-x", pull(e.clientX - box.left - box.width / 2, box.width / 2));
85
+ el.style.setProperty("--kz-mag-y", pull(e.clientY - box.top - box.height / 2, box.height / 2));
86
+ };
87
+ document.addEventListener("pointermove", move, { passive: true });
88
+ document.addEventListener("pointerleave", release);
89
+ return () => {
90
+ document.removeEventListener("pointermove", move);
91
+ document.removeEventListener("pointerleave", release);
92
+ release();
93
+ };
94
+ }, []);
95
+ return null;
96
+ }
97
+
98
+ // src/motion/RotatingWord.tsx
99
+ import { useEffect as useEffect2, useState } from "react";
100
+ import { jsx } from "react/jsx-runtime";
101
+ function RotatingWord({ words, interval = 2600, className }) {
102
+ const [i, setI] = useState(0);
103
+ useEffect2(() => {
104
+ if (words.length < 2 || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
105
+ const timer = setInterval(() => {
106
+ if (!document.hidden) setI((k) => (k + 1) % words.length);
107
+ }, interval);
108
+ return () => clearInterval(timer);
109
+ }, [words.length, interval]);
110
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "kz-rotating", children: words.map((word, k) => /* @__PURE__ */ jsx("span", { "data-active": k === i, className, children: word }, word)) });
111
+ }
112
+
113
+ export {
114
+ MotionObserver,
115
+ RotatingWord
116
+ };
117
+ //# sourceMappingURL=chunk-HAQWKORZ.js.map