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

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 (187) hide show
  1. package/README.md +98 -6
  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/native.d.ts +471 -3
  37. package/dist/native.js +1005 -36
  38. package/dist/native.js.map +1 -1
  39. package/dist/page-header.d.ts +2 -0
  40. package/dist/page-header.js.map +1 -1
  41. package/dist/progress.d.ts +5 -0
  42. package/dist/progress.js.map +1 -1
  43. package/dist/skeleton.d.ts +7 -1
  44. package/dist/skeleton.js.map +1 -1
  45. package/dist/slider.d.ts +2 -0
  46. package/dist/slider.js.map +1 -1
  47. package/dist/spinner.d.ts +5 -0
  48. package/dist/spinner.js.map +1 -1
  49. package/dist/stat.d.ts +2 -0
  50. package/dist/stat.js.map +1 -1
  51. package/dist/switch.d.ts +7 -3
  52. package/dist/switch.js.map +1 -1
  53. package/dist/tabs.d.ts +4 -1
  54. package/dist/tabs.js.map +1 -1
  55. package/dist/theme.d.ts +10 -2
  56. package/dist/theme.js +4 -2
  57. package/dist/theme.js.map +1 -1
  58. package/dist/tooltip.d.ts +3 -0
  59. package/dist/tooltip.js.map +1 -1
  60. package/package.json +31 -8
  61. package/registry/alert.json +59 -0
  62. package/registry/avatar.json +114 -0
  63. package/registry/badge.json +59 -0
  64. package/registry/button.json +115 -0
  65. package/registry/card.json +154 -0
  66. package/registry/checkbox.json +46 -0
  67. package/registry/chip.json +164 -0
  68. package/registry/cn.json +21 -0
  69. package/registry/command.json +162 -0
  70. package/registry/confirm-button.json +104 -0
  71. package/registry/countdown.json +84 -0
  72. package/registry/demos/alert.d.ts +5 -0
  73. package/registry/demos/alert.js +12 -0
  74. package/registry/demos/alert.tsx +15 -0
  75. package/registry/demos/avatar.d.ts +5 -0
  76. package/registry/demos/avatar.js +14 -0
  77. package/registry/demos/avatar.tsx +13 -0
  78. package/registry/demos/badge.d.ts +5 -0
  79. package/registry/demos/badge.js +11 -0
  80. package/registry/demos/badge.tsx +5 -0
  81. package/registry/demos/button.d.ts +5 -0
  82. package/registry/demos/button.js +11 -0
  83. package/registry/demos/button.tsx +5 -0
  84. package/registry/demos/card.d.ts +5 -0
  85. package/registry/demos/card.js +30 -0
  86. package/registry/demos/card.tsx +37 -0
  87. package/registry/demos/checkbox.d.ts +5 -0
  88. package/registry/demos/checkbox.js +11 -0
  89. package/registry/demos/checkbox.tsx +5 -0
  90. package/registry/demos/chip.d.ts +5 -0
  91. package/registry/demos/chip.js +15 -0
  92. package/registry/demos/chip.tsx +11 -0
  93. package/registry/demos/cn.d.ts +7 -0
  94. package/registry/demos/cn.js +17 -0
  95. package/registry/demos/cn.tsx +9 -0
  96. package/registry/demos/command.d.ts +5 -0
  97. package/registry/demos/command.js +32 -0
  98. package/registry/demos/command.tsx +28 -0
  99. package/registry/demos/confirm-button.d.ts +5 -0
  100. package/registry/demos/confirm-button.js +12 -0
  101. package/registry/demos/confirm-button.tsx +13 -0
  102. package/registry/demos/countdown.d.ts +5 -0
  103. package/registry/demos/countdown.js +13 -0
  104. package/registry/demos/countdown.tsx +10 -0
  105. package/registry/demos/dialog.d.ts +7 -0
  106. package/registry/demos/dialog.js +29 -0
  107. package/registry/demos/dialog.tsx +31 -0
  108. package/registry/demos/dropdown-menu.d.ts +7 -0
  109. package/registry/demos/dropdown-menu.js +36 -0
  110. package/registry/demos/dropdown-menu.tsx +27 -0
  111. package/registry/demos/empty-state.d.ts +5 -0
  112. package/registry/demos/empty-state.js +15 -0
  113. package/registry/demos/empty-state.tsx +12 -0
  114. package/registry/demos/field.d.ts +5 -0
  115. package/registry/demos/field.js +15 -0
  116. package/registry/demos/field.tsx +11 -0
  117. package/registry/demos/kbd.d.ts +5 -0
  118. package/registry/demos/kbd.js +11 -0
  119. package/registry/demos/kbd.tsx +5 -0
  120. package/registry/demos/page-header.d.ts +5 -0
  121. package/registry/demos/page-header.js +11 -0
  122. package/registry/demos/page-header.tsx +5 -0
  123. package/registry/demos/popover.d.ts +7 -0
  124. package/registry/demos/popover.js +25 -0
  125. package/registry/demos/popover.tsx +31 -0
  126. package/registry/demos/progress.d.ts +5 -0
  127. package/registry/demos/progress.js +11 -0
  128. package/registry/demos/progress.tsx +5 -0
  129. package/registry/demos/radio-group.d.ts +5 -0
  130. package/registry/demos/radio-group.js +16 -0
  131. package/registry/demos/radio-group.tsx +14 -0
  132. package/registry/demos/separator.d.ts +5 -0
  133. package/registry/demos/separator.js +15 -0
  134. package/registry/demos/separator.tsx +11 -0
  135. package/registry/demos/skeleton.d.ts +5 -0
  136. package/registry/demos/skeleton.js +20 -0
  137. package/registry/demos/skeleton.tsx +16 -0
  138. package/registry/demos/slider.d.ts +5 -0
  139. package/registry/demos/slider.js +11 -0
  140. package/registry/demos/slider.tsx +5 -0
  141. package/registry/demos/slot.d.ts +5 -0
  142. package/registry/demos/slot.js +14 -0
  143. package/registry/demos/slot.tsx +13 -0
  144. package/registry/demos/spinner.d.ts +5 -0
  145. package/registry/demos/spinner.js +11 -0
  146. package/registry/demos/spinner.tsx +5 -0
  147. package/registry/demos/stat.d.ts +5 -0
  148. package/registry/demos/stat.js +11 -0
  149. package/registry/demos/stat.tsx +5 -0
  150. package/registry/demos/switch.d.ts +5 -0
  151. package/registry/demos/switch.js +11 -0
  152. package/registry/demos/switch.tsx +5 -0
  153. package/registry/demos/tabs.d.ts +5 -0
  154. package/registry/demos/tabs.js +22 -0
  155. package/registry/demos/tabs.tsx +28 -0
  156. package/registry/demos/theme.d.ts +5 -0
  157. package/registry/demos/theme.js +11 -0
  158. package/registry/demos/theme.tsx +9 -0
  159. package/registry/demos/toast.d.ts +8 -0
  160. package/registry/demos/toast.js +29 -0
  161. package/registry/demos/toast.tsx +31 -0
  162. package/registry/demos/tooltip.d.ts +7 -0
  163. package/registry/demos/tooltip.js +15 -0
  164. package/registry/demos/tooltip.tsx +15 -0
  165. package/registry/dialog.json +218 -0
  166. package/registry/dropdown-menu.json +63 -0
  167. package/registry/empty-state.json +59 -0
  168. package/registry/field.json +66 -0
  169. package/registry/index.json +320 -0
  170. package/registry/kbd.json +38 -0
  171. package/registry/page-header.json +66 -0
  172. package/registry/popover.json +44 -0
  173. package/registry/progress.json +80 -0
  174. package/registry/radio-group.json +64 -0
  175. package/registry/separator.json +25 -0
  176. package/registry/skeleton.json +38 -0
  177. package/registry/slider.json +95 -0
  178. package/registry/slot.json +31 -0
  179. package/registry/spinner.json +45 -0
  180. package/registry/stat.json +66 -0
  181. package/registry/switch.json +53 -0
  182. package/registry/tabs.json +44 -0
  183. package/registry/theme.json +78 -0
  184. package/registry/toast.json +54 -0
  185. package/registry/tooltip.json +91 -0
  186. package/dist/chunk-5FRYKFTO.js.map +0 -1
  187. package/dist/chunk-QRG5RYEI.js.map +0 -1
@@ -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
+ }
@@ -0,0 +1,154 @@
1
+ {
2
+ "name": "card",
3
+ "type": "primitive",
4
+ "description": "`Card.*`, `card` (the slots). Server-safe.",
5
+ "files": [
6
+ {
7
+ "path": "card/card.tsx",
8
+ "content": "// Server-safe: no hook, no context. One `tv` definition with slots: each part takes its slot, and a product overrides\n// any part through `className` (merged last by tailwind-merge).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\n\nexport const card = tv({\n slots: {\n root:\n \"group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n media: \"relative overflow-hidden bg-media\",\n image: \"h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105\",\n fallback: \"flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60\",\n overlay: \"absolute z-10 flex items-center gap-1.5\",\n body: \"relative flex flex-1 flex-col gap-3\",\n title: \"line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-accent\",\n description: \"line-clamp-2 text-xs text-fg-secondary\",\n stat: \"min-w-0\",\n statLabel: \"text-[10px] font-semibold uppercase tracking-wider text-fg-secondary\",\n statValue: \"font-display text-xl font-black tabular-nums text-fg\",\n footer: \"relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary\",\n },\n variants: {\n radius: { md: { root: \"rounded-md\" }, lg: { root: \"rounded-lg\" }, xl: { root: \"rounded-xl\" } },\n tone: { default: {}, elevated: { root: \"bg-surface-2 shadow-md\" }, glass: { root: \"bg-surface-1/70 backdrop-blur-md\" } },\n interactive: { true: { root: \"kz-spotlight kz-lift hover:border-accent/50\" } },\n padding: { none: { body: \"p-0\" }, sm: { body: \"p-3\" }, md: { body: \"p-4\" }, lg: { body: \"p-6\" } },\n },\n defaultVariants: { radius: \"xl\", tone: \"default\", padding: \"md\" },\n});\n\ntype CardVariants = VariantProps<typeof card>;\n/** The parts without a variant read the default slots: no context, so server-safe. */\nconst slots = card();\n\nexport type CardRootProps = React.ComponentProps<\"div\"> &\n Pick<CardVariants, \"radius\" | \"tone\" | \"interactive\"> & {\n /**\n * md · lg · xl.\n * @default \"xl\"\n */\n radius?: CardVariants[\"radius\"];\n /**\n * default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).\n * @default \"default\"\n */\n tone?: CardVariants[\"tone\"];\n /** The spotlight that follows the pointer and the lift on hover: for a card that is a link. */\n interactive?: CardVariants[\"interactive\"];\n /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */\n reveal?: number;\n };\n\nexport function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n const revealStyle =\n reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <Comp\n data-reveal={reveal === undefined ? undefined : \"\"}\n data-interactive={interactive ? \"\" : undefined}\n style={revealStyle}\n className={card({ radius, tone, interactive }).root({ className })}\n {...props}\n />\n );\n}\n\nconst ASPECT = { video: \"aspect-video\", square: \"aspect-square\", portrait: \"aspect-[3/4]\", auto: \"\" } as const;\nexport type CardMediaProps = React.ComponentProps<\"div\"> & {\n /** The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's). */\n aspect?: keyof typeof ASPECT;\n};\n\nexport function CardMedia({ aspect = \"video\", className, ...props }: CardMediaProps) {\n return <div data-aspect={aspect} className={slots.media({ className: cn(ASPECT[aspect], className) })} {...props} />;\n}\n\nexport type CardImageProps = Omit<React.ComponentProps<\"img\">, \"src\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** Shown when there is no image: an icon, an illustration (decorative). */\n fallback?: React.ReactNode;\n};\n\n/** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */\nexport function CardImage({ src, alt = \"\", fallback, className, ...props }: CardImageProps) {\n if (!src) {\n return (\n <div data-fallback=\"\" aria-hidden className={slots.fallback({ className })}>\n {fallback}\n </div>\n );\n }\n return <img src={src} alt={alt} loading=\"lazy\" decoding=\"async\" className={slots.image({ className })} {...props} />;\n}\n\nconst CORNER = {\n \"top-left\": \"left-2.5 top-2.5\",\n \"top-right\": \"right-2.5 top-2.5\",\n \"bottom-left\": \"bottom-2.5 left-2.5\",\n \"bottom-right\": \"bottom-2.5 right-2.5\",\n} as const;\nexport type CardOverlayProps = React.ComponentProps<\"div\"> & {\n /** The corner of the media it sits in. */\n corner?: keyof typeof CORNER;\n};\n\n/** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */\nexport function CardOverlay({ corner = \"top-left\", className, ...props }: CardOverlayProps) {\n return <div data-corner={corner} className={slots.overlay({ className: cn(CORNER[corner], className) })} {...props} />;\n}\n\nexport type CardBodyProps = React.ComponentProps<\"div\"> & {\n /**\n * none · sm · md · lg.\n * @default \"md\"\n */\n padding?: CardVariants[\"padding\"];\n};\n\nexport function CardBody({ padding, className, ...props }: CardBodyProps) {\n return <div className={card({ padding }).body({ className })} {...props} />;\n}\n\nexport type CardTitleProps = React.ComponentProps<\"h3\"> & {\n /** The heading level, to fit the page's outline. */\n as?: \"h2\" | \"h3\" | \"h4\";\n};\n\nexport function CardTitle({ as: Tag = \"h3\", className, ...props }: CardTitleProps) {\n return <Tag className={slots.title({ className })} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return <p className={slots.description({ className })} {...props} />;\n}\n\nexport type CardStatProps = React.ComponentProps<\"div\"> & {\n /** What the figure is, in small capitals above it (the figure is the children). */\n label: React.ReactNode;\n};\n\n/** A figure put forward: the label in capitals, the value in tabular digits. */\nexport function CardStat({ label, className, children, ...props }: CardStatProps) {\n return (\n <div className={slots.stat({ className })} {...props}>\n <p className={slots.statLabel()}>{label}</p>\n <p className={slots.statValue()}>{children}</p>\n </div>\n );\n}\n\nexport function CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={slots.footer({ className })} {...props} />;\n}\n\n/** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */\nexport const Card = {\n Root: CardRoot,\n Media: CardMedia,\n Image: CardImage,\n Overlay: CardOverlay,\n Body: CardBody,\n Title: CardTitle,\n Description: CardDescription,\n Stat: CardStat,\n Footer: CardFooter,\n};\n"
9
+ },
10
+ {
11
+ "path": "card/index.ts",
12
+ "content": "/** @krizaka/ui/card — `Card.*`, `card` (the slots). Server-safe. */\nexport {\n Card,\n card,\n CardBody,\n type CardBodyProps,\n CardDescription,\n CardFooter,\n CardImage,\n type CardImageProps,\n CardMedia,\n type CardMediaProps,\n CardOverlay,\n type CardOverlayProps,\n CardRoot,\n type CardRootProps,\n CardStat,\n type CardStatProps,\n CardTitle,\n type CardTitleProps,\n} from \"./card\";\n"
13
+ }
14
+ ],
15
+ "dependencies": [
16
+ "radix-ui@^1.7.0",
17
+ "tailwind-variants@^3.3.1"
18
+ ],
19
+ "registryDependencies": [
20
+ "cn"
21
+ ],
22
+ "demo": {
23
+ "path": "demos/card.tsx",
24
+ "content": "import { Badge } from \"@krizaka/ui/badge\";\nimport { Card } from \"@krizaka/ui/card\";\n\n// A self-contained cover (no network).\nconst COVER =\n \"data:image/svg+xml;utf8,\" +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 160 90\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">' +\n '<stop offset=\"0\" stop-color=\"#1e3a8a\"/><stop offset=\"1\" stop-color=\"#7c3aed\"/></linearGradient></defs>' +\n '<rect width=\"160\" height=\"90\" fill=\"url(#g)\"/><circle cx=\"118\" cy=\"30\" r=\"14\" fill=\"#fde68a\"/>' +\n '<path d=\"M0 90 50 44l30 28 22-18 58 36z\" fill=\"#0f172a\" opacity=\".55\"/></svg>',\n );\n\nexport default function CardDemo() {\n return (\n <Card.Root>\n <Card.Media>\n <Card.Image src={COVER} />\n <Card.Overlay>\n <Badge tone=\"scrim\">4K</Badge>\n </Card.Overlay>\n <Card.Overlay corner=\"bottom-right\">\n <Badge tone=\"scrim\">12:04</Badge>\n </Card.Overlay>\n </Card.Media>\n <Card.Body>\n <Card.Title>Night ride across the city</Card.Title>\n <Card.Description>Ten minutes of neon, rain and empty avenues, filmed in one take.</Card.Description>\n <Card.Footer>\n <span>1.2k views</span>\n <span aria-hidden>·</span>\n <span>2 days ago</span>\n </Card.Footer>\n </Card.Body>\n </Card.Root>\n );\n}\n"
25
+ },
26
+ "props": [
27
+ {
28
+ "component": "CardRoot",
29
+ "description": "",
30
+ "props": [
31
+ {
32
+ "name": "asChild",
33
+ "type": "boolean",
34
+ "default": null,
35
+ "description": "Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged.",
36
+ "required": false
37
+ },
38
+ {
39
+ "name": "interactive",
40
+ "type": "boolean",
41
+ "default": null,
42
+ "description": "The spotlight that follows the pointer and the lift on hover: for a card that is a link.",
43
+ "required": false
44
+ },
45
+ {
46
+ "name": "radius",
47
+ "type": "\"md\" | \"lg\" | \"xl\"",
48
+ "default": "xl",
49
+ "description": "md · lg · xl.",
50
+ "required": false
51
+ },
52
+ {
53
+ "name": "reveal",
54
+ "type": "number",
55
+ "default": null,
56
+ "description": "Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most).",
57
+ "required": false
58
+ },
59
+ {
60
+ "name": "tone",
61
+ "type": "\"default\" | \"elevated\" | \"glass\"",
62
+ "default": "default",
63
+ "description": "default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).",
64
+ "required": false
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ "component": "CardMedia",
70
+ "description": "",
71
+ "props": [
72
+ {
73
+ "name": "aspect",
74
+ "type": "\"video\" | \"auto\" | \"square\" | \"portrait\"",
75
+ "default": "video",
76
+ "description": "The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's).",
77
+ "required": false
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "component": "CardImage",
83
+ "description": "The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card.",
84
+ "props": [
85
+ {
86
+ "name": "fallback",
87
+ "type": "ReactNode",
88
+ "default": null,
89
+ "description": "Shown when there is no image: an icon, an illustration (decorative).",
90
+ "required": false
91
+ },
92
+ {
93
+ "name": "src",
94
+ "type": "string | null",
95
+ "default": null,
96
+ "description": "The image's URL; `null` or nothing shows the `fallback`.",
97
+ "required": false
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "component": "CardOverlay",
103
+ "description": "What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light.",
104
+ "props": [
105
+ {
106
+ "name": "corner",
107
+ "type": "\"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\"",
108
+ "default": "top-left",
109
+ "description": "The corner of the media it sits in.",
110
+ "required": false
111
+ }
112
+ ]
113
+ },
114
+ {
115
+ "component": "CardBody",
116
+ "description": "",
117
+ "props": [
118
+ {
119
+ "name": "padding",
120
+ "type": "\"none\" | \"sm\" | \"md\" | \"lg\"",
121
+ "default": "md",
122
+ "description": "none · sm · md · lg.",
123
+ "required": false
124
+ }
125
+ ]
126
+ },
127
+ {
128
+ "component": "CardTitle",
129
+ "description": "",
130
+ "props": [
131
+ {
132
+ "name": "as",
133
+ "type": "\"h2\" | \"h3\" | \"h4\"",
134
+ "default": "h3",
135
+ "description": "The heading level, to fit the page's outline.",
136
+ "required": false
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ "component": "CardStat",
142
+ "description": "A figure put forward: the label in capitals, the value in tabular digits.",
143
+ "props": [
144
+ {
145
+ "name": "label",
146
+ "type": "ReactNode",
147
+ "default": null,
148
+ "description": "What the figure is, in small capitals above it (the figure is the children).",
149
+ "required": true
150
+ }
151
+ ]
152
+ }
153
+ ]
154
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "checkbox",
3
+ "type": "primitive",
4
+ "description": "`Checkbox` (checked · unchecked · indeterminate), `checkbox` (the slots). Client (\"use client\").",
5
+ "files": [
6
+ {
7
+ "path": "checkbox/checkbox.tsx",
8
+ "content": "// Client. Radix Checkbox: role=\"checkbox\", aria-checked (true · false · mixed), Space toggles, a hidden input inside a\n// form. It sits in a `Field` like the other controls: `<Field.Label htmlFor={id}>`, `invalid`, aria-describedby.\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const checkbox = tv({\n slots: {\n root:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\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 \"hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=indeterminate]:border-accent data-[state=indeterminate]:bg-accent \" +\n \"aria-invalid:border-danger aria-invalid:ring-danger/30\",\n indicator: \"flex items-center justify-center\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-60\",\n },\n});\n\nfunction Check() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\nfunction Dash() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M5 12h14\" />\n </svg>\n );\n}\n\nexport type CheckboxProps = Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, \"children\"> & {\n /** Marks it invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby). */\n invalid?: boolean;\n /** The text next to the box, clickable: the box and its words in one `<label>`. Otherwise name it with\n * `Field.Label htmlFor` or `aria-label`. */\n children?: React.ReactNode;\n};\n\n/** A checkbox: `checked`/`defaultChecked` (`true` · `false` · `\"indeterminate\"`) + `onCheckedChange`. */\nexport function Checkbox({ invalid, className, children, ...props }: CheckboxProps) {\n const s = checkbox();\n const box = (\n <CheckboxPrimitive.Root\n aria-invalid={invalid || undefined}\n data-invalid={invalid ? \"\" : undefined}\n className={s.root({ className: children ? \"mt-0.5\" : className })}\n {...props}\n >\n <CheckboxPrimitive.Indicator className={s.indicator()}>\n {props.checked === \"indeterminate\" ? <Dash /> : <Check />}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n if (!children) return box;\n return (\n <label className={s.label({ className })}>\n {box}\n <span className=\"min-w-0\">{children}</span>\n </label>\n );\n}\n"
9
+ },
10
+ {
11
+ "path": "checkbox/index.ts",
12
+ "content": "/** @krizaka/ui/checkbox — `Checkbox` (checked · unchecked · indeterminate), `checkbox` (the slots). Client (\"use client\"). */\nexport { Checkbox, checkbox, type CheckboxProps } from \"./checkbox\";\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/checkbox.tsx",
22
+ "content": "import { Checkbox } from \"@krizaka/ui/checkbox\";\n\nexport default function CheckboxDemo() {\n return <Checkbox>I accept the terms of use</Checkbox>;\n}\n"
23
+ },
24
+ "props": [
25
+ {
26
+ "component": "Checkbox",
27
+ "description": "A checkbox: `checked`/`defaultChecked` (`true` · `false` · `\"indeterminate\"`) + `onCheckedChange`.",
28
+ "props": [
29
+ {
30
+ "name": "children",
31
+ "type": "ReactNode",
32
+ "default": null,
33
+ "description": "The text next to the box, clickable: the box and its words in one `<label>`. Otherwise name it with\n`Field.Label htmlFor` or `aria-label`.",
34
+ "required": false
35
+ },
36
+ {
37
+ "name": "invalid",
38
+ "type": "boolean",
39
+ "default": null,
40
+ "description": "Marks it invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby).",
41
+ "required": false
42
+ }
43
+ ]
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,164 @@
1
+ {
2
+ "name": "chip",
3
+ "type": "primitive",
4
+ "description": "`Chip` (selectable, removable), `Chip.Group` (single · multiple), `chip` (the slots). Client (\"use client\").",
5
+ "files": [
6
+ {
7
+ "path": "chip/chip.tsx",
8
+ "content": "// Client (a context). A chip is a pressable pill: alone it is a Radix Toggle (aria-pressed), inside `Chip.Group` an\n// item of a Radix Toggle Group (roving focus with the arrows; `single` = radio semantics, `multiple` = pressed\n// buttons). A `removable` chip is a static label with its own remove button (two buttons cannot nest).\nimport { Toggle as TogglePrimitive, ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const chip = tv({\n slots: {\n group: \"flex flex-wrap items-center gap-2 data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch\",\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border 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 \"border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=on]:border-accent data-[state=on]:bg-accent-soft data-[state=on]:text-fg\",\n remove:\n \"-mr-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n size: { sm: { base: \"h-7 px-3 text-xs\" }, md: { base: \"h-9 px-3.5 text-xs\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type ChipVariants = VariantProps<typeof chip>;\n\ntype GroupContext = { size: ChipVariants[\"size\"] };\nconst ChipGroupContext = React.createContext<GroupContext | null>(null);\n\nfunction RemoveIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype ToggleChipProps = Omit<React.ComponentProps<\"button\">, \"value\" | \"defaultValue\"> &\n ChipVariants & {\n /**\n * sm · md (inside `Chip.Group`, the group's size by default).\n * @default \"md\"\n */\n size?: ChipVariants[\"size\"];\n /** Inside `Chip.Group`: the value this chip stands for (required there, ignored alone). */\n value?: string;\n /** Alone: pressed or not (`defaultSelected` uncontrolled), `onSelectedChange` on every press. */\n selected?: boolean;\n /** Alone: pressed at first, uncontrolled. */\n defaultSelected?: boolean;\n /** Alone: called with the new state on every press. */\n onSelectedChange?: (selected: boolean) => void;\n /** A pressable chip (the default); `true` makes it a static chip with a remove button. */\n removable?: false;\n };\n\ntype RemovableChipProps = Omit<React.ComponentProps<\"span\">, \"onSelect\"> &\n ChipVariants & {\n /** A static chip (a chosen tag, an applied filter) with a remove button. */\n removable: true;\n /** The accessible name of the remove button — passed translated, e.g. \"Remove #night\". */\n removeLabel: string;\n /** Runs when the remove button is pressed. */\n onRemove: () => void;\n /** Disables the remove button. */\n disabled?: boolean;\n };\n\nexport type ChipProps = ToggleChipProps | RemovableChipProps;\n\n/** A selectable pill (`selected` / `onSelectedChange`), an item of `Chip.Group` (`value`), or `removable`. */\nfunction ChipBase(props: ChipProps) {\n const group = React.useContext(ChipGroupContext);\n if (props.removable) {\n const { removable: _removable, removeLabel, onRemove, disabled, size, className, children, ...rest } = props;\n const s = chip({ size: size ?? group?.size });\n return (\n <span data-removable=\"\" className={s.base({ className })} {...rest}>\n {children}\n <button type=\"button\" aria-label={removeLabel} title={removeLabel} disabled={disabled} onClick={onRemove} className={s.remove()}>\n <RemoveIcon />\n </button>\n </span>\n );\n }\n const { removable: _removable, value, selected, defaultSelected, onSelectedChange, size, className, ...rest } = props;\n const s = chip({ size: size ?? group?.size });\n if (group) {\n if (value === undefined) throw new Error(\"Chip: a chip inside Chip.Group needs a `value`.\");\n return <ToggleGroupPrimitive.Item value={value} className={s.base({ className })} {...rest} />;\n }\n return (\n <TogglePrimitive.Root\n pressed={selected}\n defaultPressed={defaultSelected}\n onPressedChange={onSelectedChange}\n className={s.base({ className })}\n {...rest}\n />\n );\n}\n\ntype GroupBase = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"dir\"> &\n ChipVariants & {\n /**\n * sm · md: the size of every chip of the group.\n * @default \"md\"\n */\n size?: ChipVariants[\"size\"];\n /** The group's accessible name — passed translated (\"Filter by format\"). */\n label?: string;\n /** The arrow keys that move: left / right (horizontal, a row) or up / down (vertical, a column). */\n orientation?: \"horizontal\" | \"vertical\";\n /** Arrow keys wrap from the last chip to the first (default `true`). */\n loop?: boolean;\n /** Disables every chip of the group. */\n disabled?: boolean;\n };\n\nexport type ChipGroupProps =\n | (GroupBase & {\n /** One choice: the chips behave as radios. */\n type: \"single\";\n /** The chosen value (controlled), `\"\"` when none. */\n value?: string;\n /** The value chosen at first, uncontrolled. */\n defaultValue?: string;\n /** Called with the new value on every choice. */\n onValueChange?: (value: string) => void;\n /** Keeps one chip selected: pressing the selected chip does not clear it (default `false`). */\n required?: boolean;\n })\n | (GroupBase & {\n /** Any number of choices: the chips are pressed buttons. */\n type: \"multiple\";\n /** The chosen values (controlled). */\n value?: string[];\n /** The values chosen at first, uncontrolled. */\n defaultValue?: string[];\n /** Called with the new values on every press. */\n onValueChange?: (value: string[]) => void;\n required?: never;\n });\n\n/** Chips that choose together: `type` single · multiple, `value`/`defaultValue` + `onValueChange`. */\nexport function ChipGroup({ label, size, className, children, ...props }: ChipGroupProps) {\n const s = chip({ size });\n const context = React.useMemo(() => ({ size }), [size]);\n const common = { \"aria-label\": label, className: s.group({ className }) };\n let group: React.ReactNode;\n if (props.type === \"single\") {\n const { type: _type, value, defaultValue, onValueChange, required, ...rest } = props;\n group = <SingleGroup {...rest} {...common} value={value} defaultValue={defaultValue} onValueChange={onValueChange} required={required}>{children}</SingleGroup>;\n } else {\n const { type: _type, required: _required, ...rest } = props;\n group = (\n <ToggleGroupPrimitive.Root type=\"multiple\" {...rest} {...common}>\n {children}\n </ToggleGroupPrimitive.Root>\n );\n }\n return <ChipGroupContext.Provider value={context}>{group}</ChipGroupContext.Provider>;\n}\n\n/** Radix's single group, plus `required`: the value is held here so that an empty change can be refused. */\nfunction SingleGroup({\n value,\n defaultValue,\n onValueChange,\n required,\n ...props\n}: Omit<GroupBase, \"size\" | \"label\"> & {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n required?: boolean;\n}) {\n const [inner, setInner] = React.useState(defaultValue ?? \"\");\n const current = value ?? inner;\n return (\n <ToggleGroupPrimitive.Root\n type=\"single\"\n value={current}\n onValueChange={(next) => {\n if (required && !next) return;\n if (value === undefined) setInner(next);\n onValueChange?.(next);\n }}\n {...props}\n />\n );\n}\n\n/** `Chip` (selectable, removable) and `Chip.Group` (single · multiple). */\nexport const Chip = Object.assign(ChipBase, { Group: ChipGroup });\n"
9
+ },
10
+ {
11
+ "path": "chip/index.ts",
12
+ "content": "/** @krizaka/ui/chip — `Chip` (selectable, removable), `Chip.Group` (single · multiple), `chip` (the slots). Client (\"use client\"). */\nexport { Chip, chip, ChipGroup, type ChipGroupProps, type ChipProps, type ChipVariants } from \"./chip\";\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/chip.tsx",
22
+ "content": "import { Chip } from \"@krizaka/ui/chip\";\n\nexport default function ChipDemo() {\n return (\n <div className=\"flex gap-2\">\n <Chip>Subtitles</Chip>\n <Chip defaultSelected>HD</Chip>\n <Chip disabled>4K</Chip>\n </div>\n );\n}\n"
23
+ },
24
+ "props": [
25
+ {
26
+ "component": "Chip",
27
+ "description": "`Chip` (selectable, removable) and `Chip.Group` (single · multiple).",
28
+ "props": [
29
+ {
30
+ "name": "onRemove",
31
+ "type": "() => void",
32
+ "default": null,
33
+ "description": "Runs when the remove button is pressed.",
34
+ "required": true
35
+ },
36
+ {
37
+ "name": "removeLabel",
38
+ "type": "string",
39
+ "default": null,
40
+ "description": "The accessible name of the remove button — passed translated, e.g. \"Remove #night\".",
41
+ "required": true
42
+ },
43
+ {
44
+ "name": "defaultSelected",
45
+ "type": "boolean",
46
+ "default": null,
47
+ "description": "Alone: pressed at first, uncontrolled.",
48
+ "required": false
49
+ },
50
+ {
51
+ "name": "onSelectedChange",
52
+ "type": "((selected: boolean) => void)",
53
+ "default": null,
54
+ "description": "Alone: called with the new state on every press.",
55
+ "required": false
56
+ },
57
+ {
58
+ "name": "removable",
59
+ "type": "boolean",
60
+ "default": null,
61
+ "description": "A pressable chip (the default); `true` makes it a static chip with a remove button.\nA static chip (a chosen tag, an applied filter) with a remove button.",
62
+ "required": false
63
+ },
64
+ {
65
+ "name": "selected",
66
+ "type": "boolean",
67
+ "default": null,
68
+ "description": "Alone: pressed or not (`defaultSelected` uncontrolled), `onSelectedChange` on every press.",
69
+ "required": false
70
+ },
71
+ {
72
+ "name": "size",
73
+ "type": "\"sm\" | \"md\"",
74
+ "default": "md",
75
+ "description": "sm · md (inside `Chip.Group`, the group's size by default).",
76
+ "required": false
77
+ },
78
+ {
79
+ "name": "value",
80
+ "type": "string",
81
+ "default": null,
82
+ "description": "Inside `Chip.Group`: the value this chip stands for (required there, ignored alone).",
83
+ "required": false
84
+ }
85
+ ]
86
+ },
87
+ {
88
+ "component": "ChipGroup",
89
+ "description": "Chips that choose together: `type` single · multiple, `value`/`defaultValue` + `onValueChange`.",
90
+ "props": [
91
+ {
92
+ "name": "type",
93
+ "type": "\"single\" | \"multiple\"",
94
+ "default": null,
95
+ "description": "One choice: the chips behave as radios.\nAny number of choices: the chips are pressed buttons.",
96
+ "required": true
97
+ },
98
+ {
99
+ "name": "defaultValue",
100
+ "type": "string | string[]",
101
+ "default": null,
102
+ "description": "The value chosen at first, uncontrolled.\nThe values chosen at first, uncontrolled.",
103
+ "required": false
104
+ },
105
+ {
106
+ "name": "disabled",
107
+ "type": "boolean",
108
+ "default": null,
109
+ "description": "Disables every chip of the group.",
110
+ "required": false
111
+ },
112
+ {
113
+ "name": "label",
114
+ "type": "string",
115
+ "default": null,
116
+ "description": "The group's accessible name — passed translated (\"Filter by format\").",
117
+ "required": false
118
+ },
119
+ {
120
+ "name": "loop",
121
+ "type": "boolean",
122
+ "default": null,
123
+ "description": "Arrow keys wrap from the last chip to the first (default `true`).",
124
+ "required": false
125
+ },
126
+ {
127
+ "name": "onValueChange",
128
+ "type": "((value: string) => void) | ((value: string[]) => void)",
129
+ "default": null,
130
+ "description": "Called with the new value on every choice.\nCalled with the new values on every press.",
131
+ "required": false
132
+ },
133
+ {
134
+ "name": "orientation",
135
+ "type": "\"horizontal\" | \"vertical\"",
136
+ "default": null,
137
+ "description": "The arrow keys that move: left / right (horizontal, a row) or up / down (vertical, a column).",
138
+ "required": false
139
+ },
140
+ {
141
+ "name": "required",
142
+ "type": "boolean",
143
+ "default": null,
144
+ "description": "Keeps one chip selected: pressing the selected chip does not clear it (default `false`).",
145
+ "required": false
146
+ },
147
+ {
148
+ "name": "size",
149
+ "type": "\"sm\" | \"md\"",
150
+ "default": "md",
151
+ "description": "sm · md: the size of every chip of the group.",
152
+ "required": false
153
+ },
154
+ {
155
+ "name": "value",
156
+ "type": "string | string[]",
157
+ "default": null,
158
+ "description": "The chosen value (controlled), `\"\"` when none.\nThe chosen values (controlled).",
159
+ "required": false
160
+ }
161
+ ]
162
+ }
163
+ ]
164
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "cn",
3
+ "type": "primitive",
4
+ "description": "Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it. tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer): hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean install of the packed package.",
5
+ "files": [
6
+ {
7
+ "path": "cn.ts",
8
+ "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { cn as merge } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs)) ?? \"\";\n}\n"
9
+ }
10
+ ],
11
+ "dependencies": [
12
+ "clsx@^2.1.1",
13
+ "tailwind-variants@^3.3.1"
14
+ ],
15
+ "registryDependencies": [],
16
+ "demo": {
17
+ "path": "demos/cn.tsx",
18
+ "content": "import { cn } from \"@krizaka/ui/cn\";\n\nexport default function CnDemo({ selected = true }: { selected?: boolean }) {\n return (\n <p className={cn(\"rounded-lg border border-border-default px-3 py-2 text-sm text-fg-secondary\", selected && \"border-accent text-fg\", \"px-4\")}>\n The last class wins: <code>px-4</code> replaces <code>px-3</code>, and the selected colours replace the idle ones.\n </p>\n );\n}\n"
19
+ },
20
+ "props": []
21
+ }