@krizaka/ui 2.0.0-beta.1 → 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 (210) hide show
  1. package/README.md +44 -3
  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-8La7l0V4.d.ts +83 -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 +1 -1
  13. package/dist/card.js.map +1 -1
  14. package/dist/checkbox.d.ts +52 -0
  15. package/dist/checkbox.js +42 -0
  16. package/dist/checkbox.js.map +1 -0
  17. package/dist/chip.d.ts +120 -0
  18. package/dist/chip.js +93 -0
  19. package/dist/chip.js.map +1 -0
  20. package/dist/chunk-3X5MVH54.js +121 -0
  21. package/dist/chunk-3X5MVH54.js.map +1 -0
  22. package/dist/{chunk-RLOXWF2G.js → chunk-5AXLA4LK.js} +1 -1
  23. package/dist/chunk-5AXLA4LK.js.map +1 -0
  24. package/dist/{chunk-CPMZS4XA.js → chunk-FJDSLNXK.js} +1 -1
  25. package/dist/chunk-FJDSLNXK.js.map +1 -0
  26. package/dist/{chunk-QRG5RYEI.js → chunk-ZUD4U22H.js} +1 -1
  27. package/dist/chunk-ZUD4U22H.js.map +1 -0
  28. package/dist/cn.d.ts +3 -0
  29. package/dist/cn.js +1 -1
  30. package/dist/command.d.ts +159 -0
  31. package/dist/command.js +111 -0
  32. package/dist/command.js.map +1 -0
  33. package/dist/confirm-button.d.ts +23 -0
  34. package/dist/confirm-button.js +85 -0
  35. package/dist/confirm-button.js.map +1 -0
  36. package/dist/countdown.d.ts +3 -0
  37. package/dist/countdown.js.map +1 -1
  38. package/dist/dialog-GQ4Nvuyf.d.ts +157 -0
  39. package/dist/dialog.d.ts +12 -131
  40. package/dist/dialog.js +27 -91
  41. package/dist/dialog.js.map +1 -1
  42. package/dist/dropdown-menu.d.ts +1 -0
  43. package/dist/dropdown-menu.js +1 -1
  44. package/dist/dropdown-menu.js.map +1 -1
  45. package/dist/empty-state.d.ts +2 -0
  46. package/dist/empty-state.js.map +1 -1
  47. package/dist/field.js +1 -1
  48. package/dist/kbd.d.ts +7 -1
  49. package/dist/kbd.js.map +1 -1
  50. package/dist/motion.css +7 -1
  51. package/dist/page-header.d.ts +2 -0
  52. package/dist/page-header.js.map +1 -1
  53. package/dist/popover.js +1 -1
  54. package/dist/progress.d.ts +92 -0
  55. package/dist/progress.js +85 -0
  56. package/dist/progress.js.map +1 -0
  57. package/dist/radio-group.d.ts +77 -0
  58. package/dist/radio-group.js +48 -0
  59. package/dist/radio-group.js.map +1 -0
  60. package/dist/skeleton.d.ts +7 -1
  61. package/dist/skeleton.js.map +1 -1
  62. package/dist/slider.d.ts +90 -0
  63. package/dist/slider.js +82 -0
  64. package/dist/slider.js.map +1 -0
  65. package/dist/spinner.d.ts +5 -0
  66. package/dist/spinner.js.map +1 -1
  67. package/dist/stat.d.ts +2 -0
  68. package/dist/stat.js.map +1 -1
  69. package/dist/switch.d.ts +76 -0
  70. package/dist/switch.js +38 -0
  71. package/dist/switch.js.map +1 -0
  72. package/dist/tabs.d.ts +86 -0
  73. package/dist/tabs.js +60 -0
  74. package/dist/tabs.js.map +1 -0
  75. package/dist/theme.d.ts +11 -60
  76. package/dist/theme.js +4 -2
  77. package/dist/theme.js.map +1 -1
  78. package/dist/toast.js +1 -1
  79. package/dist/tooltip.d.ts +3 -0
  80. package/dist/tooltip.js +1 -1
  81. package/dist/tooltip.js.map +1 -1
  82. package/package.json +146 -6
  83. package/registry/alert.json +59 -0
  84. package/registry/avatar.json +114 -0
  85. package/registry/badge.json +59 -0
  86. package/registry/button.json +115 -0
  87. package/registry/card.json +154 -0
  88. package/registry/checkbox.json +46 -0
  89. package/registry/chip.json +164 -0
  90. package/registry/cn.json +21 -0
  91. package/registry/command.json +162 -0
  92. package/registry/confirm-button.json +104 -0
  93. package/registry/countdown.json +84 -0
  94. package/registry/demos/alert.d.ts +5 -0
  95. package/registry/demos/alert.js +12 -0
  96. package/registry/demos/alert.tsx +15 -0
  97. package/registry/demos/avatar.d.ts +5 -0
  98. package/registry/demos/avatar.js +14 -0
  99. package/registry/demos/avatar.tsx +13 -0
  100. package/registry/demos/badge.d.ts +5 -0
  101. package/registry/demos/badge.js +11 -0
  102. package/registry/demos/badge.tsx +5 -0
  103. package/registry/demos/button.d.ts +5 -0
  104. package/registry/demos/button.js +11 -0
  105. package/registry/demos/button.tsx +5 -0
  106. package/registry/demos/card.d.ts +5 -0
  107. package/registry/demos/card.js +30 -0
  108. package/registry/demos/card.tsx +37 -0
  109. package/registry/demos/checkbox.d.ts +5 -0
  110. package/registry/demos/checkbox.js +11 -0
  111. package/registry/demos/checkbox.tsx +5 -0
  112. package/registry/demos/chip.d.ts +5 -0
  113. package/registry/demos/chip.js +15 -0
  114. package/registry/demos/chip.tsx +11 -0
  115. package/registry/demos/cn.d.ts +7 -0
  116. package/registry/demos/cn.js +17 -0
  117. package/registry/demos/cn.tsx +9 -0
  118. package/registry/demos/command.d.ts +5 -0
  119. package/registry/demos/command.js +32 -0
  120. package/registry/demos/command.tsx +28 -0
  121. package/registry/demos/confirm-button.d.ts +5 -0
  122. package/registry/demos/confirm-button.js +12 -0
  123. package/registry/demos/confirm-button.tsx +13 -0
  124. package/registry/demos/countdown.d.ts +5 -0
  125. package/registry/demos/countdown.js +13 -0
  126. package/registry/demos/countdown.tsx +10 -0
  127. package/registry/demos/dialog.d.ts +7 -0
  128. package/registry/demos/dialog.js +29 -0
  129. package/registry/demos/dialog.tsx +31 -0
  130. package/registry/demos/dropdown-menu.d.ts +7 -0
  131. package/registry/demos/dropdown-menu.js +36 -0
  132. package/registry/demos/dropdown-menu.tsx +27 -0
  133. package/registry/demos/empty-state.d.ts +5 -0
  134. package/registry/demos/empty-state.js +15 -0
  135. package/registry/demos/empty-state.tsx +12 -0
  136. package/registry/demos/field.d.ts +5 -0
  137. package/registry/demos/field.js +15 -0
  138. package/registry/demos/field.tsx +11 -0
  139. package/registry/demos/kbd.d.ts +5 -0
  140. package/registry/demos/kbd.js +11 -0
  141. package/registry/demos/kbd.tsx +5 -0
  142. package/registry/demos/page-header.d.ts +5 -0
  143. package/registry/demos/page-header.js +11 -0
  144. package/registry/demos/page-header.tsx +5 -0
  145. package/registry/demos/popover.d.ts +7 -0
  146. package/registry/demos/popover.js +25 -0
  147. package/registry/demos/popover.tsx +31 -0
  148. package/registry/demos/progress.d.ts +5 -0
  149. package/registry/demos/progress.js +11 -0
  150. package/registry/demos/progress.tsx +5 -0
  151. package/registry/demos/radio-group.d.ts +5 -0
  152. package/registry/demos/radio-group.js +16 -0
  153. package/registry/demos/radio-group.tsx +14 -0
  154. package/registry/demos/separator.d.ts +5 -0
  155. package/registry/demos/separator.js +15 -0
  156. package/registry/demos/separator.tsx +11 -0
  157. package/registry/demos/skeleton.d.ts +5 -0
  158. package/registry/demos/skeleton.js +20 -0
  159. package/registry/demos/skeleton.tsx +16 -0
  160. package/registry/demos/slider.d.ts +5 -0
  161. package/registry/demos/slider.js +11 -0
  162. package/registry/demos/slider.tsx +5 -0
  163. package/registry/demos/slot.d.ts +5 -0
  164. package/registry/demos/slot.js +14 -0
  165. package/registry/demos/slot.tsx +13 -0
  166. package/registry/demos/spinner.d.ts +5 -0
  167. package/registry/demos/spinner.js +11 -0
  168. package/registry/demos/spinner.tsx +5 -0
  169. package/registry/demos/stat.d.ts +5 -0
  170. package/registry/demos/stat.js +11 -0
  171. package/registry/demos/stat.tsx +5 -0
  172. package/registry/demos/switch.d.ts +5 -0
  173. package/registry/demos/switch.js +11 -0
  174. package/registry/demos/switch.tsx +5 -0
  175. package/registry/demos/tabs.d.ts +5 -0
  176. package/registry/demos/tabs.js +22 -0
  177. package/registry/demos/tabs.tsx +28 -0
  178. package/registry/demos/theme.d.ts +5 -0
  179. package/registry/demos/theme.js +11 -0
  180. package/registry/demos/theme.tsx +9 -0
  181. package/registry/demos/toast.d.ts +8 -0
  182. package/registry/demos/toast.js +29 -0
  183. package/registry/demos/toast.tsx +31 -0
  184. package/registry/demos/tooltip.d.ts +7 -0
  185. package/registry/demos/tooltip.js +15 -0
  186. package/registry/demos/tooltip.tsx +15 -0
  187. package/registry/dialog.json +218 -0
  188. package/registry/dropdown-menu.json +63 -0
  189. package/registry/empty-state.json +59 -0
  190. package/registry/field.json +66 -0
  191. package/registry/index.json +320 -0
  192. package/registry/kbd.json +38 -0
  193. package/registry/page-header.json +66 -0
  194. package/registry/popover.json +44 -0
  195. package/registry/progress.json +80 -0
  196. package/registry/radio-group.json +64 -0
  197. package/registry/separator.json +25 -0
  198. package/registry/skeleton.json +38 -0
  199. package/registry/slider.json +95 -0
  200. package/registry/slot.json +31 -0
  201. package/registry/spinner.json +45 -0
  202. package/registry/stat.json +66 -0
  203. package/registry/switch.json +53 -0
  204. package/registry/tabs.json +44 -0
  205. package/registry/theme.json +78 -0
  206. package/registry/toast.json +54 -0
  207. package/registry/tooltip.json +91 -0
  208. package/dist/chunk-CPMZS4XA.js.map +0 -1
  209. package/dist/chunk-QRG5RYEI.js.map +0 -1
  210. package/dist/chunk-RLOXWF2G.js.map +0 -1
package/dist/theme.d.ts CHANGED
@@ -1,70 +1,14 @@
1
1
  import * as React from 'react';
2
- import * as tailwind_variants from 'tailwind-variants';
3
- import { VariantProps } from 'tailwind-variants';
4
-
5
- declare const buttonVariants: tailwind_variants.TVReturnType<{
6
- variant: {
7
- primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
8
- secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
9
- outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
10
- ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
11
- danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
12
- };
13
- size: {
14
- sm: "h-8 px-3 text-xs";
15
- md: "h-10 px-4 text-sm";
16
- lg: "h-12 px-6 text-sm";
17
- icon: "h-10 w-10";
18
- };
19
- shape: {
20
- rounded: "rounded-lg";
21
- pill: "rounded-full";
22
- };
23
- }, undefined, string, {
24
- variant: {
25
- primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
26
- secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
27
- outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
28
- ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
29
- danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
30
- };
31
- size: {
32
- sm: "h-8 px-3 text-xs";
33
- md: "h-10 px-4 text-sm";
34
- lg: "h-12 px-6 text-sm";
35
- icon: "h-10 w-10";
36
- };
37
- shape: {
38
- rounded: "rounded-lg";
39
- pill: "rounded-full";
40
- };
41
- }, undefined, tailwind_variants.TVReturnTypeLike<{
42
- variant: {
43
- primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
44
- secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
45
- outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
46
- ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
47
- danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
48
- };
49
- size: {
50
- sm: "h-8 px-3 text-xs";
51
- md: "h-10 px-4 text-sm";
52
- lg: "h-12 px-6 text-sm";
53
- icon: "h-10 w-10";
54
- };
55
- shape: {
56
- rounded: "rounded-lg";
57
- pill: "rounded-full";
58
- };
59
- }, undefined>>;
60
- type ButtonVariants = VariantProps<typeof buttonVariants>;
2
+ import { a as ButtonVariants } from './button-8La7l0V4.js';
3
+ import 'tailwind-variants';
61
4
 
62
5
  type Mode = "dark" | "light" | "system";
63
6
  /**
64
7
  * In <head>, before anything renders: applies the persisted mode and named theme, so the page never flashes.
65
8
  * `nonce` for a strict Content-Security-Policy.
66
9
  */
67
- declare function ThemeScript({ nonce }: {
10
+ declare function ThemeScript({ nonce, }: {
11
+ /** The nonce of a strict Content-Security-Policy (`script-src 'nonce-…'`). */
68
12
  nonce?: string;
69
13
  }): React.JSX.Element;
70
14
  type ThemeContextValue = {
@@ -83,6 +27,13 @@ type ThemeProviderProps = {
83
27
  declare function ThemeProvider({ children, defaultMode }: ThemeProviderProps): React.JSX.Element;
84
28
  declare function useTheme(): ThemeContextValue;
85
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"];
86
37
  /** The accessible name — a string, or one per current mode (e.g. `(mode) => t("theme." + mode)`). */
87
38
  label: string | ((mode: Mode) => string);
88
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/toast.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-RLOXWF2G.js";
4
+ } from "./chunk-5AXLA4LK.js";
5
5
 
6
6
  // src/toast/toast.tsx
7
7
  import { Toaster as Sonner } from "sonner";
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;
package/dist/tooltip.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-J3NC6ER4.js";
5
5
  import {
6
6
  cn
7
- } from "./chunk-RLOXWF2G.js";
7
+ } from "./chunk-5AXLA4LK.js";
8
8
 
9
9
  // src/tooltip/tooltip.tsx
10
10
  import { Tooltip as TooltipPrimitive } from "radix-ui";
@@ -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,7 +1,7 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "2.0.0-beta.1",
4
- "description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia) and the motion signature — web and /native.",
3
+ "version": "2.0.0-beta.3",
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",
7
7
  "orazaka",
@@ -16,7 +16,9 @@
16
16
  "tailwindcss",
17
17
  "components",
18
18
  "dialog",
19
- "toast"
19
+ "toast",
20
+ "tabs",
21
+ "command-palette"
20
22
  ],
21
23
  "homepage": "https://github.com/krizaka/krizaka-ui#readme",
22
24
  "bugs": "https://github.com/krizaka/krizaka-ui/issues",
@@ -124,16 +126,62 @@
124
126
  "types": "./dist/kbd.d.ts",
125
127
  "import": "./dist/kbd.js"
126
128
  },
129
+ "./tabs": {
130
+ "types": "./dist/tabs.d.ts",
131
+ "import": "./dist/tabs.js"
132
+ },
133
+ "./chip": {
134
+ "types": "./dist/chip.d.ts",
135
+ "import": "./dist/chip.js"
136
+ },
137
+ "./switch": {
138
+ "types": "./dist/switch.d.ts",
139
+ "import": "./dist/switch.js"
140
+ },
141
+ "./slider": {
142
+ "types": "./dist/slider.d.ts",
143
+ "import": "./dist/slider.js"
144
+ },
145
+ "./checkbox": {
146
+ "types": "./dist/checkbox.d.ts",
147
+ "import": "./dist/checkbox.js"
148
+ },
149
+ "./radio-group": {
150
+ "types": "./dist/radio-group.d.ts",
151
+ "import": "./dist/radio-group.js"
152
+ },
153
+ "./command": {
154
+ "types": "./dist/command.d.ts",
155
+ "import": "./dist/command.js"
156
+ },
157
+ "./confirm-button": {
158
+ "types": "./dist/confirm-button.d.ts",
159
+ "import": "./dist/confirm-button.js"
160
+ },
161
+ "./progress": {
162
+ "types": "./dist/progress.d.ts",
163
+ "import": "./dist/progress.js"
164
+ },
127
165
  "./motion.css": "./dist/motion.css",
128
166
  "./tailwind.css": "./tailwind.css",
129
167
  "./native": {
130
168
  "types": "./dist/native.d.ts",
131
169
  "import": "./dist/native.js"
132
170
  },
171
+ "./registry/demos/*": {
172
+ "types": "./registry/demos/*.d.ts",
173
+ "import": "./registry/demos/*.js"
174
+ },
175
+ "./registry/*": "./registry/*.json",
133
176
  "./package.json": "./package.json"
134
177
  },
135
178
  "files": [
136
179
  "dist",
180
+ "registry/*.json",
181
+ "registry/demos/*.tsx",
182
+ "registry/demos/*.js",
183
+ "registry/demos/*.d.ts",
184
+ "!registry/demos/*.test.*",
137
185
  "tailwind.css",
138
186
  "README.md",
139
187
  "LICENSE"
@@ -142,7 +190,7 @@
142
190
  "*.css"
143
191
  ],
144
192
  "peerDependencies": {
145
- "@krizaka/tailwind": ">=2.0.0-beta.1",
193
+ "@krizaka/tailwind": ">=2.0.0-beta.3",
146
194
  "react": ">=18",
147
195
  "react-dom": ">=18",
148
196
  "react-native": ">=0.76",
@@ -164,6 +212,7 @@
164
212
  "happy-dom": "^20.0.0",
165
213
  "publint": "^0.3.25",
166
214
  "react": "^19.2.0",
215
+ "react-docgen-typescript": "^2.4.0",
167
216
  "react-dom": "^19.2.0",
168
217
  "react-native": "^0.86.3",
169
218
  "react-native-svg": "^15.15.4",
@@ -426,20 +475,111 @@
426
475
  "tailwind-variants",
427
476
  "clsx"
428
477
  ]
478
+ },
479
+ {
480
+ "name": "./tabs",
481
+ "path": "dist/tabs.js",
482
+ "limit": "10 kB",
483
+ "gzip": true,
484
+ "ignore": [
485
+ "tailwind-variants",
486
+ "clsx"
487
+ ]
488
+ },
489
+ {
490
+ "name": "./chip",
491
+ "path": "dist/chip.js",
492
+ "limit": "10 kB",
493
+ "gzip": true,
494
+ "ignore": [
495
+ "tailwind-variants",
496
+ "clsx"
497
+ ]
498
+ },
499
+ {
500
+ "name": "./switch",
501
+ "path": "dist/switch.js",
502
+ "limit": "6 kB",
503
+ "gzip": true,
504
+ "ignore": [
505
+ "tailwind-variants",
506
+ "clsx"
507
+ ]
508
+ },
509
+ {
510
+ "name": "./slider",
511
+ "path": "dist/slider.js",
512
+ "limit": "12 kB",
513
+ "gzip": true,
514
+ "ignore": [
515
+ "tailwind-variants",
516
+ "clsx"
517
+ ]
518
+ },
519
+ {
520
+ "name": "./checkbox",
521
+ "path": "dist/checkbox.js",
522
+ "limit": "6 kB",
523
+ "gzip": true,
524
+ "ignore": [
525
+ "tailwind-variants",
526
+ "clsx"
527
+ ]
528
+ },
529
+ {
530
+ "name": "./radio-group",
531
+ "path": "dist/radio-group.js",
532
+ "limit": "11 kB",
533
+ "gzip": true,
534
+ "ignore": [
535
+ "tailwind-variants",
536
+ "clsx"
537
+ ]
538
+ },
539
+ {
540
+ "name": "./command",
541
+ "path": "dist/command.js",
542
+ "limit": "21 kB",
543
+ "gzip": true,
544
+ "ignore": [
545
+ "tailwind-variants",
546
+ "clsx"
547
+ ]
548
+ },
549
+ {
550
+ "name": "./confirm-button",
551
+ "path": "dist/confirm-button.js",
552
+ "limit": "3 kB",
553
+ "gzip": true,
554
+ "ignore": [
555
+ "tailwind-variants",
556
+ "clsx"
557
+ ]
558
+ },
559
+ {
560
+ "name": "./progress",
561
+ "path": "dist/progress.js",
562
+ "limit": "1.2 kB",
563
+ "gzip": true,
564
+ "ignore": [
565
+ "tailwind-variants",
566
+ "clsx"
567
+ ]
429
568
  }
430
569
  ],
431
570
  "dependencies": {
432
571
  "clsx": "^2.1.1",
572
+ "cmdk": "^1.1.1",
433
573
  "radix-ui": "^1.7.0",
434
574
  "sonner": "^2.0.8",
435
575
  "tailwind-variants": "^3.3.1"
436
576
  },
437
577
  "scripts": {
438
- "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",
439
579
  "lint": "eslint . --max-warnings 0 && tsc --noEmit --noUnusedParameters --noFallthroughCasesInSwitch --noImplicitReturns",
440
580
  "typecheck": "tsc --noEmit",
441
581
  "test": "vitest run",
442
582
  "size": "size-limit",
443
- "publint": "publint && attw --pack . --profile esm-only --exclude-entrypoints ./motion.css ./tailwind.css"
583
+ "publint": "publint && attw --pack . --profile esm-only --exclude-entrypoints ./motion.css ./tailwind.css && node scripts/consumer.mjs"
444
584
  }
445
585
  }
@@ -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
+ }