@krizaka/ui 2.0.0-beta.2 → 2.0.0-beta.3

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 (184) hide show
  1. package/README.md +30 -0
  2. package/dist/alert.d.ts +2 -0
  3. package/dist/alert.js.map +1 -1
  4. package/dist/avatar.d.ts +5 -0
  5. package/dist/avatar.js.map +1 -1
  6. package/dist/badge.d.ts +12 -0
  7. package/dist/badge.js.map +1 -1
  8. package/dist/{button-G5qY4qSY.d.ts → button-8La7l0V4.d.ts} +15 -0
  9. package/dist/button.d.ts +19 -0
  10. package/dist/button.js.map +1 -1
  11. package/dist/card.d.ts +24 -1
  12. package/dist/card.js.map +1 -1
  13. package/dist/chip.d.ts +23 -0
  14. package/dist/chip.js.map +1 -1
  15. package/dist/{chunk-5FRYKFTO.js → chunk-3X5MVH54.js} +2 -2
  16. package/dist/chunk-3X5MVH54.js.map +1 -0
  17. package/dist/{chunk-QRG5RYEI.js → chunk-ZUD4U22H.js} +1 -1
  18. package/dist/chunk-ZUD4U22H.js.map +1 -0
  19. package/dist/command.d.ts +4 -1
  20. package/dist/command.js +2 -2
  21. package/dist/command.js.map +1 -1
  22. package/dist/confirm-button.d.ts +1 -1
  23. package/dist/confirm-button.js +1 -1
  24. package/dist/countdown.d.ts +3 -0
  25. package/dist/countdown.js.map +1 -1
  26. package/dist/{dialog-DGBY2grk.d.ts → dialog-GQ4Nvuyf.d.ts} +10 -0
  27. package/dist/dialog.d.ts +9 -1
  28. package/dist/dialog.js +2 -2
  29. package/dist/dialog.js.map +1 -1
  30. package/dist/dropdown-menu.d.ts +1 -0
  31. package/dist/dropdown-menu.js.map +1 -1
  32. package/dist/empty-state.d.ts +2 -0
  33. package/dist/empty-state.js.map +1 -1
  34. package/dist/kbd.d.ts +7 -1
  35. package/dist/kbd.js.map +1 -1
  36. package/dist/page-header.d.ts +2 -0
  37. package/dist/page-header.js.map +1 -1
  38. package/dist/progress.d.ts +5 -0
  39. package/dist/progress.js.map +1 -1
  40. package/dist/skeleton.d.ts +7 -1
  41. package/dist/skeleton.js.map +1 -1
  42. package/dist/slider.d.ts +2 -0
  43. package/dist/slider.js.map +1 -1
  44. package/dist/spinner.d.ts +5 -0
  45. package/dist/spinner.js.map +1 -1
  46. package/dist/stat.d.ts +2 -0
  47. package/dist/stat.js.map +1 -1
  48. package/dist/switch.d.ts +7 -3
  49. package/dist/switch.js.map +1 -1
  50. package/dist/tabs.d.ts +4 -1
  51. package/dist/tabs.js.map +1 -1
  52. package/dist/theme.d.ts +10 -2
  53. package/dist/theme.js +4 -2
  54. package/dist/theme.js.map +1 -1
  55. package/dist/tooltip.d.ts +3 -0
  56. package/dist/tooltip.js.map +1 -1
  57. package/package.json +14 -3
  58. package/registry/alert.json +59 -0
  59. package/registry/avatar.json +114 -0
  60. package/registry/badge.json +59 -0
  61. package/registry/button.json +115 -0
  62. package/registry/card.json +154 -0
  63. package/registry/checkbox.json +46 -0
  64. package/registry/chip.json +164 -0
  65. package/registry/cn.json +21 -0
  66. package/registry/command.json +162 -0
  67. package/registry/confirm-button.json +104 -0
  68. package/registry/countdown.json +84 -0
  69. package/registry/demos/alert.d.ts +5 -0
  70. package/registry/demos/alert.js +12 -0
  71. package/registry/demos/alert.tsx +15 -0
  72. package/registry/demos/avatar.d.ts +5 -0
  73. package/registry/demos/avatar.js +14 -0
  74. package/registry/demos/avatar.tsx +13 -0
  75. package/registry/demos/badge.d.ts +5 -0
  76. package/registry/demos/badge.js +11 -0
  77. package/registry/demos/badge.tsx +5 -0
  78. package/registry/demos/button.d.ts +5 -0
  79. package/registry/demos/button.js +11 -0
  80. package/registry/demos/button.tsx +5 -0
  81. package/registry/demos/card.d.ts +5 -0
  82. package/registry/demos/card.js +30 -0
  83. package/registry/demos/card.tsx +37 -0
  84. package/registry/demos/checkbox.d.ts +5 -0
  85. package/registry/demos/checkbox.js +11 -0
  86. package/registry/demos/checkbox.tsx +5 -0
  87. package/registry/demos/chip.d.ts +5 -0
  88. package/registry/demos/chip.js +15 -0
  89. package/registry/demos/chip.tsx +11 -0
  90. package/registry/demos/cn.d.ts +7 -0
  91. package/registry/demos/cn.js +17 -0
  92. package/registry/demos/cn.tsx +9 -0
  93. package/registry/demos/command.d.ts +5 -0
  94. package/registry/demos/command.js +32 -0
  95. package/registry/demos/command.tsx +28 -0
  96. package/registry/demos/confirm-button.d.ts +5 -0
  97. package/registry/demos/confirm-button.js +12 -0
  98. package/registry/demos/confirm-button.tsx +13 -0
  99. package/registry/demos/countdown.d.ts +5 -0
  100. package/registry/demos/countdown.js +13 -0
  101. package/registry/demos/countdown.tsx +10 -0
  102. package/registry/demos/dialog.d.ts +7 -0
  103. package/registry/demos/dialog.js +29 -0
  104. package/registry/demos/dialog.tsx +31 -0
  105. package/registry/demos/dropdown-menu.d.ts +7 -0
  106. package/registry/demos/dropdown-menu.js +36 -0
  107. package/registry/demos/dropdown-menu.tsx +27 -0
  108. package/registry/demos/empty-state.d.ts +5 -0
  109. package/registry/demos/empty-state.js +15 -0
  110. package/registry/demos/empty-state.tsx +12 -0
  111. package/registry/demos/field.d.ts +5 -0
  112. package/registry/demos/field.js +15 -0
  113. package/registry/demos/field.tsx +11 -0
  114. package/registry/demos/kbd.d.ts +5 -0
  115. package/registry/demos/kbd.js +11 -0
  116. package/registry/demos/kbd.tsx +5 -0
  117. package/registry/demos/page-header.d.ts +5 -0
  118. package/registry/demos/page-header.js +11 -0
  119. package/registry/demos/page-header.tsx +5 -0
  120. package/registry/demos/popover.d.ts +7 -0
  121. package/registry/demos/popover.js +25 -0
  122. package/registry/demos/popover.tsx +31 -0
  123. package/registry/demos/progress.d.ts +5 -0
  124. package/registry/demos/progress.js +11 -0
  125. package/registry/demos/progress.tsx +5 -0
  126. package/registry/demos/radio-group.d.ts +5 -0
  127. package/registry/demos/radio-group.js +16 -0
  128. package/registry/demos/radio-group.tsx +14 -0
  129. package/registry/demos/separator.d.ts +5 -0
  130. package/registry/demos/separator.js +15 -0
  131. package/registry/demos/separator.tsx +11 -0
  132. package/registry/demos/skeleton.d.ts +5 -0
  133. package/registry/demos/skeleton.js +20 -0
  134. package/registry/demos/skeleton.tsx +16 -0
  135. package/registry/demos/slider.d.ts +5 -0
  136. package/registry/demos/slider.js +11 -0
  137. package/registry/demos/slider.tsx +5 -0
  138. package/registry/demos/slot.d.ts +5 -0
  139. package/registry/demos/slot.js +14 -0
  140. package/registry/demos/slot.tsx +13 -0
  141. package/registry/demos/spinner.d.ts +5 -0
  142. package/registry/demos/spinner.js +11 -0
  143. package/registry/demos/spinner.tsx +5 -0
  144. package/registry/demos/stat.d.ts +5 -0
  145. package/registry/demos/stat.js +11 -0
  146. package/registry/demos/stat.tsx +5 -0
  147. package/registry/demos/switch.d.ts +5 -0
  148. package/registry/demos/switch.js +11 -0
  149. package/registry/demos/switch.tsx +5 -0
  150. package/registry/demos/tabs.d.ts +5 -0
  151. package/registry/demos/tabs.js +22 -0
  152. package/registry/demos/tabs.tsx +28 -0
  153. package/registry/demos/theme.d.ts +5 -0
  154. package/registry/demos/theme.js +11 -0
  155. package/registry/demos/theme.tsx +9 -0
  156. package/registry/demos/toast.d.ts +8 -0
  157. package/registry/demos/toast.js +29 -0
  158. package/registry/demos/toast.tsx +31 -0
  159. package/registry/demos/tooltip.d.ts +7 -0
  160. package/registry/demos/tooltip.js +15 -0
  161. package/registry/demos/tooltip.tsx +15 -0
  162. package/registry/dialog.json +218 -0
  163. package/registry/dropdown-menu.json +63 -0
  164. package/registry/empty-state.json +59 -0
  165. package/registry/field.json +66 -0
  166. package/registry/index.json +320 -0
  167. package/registry/kbd.json +38 -0
  168. package/registry/page-header.json +66 -0
  169. package/registry/popover.json +44 -0
  170. package/registry/progress.json +80 -0
  171. package/registry/radio-group.json +64 -0
  172. package/registry/separator.json +25 -0
  173. package/registry/skeleton.json +38 -0
  174. package/registry/slider.json +95 -0
  175. package/registry/slot.json +31 -0
  176. package/registry/spinner.json +45 -0
  177. package/registry/stat.json +66 -0
  178. package/registry/switch.json +53 -0
  179. package/registry/tabs.json +44 -0
  180. package/registry/theme.json +78 -0
  181. package/registry/toast.json +54 -0
  182. package/registry/tooltip.json +91 -0
  183. package/dist/chunk-5FRYKFTO.js.map +0 -1
  184. package/dist/chunk-QRG5RYEI.js.map +0 -1
package/dist/tabs.d.ts CHANGED
@@ -65,7 +65,10 @@ declare const tabs: tailwind_variants.TVReturnType<{
65
65
  content: string;
66
66
  }>>;
67
67
  type TabsVariants = VariantProps<typeof tabs>;
68
- type TabsRootProps = React.ComponentProps<typeof Tabs$1.Root> & TabsVariants;
68
+ type TabsRootProps = React.ComponentProps<typeof Tabs$1.Root> & {
69
+ /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */
70
+ variant?: TabsVariants["variant"];
71
+ };
69
72
  /** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */
70
73
  declare function TabsRoot({ variant, className, ...props }: TabsRootProps): React.JSX.Element;
71
74
  /** The tabs. Name it (`aria-label`) when no heading says what it switches. */
package/dist/tabs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tabs/tabs.tsx"],"sourcesContent":["// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n list: \"flex shrink-0 data-[orientation=vertical]:flex-col\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-fade min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list: \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-full after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list: \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list: \"gap-1.5 overflow-x-auto overscroll-x-contain [scrollbar-width:none] data-[orientation=horizontal]:pb-1\",\n trigger:\n \"h-9 rounded-full border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & TabsVariants;\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.List data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n"],"mappings":";;;AAEA,SAAS,QAAQ,qBAAqB;AACtC,YAAY,WAAW;AACvB,SAAS,UAA6B;AAoDhC;AAlDC,IAAM,OAAO,GAAG;AAAA,EACrB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SACE;AAAA,IAEF,SAAS;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA;AAAA,MAEP,WAAW;AAAA,QACT,MAAM;AAAA,QACN,SACE;AAAA,MAIJ;AAAA;AAAA,MAEA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,SACE;AAAA,MAEJ;AAAA;AAAA,MAEA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SACE;AAAA,MAEJ;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,SAAS,YAAY;AAC1C,CAAC;AAKD,IAAM,iBAAuB,oBAAuB,WAAW;AAKxD,SAAS,SAAS,EAAE,UAAU,aAAa,WAAW,GAAG,MAAM,GAAkB;AACtF,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,SAC9B,8BAAC,cAAc,MAAd,EAAmB,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO,GAC1G;AAEJ;AAGO,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAoD;AACjG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,SAAO,oBAAC,cAAc,MAAd,EAAmB,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACjH;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAChG;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACrF;AAGO,IAAM,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU,SAAS,aAAa,SAAS,YAAY;","names":[]}
1
+ {"version":3,"sources":["../src/tabs/tabs.tsx"],"sourcesContent":["// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n list: \"flex shrink-0 data-[orientation=vertical]:flex-col\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-fade min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list: \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-full after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list: \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list: \"gap-1.5 overflow-x-auto overscroll-x-contain [scrollbar-width:none] data-[orientation=horizontal]:pb-1\",\n trigger:\n \"h-9 rounded-full border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */\n variant?: TabsVariants[\"variant\"];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.List data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n"],"mappings":";;;AAEA,SAAS,QAAQ,qBAAqB;AACtC,YAAY,WAAW;AACvB,SAAS,UAA6B;AAuDhC;AArDC,IAAM,OAAO,GAAG;AAAA,EACrB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SACE;AAAA,IAEF,SAAS;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA;AAAA,MAEP,WAAW;AAAA,QACT,MAAM;AAAA,QACN,SACE;AAAA,MAIJ;AAAA;AAAA,MAEA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,SACE;AAAA,MAEJ;AAAA;AAAA,MAEA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SACE;AAAA,MAEJ;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,SAAS,YAAY;AAC1C,CAAC;AAKD,IAAM,iBAAuB,oBAAuB,WAAW;AAQxD,SAAS,SAAS,EAAE,UAAU,aAAa,WAAW,GAAG,MAAM,GAAkB;AACtF,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,SAC9B,8BAAC,cAAc,MAAd,EAAmB,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO,GAC1G;AAEJ;AAGO,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAoD;AACjG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,SAAO,oBAAC,cAAc,MAAd,EAAmB,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACjH;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAChG;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACrF;AAGO,IAAM,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU,SAAS,aAAa,SAAS,YAAY;","names":[]}
package/dist/theme.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { a as ButtonVariants } from './button-G5qY4qSY.js';
2
+ import { a as ButtonVariants } from './button-8La7l0V4.js';
3
3
  import 'tailwind-variants';
4
4
 
5
5
  type Mode = "dark" | "light" | "system";
@@ -7,7 +7,8 @@ type Mode = "dark" | "light" | "system";
7
7
  * In <head>, before anything renders: applies the persisted mode and named theme, so the page never flashes.
8
8
  * `nonce` for a strict Content-Security-Policy.
9
9
  */
10
- declare function ThemeScript({ nonce }: {
10
+ declare function ThemeScript({ nonce, }: {
11
+ /** The nonce of a strict Content-Security-Policy (`script-src 'nonce-…'`). */
11
12
  nonce?: string;
12
13
  }): React.JSX.Element;
13
14
  type ThemeContextValue = {
@@ -26,6 +27,13 @@ type ThemeProviderProps = {
26
27
  declare function ThemeProvider({ children, defaultMode }: ThemeProviderProps): React.JSX.Element;
27
28
  declare function useTheme(): ThemeContextValue;
28
29
  type ThemeToggleProps = Omit<React.ComponentProps<"button">, "onClick" | "children"> & Pick<ButtonVariants, "variant" | "shape"> & {
30
+ /** Any `Button` variant. */
31
+ variant?: ButtonVariants["variant"];
32
+ /**
33
+ * rounded · pill.
34
+ * @default "rounded"
35
+ */
36
+ shape?: ButtonVariants["shape"];
29
37
  /** The accessible name — a string, or one per current mode (e.g. `(mode) => t("theme." + mode)`). */
30
38
  label: string | ((mode: Mode) => string);
31
39
  };
package/dist/theme.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  buttonVariants
4
- } from "./chunk-QRG5RYEI.js";
4
+ } from "./chunk-ZUD4U22H.js";
5
5
 
6
6
  // src/theme/theme.tsx
7
7
  import * as React from "react";
@@ -47,7 +47,9 @@ function applyName(previous, next) {
47
47
  if (previous) classes.remove(`theme-${previous}`);
48
48
  if (next) classes.add(`theme-${next}`);
49
49
  }
50
- function ThemeScript({ nonce }) {
50
+ function ThemeScript({
51
+ nonce
52
+ }) {
51
53
  const js = `(function(){try{var m=localStorage.getItem("${MODE_KEY}")||"system";var n=localStorage.getItem("${NAME_KEY}");var d=matchMedia("${DARK_QUERY}").matches;var c=document.documentElement.classList;c.toggle("light",m==="light"||(m==="system"&&!d));if(n)c.add("theme-"+n)}catch(e){}})()`;
52
54
  return /* @__PURE__ */ jsx("script", { nonce, dangerouslySetInnerHTML: { __html: js } });
53
55
  }
package/dist/theme.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theme/theme.tsx"],"sourcesContent":["// Client. ONE mechanism for the whole organisation: dark by default, `html.light` for light, `html.theme-<name>` for a\n// named theme, persisted in localStorage (`kz-theme` for the mode, `kz-theme-name` for the named theme).\nimport * as React from \"react\";\n\nimport { type ButtonVariants, buttonVariants } from \"../button/button\";\n\nexport type Mode = \"dark\" | \"light\" | \"system\";\n\nconst MODE_KEY = \"kz-theme\";\nconst NAME_KEY = \"kz-theme-name\";\nconst MODES: readonly Mode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isMode = (value: unknown): value is Mode => MODES.includes(value as Mode);\n\n// Storage blocked (private mode, sandbox): the choice lives in memory for the page.\nconst memory = new Map<string, string>();\n\nfunction read(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return memory.get(key) ?? null;\n }\n}\n\nfunction write(key: string, value: string | null) {\n if (value === null) memory.delete(key);\n else memory.set(key, value);\n try {\n if (value === null) localStorage.removeItem(key);\n else localStorage.setItem(key, value);\n } catch {\n // storage blocked: memory holds it\n }\n}\n\n// The persisted choice is an external store: every provider and tab reads the same value, the server reads none.\nconst listeners = new Set<() => void>();\nconst notify = () => listeners.forEach((listener) => listener());\nfunction subscribe(listener: () => void) {\n listeners.add(listener);\n window.addEventListener(\"storage\", listener);\n return () => {\n listeners.delete(listener);\n window.removeEventListener(\"storage\", listener);\n };\n}\n\nfunction applyMode(mode: Mode) {\n const dark = window.matchMedia(DARK_QUERY).matches;\n document.documentElement.classList.toggle(\"light\", mode === \"light\" || (mode === \"system\" && !dark));\n}\n\nfunction applyName(previous: string | null, next: string | null) {\n const classes = document.documentElement.classList;\n if (previous) classes.remove(`theme-${previous}`);\n if (next) classes.add(`theme-${next}`);\n}\n\n/**\n * In <head>, before anything renders: applies the persisted mode and named theme, so the page never flashes.\n * `nonce` for a strict Content-Security-Policy.\n */\nexport function ThemeScript({ nonce }: { nonce?: string }) {\n const js =\n `(function(){try{var m=localStorage.getItem(\"${MODE_KEY}\")||\"system\";var n=localStorage.getItem(\"${NAME_KEY}\");` +\n `var d=matchMedia(\"${DARK_QUERY}\").matches;var c=document.documentElement.classList;` +\n `c.toggle(\"light\",m===\"light\"||(m===\"system\"&&!d));if(n)c.add(\"theme-\"+n)}catch(e){}})()`;\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: js }} />;\n}\n\nexport type ThemeContextValue = {\n /** The chosen mode: dark, light or follow the system. */\n mode: Mode;\n setMode: (mode: Mode) => void;\n /** The named theme (`html.theme-<name>`), or null. */\n theme: string | null;\n setTheme: (theme: string | null) => void;\n};\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\nexport type ThemeProviderProps = {\n children: React.ReactNode;\n /** The mode before anything is persisted. */\n defaultMode?: Mode;\n};\n\nexport function ThemeProvider({ children, defaultMode = \"system\" }: ThemeProviderProps) {\n // Before hydration the server knows nothing persisted (ThemeScript has already applied it to <html>).\n const stored = React.useSyncExternalStore(subscribe, () => read(MODE_KEY), () => null);\n const mode: Mode = isMode(stored) ? stored : defaultMode;\n const theme = React.useSyncExternalStore(subscribe, () => read(NAME_KEY), () => null);\n\n // \"system\" follows the operating system while the page is open.\n React.useEffect(() => {\n if (mode !== \"system\") return;\n const query = window.matchMedia(DARK_QUERY);\n const onChange = () => applyMode(\"system\");\n query.addEventListener?.(\"change\", onChange);\n return () => query.removeEventListener?.(\"change\", onChange);\n }, [mode]);\n\n const setMode = React.useCallback((next: Mode) => {\n write(MODE_KEY, next);\n applyMode(next);\n notify();\n }, []);\n\n const setTheme = React.useCallback((next: string | null) => {\n applyName(read(NAME_KEY), next);\n write(NAME_KEY, next);\n notify();\n }, []);\n\n const value = React.useMemo(() => ({ mode, setMode, theme, setTheme }), [mode, setMode, theme, setTheme]);\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext);\n if (!context) throw new Error(\"useTheme: wrap the app in <ThemeProvider>\");\n return context;\n}\n\nconst ICONS: Record<Mode, React.ReactNode> = {\n dark: <path d=\"M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z\" />,\n light: (\n <>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4\" />\n </>\n ),\n system: (\n <>\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"12\" rx=\"2\" />\n <path d=\"M8 20h8M12 16v4\" />\n </>\n ),\n};\n\nexport type ThemeToggleProps = Omit<React.ComponentProps<\"button\">, \"onClick\" | \"children\"> &\n Pick<ButtonVariants, \"variant\" | \"shape\"> & {\n /** The accessible name — a string, or one per current mode (e.g. `(mode) => t(\"theme.\" + mode)`). */\n label: string | ((mode: Mode) => string);\n };\n\n/**\n * An icon button (styled as `IconButton`, without its Slot) that cycles dark → light → system. Needs a\n * <ThemeProvider> above it. State: `data-mode`.\n */\nexport function ThemeToggle({ label, variant = \"ghost\", shape, className, ...props }: ThemeToggleProps) {\n const { mode, setMode } = useTheme();\n const next = MODES[(MODES.indexOf(mode) + 1) % MODES.length] as Mode;\n const name = typeof label === \"function\" ? label(mode) : label;\n return (\n <button\n type=\"button\"\n aria-label={name}\n title={name}\n data-mode={mode}\n onClick={() => setMode(next)}\n className={buttonVariants({ variant, shape, size: \"icon\", className })}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" className=\"h-5 w-5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {ICONS[mode]}\n </svg>\n </button>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/theme/theme.tsx"],"sourcesContent":["// Client. ONE mechanism for the whole organisation: dark by default, `html.light` for light, `html.theme-<name>` for a\n// named theme, persisted in localStorage (`kz-theme` for the mode, `kz-theme-name` for the named theme).\nimport * as React from \"react\";\n\nimport { type ButtonVariants, buttonVariants } from \"../button/button\";\n\nexport type Mode = \"dark\" | \"light\" | \"system\";\n\nconst MODE_KEY = \"kz-theme\";\nconst NAME_KEY = \"kz-theme-name\";\nconst MODES: readonly Mode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isMode = (value: unknown): value is Mode => MODES.includes(value as Mode);\n\n// Storage blocked (private mode, sandbox): the choice lives in memory for the page.\nconst memory = new Map<string, string>();\n\nfunction read(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return memory.get(key) ?? null;\n }\n}\n\nfunction write(key: string, value: string | null) {\n if (value === null) memory.delete(key);\n else memory.set(key, value);\n try {\n if (value === null) localStorage.removeItem(key);\n else localStorage.setItem(key, value);\n } catch {\n // storage blocked: memory holds it\n }\n}\n\n// The persisted choice is an external store: every provider and tab reads the same value, the server reads none.\nconst listeners = new Set<() => void>();\nconst notify = () => listeners.forEach((listener) => listener());\nfunction subscribe(listener: () => void) {\n listeners.add(listener);\n window.addEventListener(\"storage\", listener);\n return () => {\n listeners.delete(listener);\n window.removeEventListener(\"storage\", listener);\n };\n}\n\nfunction applyMode(mode: Mode) {\n const dark = window.matchMedia(DARK_QUERY).matches;\n document.documentElement.classList.toggle(\"light\", mode === \"light\" || (mode === \"system\" && !dark));\n}\n\nfunction applyName(previous: string | null, next: string | null) {\n const classes = document.documentElement.classList;\n if (previous) classes.remove(`theme-${previous}`);\n if (next) classes.add(`theme-${next}`);\n}\n\n/**\n * In <head>, before anything renders: applies the persisted mode and named theme, so the page never flashes.\n * `nonce` for a strict Content-Security-Policy.\n */\nexport function ThemeScript({\n nonce,\n}: {\n /** The nonce of a strict Content-Security-Policy (`script-src 'nonce-…'`). */\n nonce?: string;\n}) {\n const js =\n `(function(){try{var m=localStorage.getItem(\"${MODE_KEY}\")||\"system\";var n=localStorage.getItem(\"${NAME_KEY}\");` +\n `var d=matchMedia(\"${DARK_QUERY}\").matches;var c=document.documentElement.classList;` +\n `c.toggle(\"light\",m===\"light\"||(m===\"system\"&&!d));if(n)c.add(\"theme-\"+n)}catch(e){}})()`;\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: js }} />;\n}\n\nexport type ThemeContextValue = {\n /** The chosen mode: dark, light or follow the system. */\n mode: Mode;\n setMode: (mode: Mode) => void;\n /** The named theme (`html.theme-<name>`), or null. */\n theme: string | null;\n setTheme: (theme: string | null) => void;\n};\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\nexport type ThemeProviderProps = {\n children: React.ReactNode;\n /** The mode before anything is persisted. */\n defaultMode?: Mode;\n};\n\nexport function ThemeProvider({ children, defaultMode = \"system\" }: ThemeProviderProps) {\n // Before hydration the server knows nothing persisted (ThemeScript has already applied it to <html>).\n const stored = React.useSyncExternalStore(subscribe, () => read(MODE_KEY), () => null);\n const mode: Mode = isMode(stored) ? stored : defaultMode;\n const theme = React.useSyncExternalStore(subscribe, () => read(NAME_KEY), () => null);\n\n // \"system\" follows the operating system while the page is open.\n React.useEffect(() => {\n if (mode !== \"system\") return;\n const query = window.matchMedia(DARK_QUERY);\n const onChange = () => applyMode(\"system\");\n query.addEventListener?.(\"change\", onChange);\n return () => query.removeEventListener?.(\"change\", onChange);\n }, [mode]);\n\n const setMode = React.useCallback((next: Mode) => {\n write(MODE_KEY, next);\n applyMode(next);\n notify();\n }, []);\n\n const setTheme = React.useCallback((next: string | null) => {\n applyName(read(NAME_KEY), next);\n write(NAME_KEY, next);\n notify();\n }, []);\n\n const value = React.useMemo(() => ({ mode, setMode, theme, setTheme }), [mode, setMode, theme, setTheme]);\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext);\n if (!context) throw new Error(\"useTheme: wrap the app in <ThemeProvider>\");\n return context;\n}\n\nconst ICONS: Record<Mode, React.ReactNode> = {\n dark: <path d=\"M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z\" />,\n light: (\n <>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4\" />\n </>\n ),\n system: (\n <>\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"12\" rx=\"2\" />\n <path d=\"M8 20h8M12 16v4\" />\n </>\n ),\n};\n\nexport type ThemeToggleProps = Omit<React.ComponentProps<\"button\">, \"onClick\" | \"children\"> &\n Pick<ButtonVariants, \"variant\" | \"shape\"> & {\n /** Any `Button` variant. */\n variant?: ButtonVariants[\"variant\"];\n /**\n * rounded · pill.\n * @default \"rounded\"\n */\n shape?: ButtonVariants[\"shape\"];\n /** The accessible name — a string, or one per current mode (e.g. `(mode) => t(\"theme.\" + mode)`). */\n label: string | ((mode: Mode) => string);\n };\n\n/**\n * An icon button (styled as `IconButton`, without its Slot) that cycles dark → light → system. Needs a\n * <ThemeProvider> above it. State: `data-mode`.\n */\nexport function ThemeToggle({ label, variant = \"ghost\", shape, className, ...props }: ThemeToggleProps) {\n const { mode, setMode } = useTheme();\n const next = MODES[(MODES.indexOf(mode) + 1) % MODES.length] as Mode;\n const name = typeof label === \"function\" ? label(mode) : label;\n return (\n <button\n type=\"button\"\n aria-label={name}\n title={name}\n data-mode={mode}\n onClick={() => setMode(next)}\n className={buttonVariants({ variant, shape, size: \"icon\", className })}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" className=\"h-5 w-5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {ICONS[mode]}\n </svg>\n </button>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/tooltip.d.ts CHANGED
@@ -8,8 +8,11 @@ type TooltipProps = Omit<React.ComponentProps<typeof Tooltip$1.Content>, "conten
8
8
  content: React.ReactNode;
9
9
  /** The trigger, rendered as is (`asChild`): a focusable element. */
10
10
  children: React.ReactNode;
11
+ /** Open or closed (controlled), with `onOpenChange`. */
11
12
  open?: boolean;
13
+ /** Open at first, uncontrolled. */
12
14
  defaultOpen?: boolean;
15
+ /** Called when it opens or closes. */
13
16
  onOpenChange?: (open: boolean) => void;
14
17
  /** Hover delay in ms before it opens (300 by default). */
15
18
  delayDuration?: number;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tooltip/tooltip.tsx"],"sourcesContent":["// Client (a portal). A short label for a control, on hover after `delayDuration` and on keyboard focus at once;\n// Escape dismisses it. It names nothing: the control keeps its own accessible name (an IconButton's `label`).\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\n/** Optional, once near the root: shares `delayDuration` and the skip delay between tooltips. */\nexport const TooltipProvider = TooltipPrimitive.Provider;\n\nexport type TooltipProps = Omit<React.ComponentProps<typeof TooltipPrimitive.Content>, \"content\" | \"children\"> & {\n /** What the tooltip says — passed translated. */\n content: React.ReactNode;\n /** The trigger, rendered as is (`asChild`): a focusable element. */\n children: React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Hover delay in ms before it opens (300 by default). */\n delayDuration?: number;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function Tooltip({\n content,\n children,\n open,\n defaultOpen,\n onOpenChange,\n delayDuration = 300,\n container,\n sideOffset = 6,\n collisionPadding = 8,\n className,\n ...props\n}: TooltipProps) {\n return (\n <TooltipPrimitive.Provider delayDuration={delayDuration}>\n <TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>\n <TooltipPrimitive.Portal container={container}>\n <TooltipPrimitive.Content\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n className={cn(floating, \"max-w-xs px-2.5 py-1.5 text-xs font-medium origin-(--radix-tooltip-content-transform-origin)\", className)}\n {...props}\n >\n {content}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n </TooltipPrimitive.Root>\n </TooltipPrimitive.Provider>\n );\n}\n"],"mappings":";;;;;;;;;AAEA,SAAS,WAAW,wBAAwB;AAsCtC,SACE,KADF;AA/BC,IAAM,kBAAkB,iBAAiB;AAgBzC,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB;AAAA,EACA,GAAG;AACL,GAAiB;AACf,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,eACzB,+BAAC,iBAAiB,MAAjB,EAAsB,MAAY,aAA0B,cAC3D;AAAA,wBAAC,iBAAiB,SAAjB,EAAyB,SAAO,MAAE,UAAS;AAAA,IAC5C,oBAAC,iBAAiB,QAAjB,EAAwB,WACvB;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAW,GAAG,UAAU,gGAAgG,SAAS;AAAA,QAChI,GAAG;AAAA,QAEH;AAAA;AAAA,IACH,GACF;AAAA,KACF,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/tooltip/tooltip.tsx"],"sourcesContent":["// Client (a portal). A short label for a control, on hover after `delayDuration` and on keyboard focus at once;\n// Escape dismisses it. It names nothing: the control keeps its own accessible name (an IconButton's `label`).\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\n/** Optional, once near the root: shares `delayDuration` and the skip delay between tooltips. */\nexport const TooltipProvider = TooltipPrimitive.Provider;\n\nexport type TooltipProps = Omit<React.ComponentProps<typeof TooltipPrimitive.Content>, \"content\" | \"children\"> & {\n /** What the tooltip says — passed translated. */\n content: React.ReactNode;\n /** The trigger, rendered as is (`asChild`): a focusable element. */\n children: React.ReactNode;\n /** Open or closed (controlled), with `onOpenChange`. */\n open?: boolean;\n /** Open at first, uncontrolled. */\n defaultOpen?: boolean;\n /** Called when it opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Hover delay in ms before it opens (300 by default). */\n delayDuration?: number;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function Tooltip({\n content,\n children,\n open,\n defaultOpen,\n onOpenChange,\n delayDuration = 300,\n container,\n sideOffset = 6,\n collisionPadding = 8,\n className,\n ...props\n}: TooltipProps) {\n return (\n <TooltipPrimitive.Provider delayDuration={delayDuration}>\n <TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>\n <TooltipPrimitive.Portal container={container}>\n <TooltipPrimitive.Content\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n className={cn(floating, \"max-w-xs px-2.5 py-1.5 text-xs font-medium origin-(--radix-tooltip-content-transform-origin)\", className)}\n {...props}\n >\n {content}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n </TooltipPrimitive.Root>\n </TooltipPrimitive.Provider>\n );\n}\n"],"mappings":";;;;;;;;;AAEA,SAAS,WAAW,wBAAwB;AAyCtC,SACE,KADF;AAlCC,IAAM,kBAAkB,iBAAiB;AAmBzC,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB;AAAA,EACA,GAAG;AACL,GAAiB;AACf,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,eACzB,+BAAC,iBAAiB,MAAjB,EAAsB,MAAY,aAA0B,cAC3D;AAAA,wBAAC,iBAAiB,SAAjB,EAAyB,SAAO,MAAE,UAAS;AAAA,IAC5C,oBAAC,iBAAiB,QAAjB,EAAwB,WACvB;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAW,GAAG,UAAU,gGAAgG,SAAS;AAAA,QAChI,GAAG;AAAA,QAEH;AAAA;AAAA,IACH,GACF;AAAA,KACF,GACF;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia) and the motion signature — web and /native.",
5
5
  "keywords": [
6
6
  "krizaka",
@@ -168,10 +168,20 @@
168
168
  "types": "./dist/native.d.ts",
169
169
  "import": "./dist/native.js"
170
170
  },
171
+ "./registry/demos/*": {
172
+ "types": "./registry/demos/*.d.ts",
173
+ "import": "./registry/demos/*.js"
174
+ },
175
+ "./registry/*": "./registry/*.json",
171
176
  "./package.json": "./package.json"
172
177
  },
173
178
  "files": [
174
179
  "dist",
180
+ "registry/*.json",
181
+ "registry/demos/*.tsx",
182
+ "registry/demos/*.js",
183
+ "registry/demos/*.d.ts",
184
+ "!registry/demos/*.test.*",
175
185
  "tailwind.css",
176
186
  "README.md",
177
187
  "LICENSE"
@@ -180,7 +190,7 @@
180
190
  "*.css"
181
191
  ],
182
192
  "peerDependencies": {
183
- "@krizaka/tailwind": ">=2.0.0-beta.2",
193
+ "@krizaka/tailwind": ">=2.0.0-beta.3",
184
194
  "react": ">=18",
185
195
  "react-dom": ">=18",
186
196
  "react-native": ">=0.76",
@@ -202,6 +212,7 @@
202
212
  "happy-dom": "^20.0.0",
203
213
  "publint": "^0.3.25",
204
214
  "react": "^19.2.0",
215
+ "react-docgen-typescript": "^2.4.0",
205
216
  "react-dom": "^19.2.0",
206
217
  "react-native": "^0.86.3",
207
218
  "react-native-svg": "^15.15.4",
@@ -564,7 +575,7 @@
564
575
  "tailwind-variants": "^3.3.1"
565
576
  },
566
577
  "scripts": {
567
- "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css')\"",
578
+ "build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/demos'))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/demos/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css')\" && node scripts/build-registry.mjs",
568
579
  "lint": "eslint . --max-warnings 0 && tsc --noEmit --noUnusedParameters --noFallthroughCasesInSwitch --noImplicitReturns",
569
580
  "typecheck": "tsc --noEmit",
570
581
  "test": "vitest run",
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "alert",
3
+ "type": "primitive",
4
+ "description": "`Alert`, `alertVariants`. Server-safe.",
5
+ "files": [
6
+ {
7
+ "path": "alert/alert.tsx",
8
+ "content": "// Server-safe. A message in the flow of the page (not a toast): a tone, an icon, a title, a text, an action.\n// danger and warning are announced at once (role=\"alert\"); info and success politely (role=\"status\").\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\n// Soft tones (a tint, a tinted border, a coloured icon): the text stays a text role — the invariant status tokens are\n// too light for small text on a light surface (WCAG AA).\nexport const alertVariants = tv({\n slots: {\n root: \"relative flex w-full items-start gap-3 rounded-xl border p-4 text-sm text-fg\",\n icon: \"mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:h-5 [&>svg]:w-5\",\n content: \"flex min-w-0 flex-1 flex-col gap-1\",\n title: \"font-semibold text-fg\",\n description: \"text-fg-secondary\",\n action: \"flex shrink-0 items-center gap-2 self-center\",\n },\n variants: {\n tone: {\n info: { root: \"border-info/40 bg-info/10\", icon: \"text-info\" },\n success: { root: \"border-success/40 bg-success/10\", icon: \"text-success\" },\n warning: { root: \"border-warning/50 bg-warning/10\", icon: \"text-warning\" },\n danger: { root: \"border-danger/50 bg-danger/10\", icon: \"text-danger\" },\n },\n },\n defaultVariants: { tone: \"info\" },\n});\n\nexport type AlertTone = NonNullable<VariantProps<typeof alertVariants>[\"tone\"]>;\n\nexport type AlertProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The tone: `danger` and `warning` are announced at once (`role=\"alert\"`), `info` and `success` politely. */\n tone?: AlertTone;\n /** A decorative icon (hidden from assistive technology). */\n icon?: React.ReactNode;\n /** The headline, in bold above the text. */\n title?: React.ReactNode;\n /** What to do: a Button, a link. */\n action?: React.ReactNode;\n};\n\nexport function Alert({ tone = \"info\", icon, title, action, className, children, ...props }: AlertProps) {\n const s = alertVariants({ tone });\n return (\n <div role={tone === \"danger\" || tone === \"warning\" ? \"alert\" : \"status\"} data-tone={tone} className={s.root({ className })} {...props}>\n {icon && (\n <span aria-hidden className={s.icon()}>\n {icon}\n </span>\n )}\n <div className={s.content()}>\n {title && <p className={s.title()}>{title}</p>}\n {children && <div className={s.description()}>{children}</div>}\n </div>\n {action && <div className={s.action()}>{action}</div>}\n </div>\n );\n}\n"
9
+ },
10
+ {
11
+ "path": "alert/index.ts",
12
+ "content": "/** @krizaka/ui/alert — `Alert`, `alertVariants`. Server-safe. */\nexport { Alert, type AlertProps, type AlertTone, alertVariants } from \"./alert\";\n"
13
+ }
14
+ ],
15
+ "dependencies": [
16
+ "tailwind-variants@^3.3.1"
17
+ ],
18
+ "registryDependencies": [],
19
+ "demo": {
20
+ "path": "demos/alert.tsx",
21
+ "content": "import { Alert } from \"@krizaka/ui/alert\";\n\nconst INFO = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M12 16v-4M12 8h.01M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Z\" />\n </svg>\n);\n\nexport default function AlertDemo() {\n return (\n <Alert title=\"New payout schedule\" icon={INFO}>\n Payouts now arrive every Monday.\n </Alert>\n );\n}\n"
22
+ },
23
+ "props": [
24
+ {
25
+ "component": "Alert",
26
+ "description": "",
27
+ "props": [
28
+ {
29
+ "name": "action",
30
+ "type": "ReactNode",
31
+ "default": null,
32
+ "description": "What to do: a Button, a link.",
33
+ "required": false
34
+ },
35
+ {
36
+ "name": "icon",
37
+ "type": "ReactNode",
38
+ "default": null,
39
+ "description": "A decorative icon (hidden from assistive technology).",
40
+ "required": false
41
+ },
42
+ {
43
+ "name": "title",
44
+ "type": "ReactNode",
45
+ "default": null,
46
+ "description": "The headline, in bold above the text.",
47
+ "required": false
48
+ },
49
+ {
50
+ "name": "tone",
51
+ "type": "AlertTone",
52
+ "default": "info",
53
+ "description": "The tone: `danger` and `warning` are announced at once (`role=\"alert\"`), `info` and `success` politely.",
54
+ "required": false
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "avatar",
3
+ "type": "primitive",
4
+ "description": "`Avatar`, `Avatar.Group`. Client (\"use client\").",
5
+ "files": [
6
+ {
7
+ "path": "avatar/avatar.tsx",
8
+ "content": "// Client: Radix tracks the image's loading state and shows the fallback until it has loaded (or when it fails).\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nconst avatar = tv({\n slots: {\n root: \"relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle\",\n image: \"h-full w-full object-cover\",\n fallback: \"flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary\",\n group: \"flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0\",\n },\n variants: {\n size: {\n xs: { root: \"h-5 w-5 text-[9px]\" },\n sm: { root: \"h-8 w-8 text-xs\" },\n md: { root: \"h-10 w-10 text-sm\" },\n lg: { root: \"h-14 w-14 text-base\" },\n xl: { root: \"h-20 w-20 text-lg\" },\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type AvatarSize = NonNullable<VariantProps<typeof avatar>[\"size\"]>;\n\nexport type AvatarProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */\n alt?: string;\n /** Shown while the image loads, when it fails or when there is none: initials, an icon. */\n fallback?: React.ReactNode;\n /**\n * xs · sm · md · lg · xl.\n * @default \"md\"\n */\n size?: AvatarSize;\n};\n\nexport function AvatarRoot({ src, alt = \"\", fallback, size, className, ...props }: AvatarProps) {\n const s = avatar({ size });\n return (\n <AvatarPrimitive.Root data-size={size ?? \"md\"} className={s.root({ className })} {...props}>\n {src && <AvatarPrimitive.Image src={src} alt={alt} className={s.image()} />}\n <AvatarPrimitive.Fallback delayMs={src ? 300 : 0} className={s.fallback()}>\n {fallback}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n );\n}\n\nexport type AvatarGroupProps = React.ComponentProps<\"div\"> & {\n /** How many avatars are shown; the rest becomes a `+n` avatar. */\n max?: number;\n /** The size of the `+n` avatar (match the avatars of the group). */\n size?: AvatarSize;\n};\n\n/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */\nexport function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps) {\n const items = React.Children.toArray(children);\n const shown = max === undefined ? items : items.slice(0, max);\n const rest = items.length - shown.length;\n return (\n <div className={avatar().group({ className })} {...props}>\n {shown}\n {rest > 0 && <AvatarRoot size={size} fallback={`+${rest}`} />}\n </div>\n );\n}\n\n/** `Avatar` (an image with a fallback) and `Avatar.Group`. */\nexport const Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });\n"
9
+ },
10
+ {
11
+ "path": "avatar/index.ts",
12
+ "content": "/** @krizaka/ui/avatar — `Avatar`, `Avatar.Group`. Client (\"use client\"). */\nexport { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, AvatarRoot, type AvatarSize } from \"./avatar\";\n"
13
+ }
14
+ ],
15
+ "dependencies": [
16
+ "radix-ui@^1.7.0",
17
+ "tailwind-variants@^3.3.1"
18
+ ],
19
+ "registryDependencies": [],
20
+ "demo": {
21
+ "path": "demos/avatar.tsx",
22
+ "content": "import { Avatar } from \"@krizaka/ui/avatar\";\n\n// A self-contained portrait (no network).\nconst PORTRAIT =\n \"data:image/svg+xml;utf8,\" +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 80 80\"><rect width=\"80\" height=\"80\" fill=\"#3b82f6\"/>' +\n '<circle cx=\"40\" cy=\"31\" r=\"14\" fill=\"#dbeafe\"/><path d=\"M12 80a28 24 0 0 1 56 0z\" fill=\"#dbeafe\"/></svg>',\n );\n\nexport default function AvatarDemo() {\n return <Avatar src={PORTRAIT} alt=\"Oussama\" fallback=\"OA\" />;\n}\n"
23
+ },
24
+ "props": [
25
+ {
26
+ "component": "Avatar",
27
+ "description": "`Avatar` (an image with a fallback) and `Avatar.Group`.",
28
+ "props": [
29
+ {
30
+ "name": "alt",
31
+ "type": "string",
32
+ "default": null,
33
+ "description": "The image's text alternative (the person's name); empty when a visible name sits next to the avatar.",
34
+ "required": false
35
+ },
36
+ {
37
+ "name": "fallback",
38
+ "type": "ReactNode",
39
+ "default": null,
40
+ "description": "Shown while the image loads, when it fails or when there is none: initials, an icon.",
41
+ "required": false
42
+ },
43
+ {
44
+ "name": "size",
45
+ "type": "AvatarSize",
46
+ "default": "md",
47
+ "description": "xs · sm · md · lg · xl.",
48
+ "required": false
49
+ },
50
+ {
51
+ "name": "src",
52
+ "type": "string | null",
53
+ "default": null,
54
+ "description": "The image's URL; `null` or nothing shows the `fallback`.",
55
+ "required": false
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "component": "AvatarGroup",
61
+ "description": "Overlapping avatars; past `max`, a `+n` avatar counts the rest.",
62
+ "props": [
63
+ {
64
+ "name": "max",
65
+ "type": "number",
66
+ "default": null,
67
+ "description": "How many avatars are shown; the rest becomes a `+n` avatar.",
68
+ "required": false
69
+ },
70
+ {
71
+ "name": "size",
72
+ "type": "AvatarSize",
73
+ "default": null,
74
+ "description": "The size of the `+n` avatar (match the avatars of the group).",
75
+ "required": false
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "component": "AvatarRoot",
81
+ "description": "",
82
+ "props": [
83
+ {
84
+ "name": "alt",
85
+ "type": "string",
86
+ "default": "",
87
+ "description": "The image's text alternative (the person's name); empty when a visible name sits next to the avatar.",
88
+ "required": false
89
+ },
90
+ {
91
+ "name": "fallback",
92
+ "type": "ReactNode",
93
+ "default": null,
94
+ "description": "Shown while the image loads, when it fails or when there is none: initials, an icon.",
95
+ "required": false
96
+ },
97
+ {
98
+ "name": "size",
99
+ "type": "AvatarSize",
100
+ "default": "md",
101
+ "description": "xs · sm · md · lg · xl.",
102
+ "required": false
103
+ },
104
+ {
105
+ "name": "src",
106
+ "type": "string | null",
107
+ "default": null,
108
+ "description": "The image's URL; `null` or nothing shows the `fallback`.",
109
+ "required": false
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "badge",
3
+ "type": "primitive",
4
+ "description": "`Badge`, `badgeVariants`. Server-safe.",
5
+ "files": [
6
+ {
7
+ "path": "badge/badge.tsx",
8
+ "content": "// Server-safe: no hook, no context.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\n// Status tones are soft (a tint, a coloured dot, a text role): the invariant status tokens are too light to carry\n// small text on a light surface, or to sit under white text (WCAG AA).\nexport const badgeVariants = tv({\n base: \"inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-full font-semibold uppercase tracking-wider\",\n variants: {\n tone: {\n neutral: \"bg-surface-3 text-fg-secondary\",\n accent: \"bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent\",\n success: \"bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success\",\n warning: \"bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning\",\n danger: \"bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger\",\n /** On a media: veil and text invariant, identical in both themes. */\n scrim: \"bg-scrim text-fg-on-media backdrop-blur-md\",\n },\n size: { sm: \"px-2 py-0.5 text-[10px]\", md: \"px-2.5 py-1 text-[11px]\" },\n pulse: { true: \"[&>[data-dot]]:motion-safe:animate-pulse\" },\n },\n defaultVariants: { tone: \"neutral\", size: \"sm\" },\n});\n\nexport type BadgeVariants = VariantProps<typeof badgeVariants>;\n\nexport type BadgeProps = React.ComponentProps<\"span\"> &\n BadgeVariants & {\n /**\n * neutral · accent · success · warning · danger (soft tints, the text stays a text role) · scrim (on a media).\n * @default \"neutral\"\n */\n tone?: BadgeVariants[\"tone\"];\n /**\n * sm · md.\n * @default \"sm\"\n */\n size?: BadgeVariants[\"size\"];\n /** Makes the `dot` pulse (stopped under reduced motion). */\n pulse?: BadgeVariants[\"pulse\"];\n /** A small dot before the text, in the tone's colour (pulsing with `pulse`). */\n dot?: boolean;\n };\n\nexport function Badge({ tone, size, pulse, dot, className, children, ...props }: BadgeProps) {\n return (\n <span data-tone={tone ?? \"neutral\"} className={badgeVariants({ tone, size, pulse, className })} {...props}>\n {dot && <span data-dot=\"\" aria-hidden className=\"h-1.5 w-1.5 rounded-full bg-current\" />}\n {children}\n </span>\n );\n}\n"
9
+ },
10
+ {
11
+ "path": "badge/index.ts",
12
+ "content": "/** @krizaka/ui/badge — `Badge`, `badgeVariants`. Server-safe. */\nexport { Badge, type BadgeProps, type BadgeVariants, badgeVariants } from \"./badge\";\n"
13
+ }
14
+ ],
15
+ "dependencies": [
16
+ "tailwind-variants@^3.3.1"
17
+ ],
18
+ "registryDependencies": [],
19
+ "demo": {
20
+ "path": "demos/badge.tsx",
21
+ "content": "import { Badge } from \"@krizaka/ui/badge\";\n\nexport default function BadgeDemo() {\n return <Badge>New</Badge>;\n}\n"
22
+ },
23
+ "props": [
24
+ {
25
+ "component": "Badge",
26
+ "description": "",
27
+ "props": [
28
+ {
29
+ "name": "dot",
30
+ "type": "boolean",
31
+ "default": null,
32
+ "description": "A small dot before the text, in the tone's colour (pulsing with `pulse`).",
33
+ "required": false
34
+ },
35
+ {
36
+ "name": "pulse",
37
+ "type": "boolean",
38
+ "default": null,
39
+ "description": "Makes the `dot` pulse (stopped under reduced motion).",
40
+ "required": false
41
+ },
42
+ {
43
+ "name": "size",
44
+ "type": "\"sm\" | \"md\"",
45
+ "default": "sm",
46
+ "description": "sm · md.",
47
+ "required": false
48
+ },
49
+ {
50
+ "name": "tone",
51
+ "type": "\"success\" | \"warning\" | \"danger\" | \"neutral\" | \"accent\" | \"scrim\"",
52
+ "default": "neutral",
53
+ "description": "neutral · accent · success · warning · danger (soft tints, the text stays a text role) · scrim (on a media).",
54
+ "required": false
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "button",
3
+ "type": "primitive",
4
+ "description": "`Button`, `IconButton`, `buttonVariants`. Server-safe.",
5
+ "files": [
6
+ {
7
+ "path": "button/button.tsx",
8
+ "content": "// 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 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-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The danger token is invariant and too light for small text on a light surface (3.6:1): the label stays a\n // text role, the border and the tint carry the danger.\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), `secondary` (a raised surface), `outline`, `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"
9
+ },
10
+ {
11
+ "path": "button/index.ts",
12
+ "content": "/** @krizaka/ui/button — `Button`, `IconButton`, `buttonVariants`. Server-safe. */\nexport { Button, type ButtonProps, type ButtonVariants, buttonVariants, IconButton, type IconButtonProps } from \"./button\";\n"
13
+ }
14
+ ],
15
+ "dependencies": [
16
+ "radix-ui@^1.7.0",
17
+ "tailwind-variants@^3.3.1"
18
+ ],
19
+ "registryDependencies": [],
20
+ "demo": {
21
+ "path": "demos/button.tsx",
22
+ "content": "import { Button } from \"@krizaka/ui/button\";\n\nexport default function ButtonDemo() {\n return <Button variant=\"primary\">Continue</Button>;\n}\n"
23
+ },
24
+ "props": [
25
+ {
26
+ "component": "Button",
27
+ "description": "",
28
+ "props": [
29
+ {
30
+ "name": "asChild",
31
+ "type": "boolean",
32
+ "default": null,
33
+ "description": "Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged.",
34
+ "required": false
35
+ },
36
+ {
37
+ "name": "loading",
38
+ "type": "boolean",
39
+ "default": null,
40
+ "description": "Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one.",
41
+ "required": false
42
+ },
43
+ {
44
+ "name": "shape",
45
+ "type": "\"rounded\" | \"pill\"",
46
+ "default": "rounded",
47
+ "description": "rounded · pill.",
48
+ "required": false
49
+ },
50
+ {
51
+ "name": "size",
52
+ "type": "\"icon\" | \"sm\" | \"md\" | \"lg\"",
53
+ "default": "md",
54
+ "description": "sm · md · lg, or `icon` (a square).",
55
+ "required": false
56
+ },
57
+ {
58
+ "name": "variant",
59
+ "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
60
+ "default": "secondary",
61
+ "description": "The look: `primary` (the accent), `secondary` (a raised surface), `outline`, `ghost`, `danger`.",
62
+ "required": false
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "component": "IconButton",
68
+ "description": "An icon-only button: `label` is required, it is its accessible name.",
69
+ "props": [
70
+ {
71
+ "name": "label",
72
+ "type": "string",
73
+ "default": null,
74
+ "description": "The accessible name (and tooltip) of the button — required, passed translated.",
75
+ "required": true
76
+ },
77
+ {
78
+ "name": "asChild",
79
+ "type": "boolean",
80
+ "default": null,
81
+ "description": "Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged.",
82
+ "required": false
83
+ },
84
+ {
85
+ "name": "loading",
86
+ "type": "boolean",
87
+ "default": null,
88
+ "description": "Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one.",
89
+ "required": false
90
+ },
91
+ {
92
+ "name": "shape",
93
+ "type": "\"rounded\" | \"pill\"",
94
+ "default": "rounded",
95
+ "description": "rounded · pill.",
96
+ "required": false
97
+ },
98
+ {
99
+ "name": "size",
100
+ "type": "\"icon\" | \"sm\" | \"md\" | \"lg\"",
101
+ "default": "icon",
102
+ "description": "sm · md · lg, or `icon` (a square).",
103
+ "required": false
104
+ },
105
+ {
106
+ "name": "variant",
107
+ "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
108
+ "default": "secondary",
109
+ "description": "The look: `primary` (the accent), `secondary` (a raised surface), `outline`, `ghost`, `danger`.",
110
+ "required": false
111
+ }
112
+ ]
113
+ }
114
+ ]
115
+ }