@krizaka/ui 2.0.0-beta.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +13 -10
  2. package/dist/{button-D5aSsmxX.d.ts → button-Cgfkg_3y.d.ts} +5 -1
  3. package/dist/button.d.ts +5 -1
  4. package/dist/button.js +4 -2
  5. package/dist/button.js.map +1 -1
  6. package/dist/card.d.ts +7 -1
  7. package/dist/card.js +2 -2
  8. package/dist/card.js.map +1 -1
  9. package/dist/{chunk-IUGBKMJC.js → chunk-FHYZSO2S.js} +2 -2
  10. package/dist/{chunk-6DJ6SQY3.js → chunk-PESA2R4U.js} +5 -3
  11. package/dist/chunk-PESA2R4U.js.map +1 -0
  12. package/dist/command.d.ts +11 -2
  13. package/dist/command.js +17 -6
  14. package/dist/command.js.map +1 -1
  15. package/dist/confirm-button.d.ts +1 -1
  16. package/dist/confirm-button.js +1 -1
  17. package/dist/countdown.d.ts +7 -6
  18. package/dist/countdown.js +12 -8
  19. package/dist/countdown.js.map +1 -1
  20. package/dist/dialog.js +2 -2
  21. package/dist/dropdown-menu.js +3 -3
  22. package/dist/dropdown-menu.js.map +1 -1
  23. package/dist/field.js +2 -2
  24. package/dist/field.js.map +1 -1
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +52 -10
  27. package/dist/index.js.map +1 -1
  28. package/dist/native.d.ts +20 -8
  29. package/dist/native.js +170 -184
  30. package/dist/native.js.map +1 -1
  31. package/dist/theme.d.ts +19 -9
  32. package/dist/theme.js +8 -5
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +3 -3
  35. package/registry/avatar.json +20 -5
  36. package/registry/button.json +36 -15
  37. package/registry/card.json +17 -2
  38. package/registry/command.json +30 -8
  39. package/registry/confirm-button.json +4 -4
  40. package/registry/countdown.json +53 -12
  41. package/registry/dropdown-menu.json +1 -1
  42. package/registry/examples/button/gradient.d.ts +4 -0
  43. package/registry/examples/button/gradient.js +11 -0
  44. package/registry/examples/button/gradient.tsx +5 -0
  45. package/registry/examples/button/native/gradient.d.ts +4 -0
  46. package/registry/examples/button/native/gradient.dark.png +0 -0
  47. package/registry/examples/button/native/gradient.js +12 -0
  48. package/registry/examples/button/native/gradient.light.png +0 -0
  49. package/registry/examples/button/native/gradient.tsx +5 -0
  50. package/registry/examples/command/dialog.js +1 -2
  51. package/registry/examples/command/dialog.tsx +1 -2
  52. package/registry/examples/command/inline.js +1 -2
  53. package/registry/examples/command/inline.tsx +1 -2
  54. package/registry/examples/command/no-results.d.ts +4 -0
  55. package/registry/examples/command/no-results.js +19 -0
  56. package/registry/examples/command/no-results.tsx +20 -0
  57. package/registry/examples/countdown/native/urgent.dark.png +0 -0
  58. package/registry/examples/countdown/native/urgent.light.png +0 -0
  59. package/registry/examples/countdown/native/with-label.d.ts +4 -0
  60. package/registry/examples/countdown/native/with-label.dark.png +0 -0
  61. package/registry/examples/countdown/native/with-label.js +13 -0
  62. package/registry/examples/countdown/native/with-label.light.png +0 -0
  63. package/registry/examples/countdown/native/with-label.tsx +7 -0
  64. package/registry/examples/countdown/with-label.d.ts +4 -0
  65. package/registry/examples/countdown/with-label.js +13 -0
  66. package/registry/examples/countdown/with-label.tsx +10 -0
  67. package/registry/field.json +1 -1
  68. package/registry/index.json +8 -8
  69. package/registry/theme.json +24 -9
  70. package/dist/chunk-6DJ6SQY3.js.map +0 -1
  71. /package/dist/{chunk-IUGBKMJC.js.map → chunk-FHYZSO2S.js.map} +0 -0
@@ -28,7 +28,7 @@
28
28
  "bestPractices": [
29
29
  "Group the commands (`Command.Group heading`) and show their shortcuts with `Command.Shortcut`.",
30
30
  "Add `keywords` so a command is found by the words people type, not only by its label.",
31
- "Always say what an empty search means with `Command.Empty emptyLabel`."
31
+ "Always say what an empty search means with `emptyLabel` on `Command.List` (said beside the listbox, never inside it)."
32
32
  ],
33
33
  "accessibility": {
34
34
  "keyboard": [
@@ -97,6 +97,13 @@
97
97
  "component": "CommandList",
98
98
  "description": "",
99
99
  "props": [
100
+ {
101
+ "name": "emptyLabel",
102
+ "type": "ReactNode",
103
+ "default": null,
104
+ "description": "Shown when nothing matches — passed translated. Rendered after the listbox, never inside it: an empty listbox is\nvalid, a listbox holding text is not.",
105
+ "required": false
106
+ },
100
107
  {
101
108
  "name": "label",
102
109
  "type": "string",
@@ -108,7 +115,7 @@
108
115
  },
109
116
  {
110
117
  "component": "CommandEmpty",
111
- "description": "Shown by cmdk only when no item matches.",
118
+ "description": "Shown by cmdk only when no item matches. Place it beside `Command.List` (or pass `emptyLabel` to the list), never\ninside it: inside, it is rendered as before but warns in development — the listbox then holds text, which axe\nrefuses (`aria-required-children`).",
112
119
  "props": [
113
120
  {
114
121
  "name": "emptyLabel",
@@ -200,7 +207,15 @@
200
207
  "description": "In a card: the input, groups, shortcuts, a disabled item.",
201
208
  "path": "examples/command/inline.tsx",
202
209
  "module": "@krizaka/ui/registry/examples/command/inline",
203
- "code": "import { Command } from \"@krizaka/ui/command\";\n\nexport default function CommandInline() {\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" />\n <Command.List label=\"Suggestions\">\n <Command.Empty emptyLabel=\"Nothing matches.\" />\n <Command.Group heading=\"Pages\">\n <Command.Item>\n Home\n <Command.Shortcut>G H</Command.Shortcut>\n </Command.Item>\n <Command.Item>\n Wallet\n <Command.Shortcut>G W</Command.Shortcut>\n </Command.Item>\n <Command.Item disabled>Studio</Command.Item>\n </Command.Group>\n <Command.Separator />\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n <Command.Item>Switch the theme</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
210
+ "code": "import { Command } from \"@krizaka/ui/command\";\n\nexport default function CommandInline() {\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" />\n <Command.List label=\"Suggestions\" emptyLabel=\"Nothing matches.\">\n <Command.Group heading=\"Pages\">\n <Command.Item>\n Home\n <Command.Shortcut>G H</Command.Shortcut>\n </Command.Item>\n <Command.Item>\n Wallet\n <Command.Shortcut>G W</Command.Shortcut>\n </Command.Item>\n <Command.Item disabled>Studio</Command.Item>\n </Command.Group>\n <Command.Separator />\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n <Command.Item>Switch the theme</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
211
+ },
212
+ {
213
+ "name": "no-results",
214
+ "title": "No results",
215
+ "description": "`emptyLabel` on `Command.List`: said beside the listbox when nothing matches.",
216
+ "path": "examples/command/no-results.tsx",
217
+ "module": "@krizaka/ui/registry/examples/command/no-results",
218
+ "code": "\"use client\";\n\nimport { Command } from \"@krizaka/ui/command\";\nimport { useState } from \"react\";\n\n// Nothing matches: `emptyLabel` is said beside the results, the listbox stays empty (valid for screen readers).\nexport default function CommandNoResults() {\n const [query, setQuery] = useState(\"zzz\");\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" value={query} onValueChange={setQuery} />\n <Command.List label=\"Suggestions\" emptyLabel={`Nothing matches “${query}”.`}>\n <Command.Group heading=\"Pages\">\n <Command.Item>Home</Command.Item>\n <Command.Item>Wallet</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
204
219
  },
205
220
  {
206
221
  "name": "dialog",
@@ -208,7 +223,7 @@
208
223
  "description": "`CommandDialog`: the palette of an app, opened from a button or a shortcut.",
209
224
  "path": "examples/command/dialog.tsx",
210
225
  "module": "@krizaka/ui/registry/examples/command/dialog",
211
- "code": "\"use client\";\n\nimport { Button } from \"@krizaka/ui/button\";\nimport { Command, CommandDialog } from \"@krizaka/ui/command\";\nimport { Kbd } from \"@krizaka/ui/kbd\";\nimport { useState } from \"react\";\n\n// The palette of an app, in the platform's Dialog: open it from a button or your own ⌘K shortcut.\nexport default function CommandDialogExample({ defaultOpen = false }: { defaultOpen?: boolean }) {\n const [open, setOpen] = useState(defaultOpen);\n return (\n <>\n <Button variant=\"outline\" onClick={() => setOpen(true)}>\n Search <Kbd size=\"sm\">⌘K</Kbd>\n </Button>\n <CommandDialog\n open={open}\n onOpenChange={setOpen}\n label=\"Search\"\n footer={<p className=\"border-t border-border-subtle bg-surface-2 px-4 py-2.5 text-xs text-fg-secondary\">↑↓ to move, ↵ to open</p>}\n >\n <Command.Input placeholder=\"Search creators, videos, tags\" />\n <Command.List label=\"Results\">\n <Command.Empty emptyLabel=\"Nothing matches.\" />\n <Command.Group heading=\"Pages\">\n <Command.Item onSelect={() => setOpen(false)}>Home</Command.Item>\n <Command.Item onSelect={() => setOpen(false)}>Wallet</Command.Item>\n </Command.Group>\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n </Command.Group>\n </Command.List>\n </CommandDialog>\n </>\n );\n}\n"
226
+ "code": "\"use client\";\n\nimport { Button } from \"@krizaka/ui/button\";\nimport { Command, CommandDialog } from \"@krizaka/ui/command\";\nimport { Kbd } from \"@krizaka/ui/kbd\";\nimport { useState } from \"react\";\n\n// The palette of an app, in the platform's Dialog: open it from a button or your own ⌘K shortcut.\nexport default function CommandDialogExample({ defaultOpen = false }: { defaultOpen?: boolean }) {\n const [open, setOpen] = useState(defaultOpen);\n return (\n <>\n <Button variant=\"outline\" onClick={() => setOpen(true)}>\n Search <Kbd size=\"sm\">⌘K</Kbd>\n </Button>\n <CommandDialog\n open={open}\n onOpenChange={setOpen}\n label=\"Search\"\n footer={<p className=\"border-t border-border-subtle bg-surface-2 px-4 py-2.5 text-xs text-fg-secondary\">↑↓ to move, ↵ to open</p>}\n >\n <Command.Input placeholder=\"Search creators, videos, tags\" />\n <Command.List label=\"Results\" emptyLabel=\"Nothing matches.\">\n <Command.Group heading=\"Pages\">\n <Command.Item onSelect={() => setOpen(false)}>Home</Command.Item>\n <Command.Item onSelect={() => setOpen(false)}>Wallet</Command.Item>\n </Command.Group>\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n </Command.Group>\n </Command.List>\n </CommandDialog>\n </>\n );\n}\n"
212
227
  }
213
228
  ]
214
229
  },
@@ -216,7 +231,7 @@
216
231
  "files": [
217
232
  {
218
233
  "path": "command/command.tsx",
219
- "content": "// Client. A command palette on cmdk: the combobox / listbox roles, the active option (aria-activedescendant), the\n// arrows, Home / End, Enter, and the filtering (turn it off with `shouldFilter={false}` when the server searches).\n// `CommandDialog` puts it in the platform's Dialog. Words arrive as props.\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { DialogContent, type DialogContentProps } from \"../dialog/dialog\";\n\nexport const command = tv({\n slots: {\n root: \"flex h-full w-full flex-col overflow-hidden bg-surface-1 text-fg\",\n inputWrap: \"flex items-center gap-3 border-b border-border-default px-4\",\n icon: \"h-5 w-5 shrink-0 text-fg-secondary\",\n input:\n \"h-13 w-full min-w-0 flex-1 bg-transparent text-sm text-fg outline-hidden placeholder:text-fg-muted disabled:cursor-not-allowed disabled:opacity-50 sm:text-base\",\n list: \"max-h-[min(60dvh,26rem)] scroll-py-2 overflow-y-auto overscroll-contain p-2\",\n empty: \"px-3 py-8 text-center text-sm text-fg-secondary\",\n loading: \"px-3 py-6 text-center text-sm text-fg-secondary\",\n group:\n \"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:pb-1.5 [&_[cmdk-group-heading]]:pt-3 [&_[cmdk-group-heading]]:text-xs \" +\n \"[&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider [&_[cmdk-group-heading]]:text-fg-secondary\",\n item:\n \"relative flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-fg outline-hidden transition-colors \" +\n \"before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-transparent \" +\n \"data-[selected=true]:bg-surface-2 data-[selected=true]:before:bg-accent data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-40\",\n separator: \"-mx-2 my-1 h-px bg-border-subtle\",\n shortcut: \"ml-auto text-xs tracking-widest text-fg-secondary\",\n },\n});\nconst s = command();\n\nfunction SearchIcon({ className }: { className?: string }) {\n return (\n <svg viewBox=\"0 0 24 24\" className={className} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </svg>\n );\n}\n\nexport type CommandRootProps = React.ComponentProps<typeof CommandPrimitive> & {\n /** The accessible name of the palette — passed translated (\"Search\", \"Commands\"). */\n label: string;\n};\n\n/** The palette: `value` / `onValueChange` (the active item), `shouldFilter`, `filter`, `loop`. */\nexport function CommandRoot({ className, ...props }: CommandRootProps) {\n return <CommandPrimitive className={s.root({ className })} {...props} />;\n}\n\nexport type CommandInputProps = Omit<React.ComponentProps<typeof CommandPrimitive.Input>, \"placeholder\"> & {\n /** What to type — passed translated. The input is named by the palette's `label`. */\n placeholder: string;\n /** After the input, inside the bar: a spinner, a clear button. */\n trailing?: React.ReactNode;\n};\n\n/** The search field (`value` / `onValueChange` for the query), with a magnifier. */\nexport function CommandInput({ className, placeholder, trailing, ...props }: CommandInputProps) {\n return (\n <div className={s.inputWrap()} cmdk-input-wrapper=\"\">\n <SearchIcon className={s.icon()} />\n <CommandPrimitive.Input placeholder={placeholder} className={s.input({ className })} {...props} />\n {trailing}\n </div>\n );\n}\n\nexport type CommandListProps = React.ComponentProps<typeof CommandPrimitive.List> & {\n /** The accessible name of the list of results — passed translated (\"Suggestions\", \"Results\"). */\n label: string;\n};\n\nexport function CommandList({ className, ...props }: CommandListProps) {\n return <CommandPrimitive.List className={s.list({ className })} {...props} />;\n}\n\nexport type CommandEmptyProps = Omit<React.ComponentProps<typeof CommandPrimitive.Empty>, \"children\"> & {\n /** Shown when nothing matches — passed translated. */\n emptyLabel: React.ReactNode;\n};\n\n/** Shown by cmdk only when no item matches. */\nexport function CommandEmpty({ className, emptyLabel, ...props }: CommandEmptyProps) {\n return (\n <CommandPrimitive.Empty className={s.empty({ className })} {...props}>\n {emptyLabel}\n </CommandPrimitive.Empty>\n );\n}\n\n/** While results load (`label` is announced). */\nexport function CommandLoading({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Loading>) {\n return <CommandPrimitive.Loading className={s.loading({ className })} {...props} />;\n}\n\n/** A group of items under a `heading`. */\nexport function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return <CommandPrimitive.Group className={s.group({ className })} {...props} />;\n}\n\n/** An option: `onSelect` on Enter or click, `value` and `keywords` for the filter, `disabled`. */\nexport function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return <CommandPrimitive.Item className={s.item({ className })} {...props} />;\n}\n\n/** A line between groups. Decorative (`role=\"none\"`): cmdk's own separator is a role a listbox may not hold (axe). */\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div role=\"none\" className={s.separator({ className })} {...props} />;\n}\n\n/** A keyboard hint at the end of an item. */\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return <span className={s.shortcut({ className })} {...props} />;\n}\n\n/** `Command.Root/Input/List/Empty/Loading/Group/Item/Separator/Shortcut`. */\nexport const Command = {\n Root: CommandRoot,\n Input: CommandInput,\n List: CommandList,\n Empty: CommandEmpty,\n Loading: CommandLoading,\n Group: CommandGroup,\n Item: CommandItem,\n Separator: CommandSeparator,\n Shortcut: CommandShortcut,\n};\n\nexport type CommandDialogProps = Omit<CommandRootProps, \"title\"> & {\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 (Escape, a click outside, a shortcut of the product). */\n onOpenChange?: (open: boolean) => void;\n /** Under the palette, inside the dialog: shortcuts, a \"see all results\" link. */\n footer?: React.ReactNode;\n /** Passed to `Dialog.Content`: `size`, `container`, `className` of the dialog. */\n contentProps?: Omit<Extract<DialogContentProps, { hideClose: true }>, \"hideClose\" | \"children\" | \"placement\">;\n};\n\n/**\n * The palette in a dialog (Radix: focus trap, Escape, focus return). The dialog is named by `label`; it has no close\n * button — Escape and a click outside close it — and the input takes the focus when it opens.\n */\nexport function CommandDialog({ open, defaultOpen, onOpenChange, footer, contentProps, label, className, children, ...props }: CommandDialogProps) {\n const { className: contentClassName, ...content } = contentProps ?? {};\n return (\n <DialogPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n <DialogContent\n hideClose\n size=\"lg\"\n aria-describedby={undefined}\n className={cn(\"top-[12dvh] translate-y-0 sm:top-[14dvh]\", contentClassName)}\n {...content}\n >\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <CommandRoot label={label} className={className} {...props}>\n {children}\n </CommandRoot>\n {footer}\n </DialogContent>\n </DialogPrimitive.Root>\n );\n}\n"
234
+ "content": "// Client. A command palette on cmdk: the combobox / listbox roles, the active option (aria-activedescendant), the\n// arrows, Home / End, Enter, and the filtering (turn it off with `shouldFilter={false}` when the server searches).\n// `CommandDialog` puts it in the platform's Dialog. Words arrive as props.\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { DialogContent, type DialogContentProps } from \"../dialog/dialog\";\n\nexport const command = tv({\n slots: {\n root: \"flex h-full w-full flex-col overflow-hidden bg-surface-1 text-fg\",\n inputWrap: \"flex items-center gap-3 border-b border-border-default px-4\",\n icon: \"h-5 w-5 shrink-0 text-fg-secondary\",\n input:\n \"h-13 w-full min-w-0 flex-1 bg-transparent text-sm text-fg outline-hidden placeholder:text-fg-muted disabled:cursor-not-allowed disabled:opacity-50 sm:text-base\",\n list: \"max-h-[min(60dvh,26rem)] scroll-py-2 overflow-y-auto overscroll-contain p-2 [&:not(:has([cmdk-item]))]:p-0\",\n empty: \"px-3 py-8 text-center text-sm text-fg-secondary\",\n loading: \"px-3 py-6 text-center text-sm text-fg-secondary\",\n group:\n \"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:pb-1.5 [&_[cmdk-group-heading]]:pt-3 [&_[cmdk-group-heading]]:text-xs \" +\n \"[&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider [&_[cmdk-group-heading]]:text-fg-secondary\",\n item:\n \"relative flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-fg outline-hidden transition-colors \" +\n \"before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-transparent \" +\n \"data-[selected=true]:bg-surface-2 data-[selected=true]:before:bg-accent data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-40\",\n separator: \"-mx-2 my-1 h-px bg-border-subtle\",\n shortcut: \"ml-auto text-xs tracking-widest text-fg-secondary\",\n },\n});\nconst s = command();\n\nfunction SearchIcon({ className }: { className?: string }) {\n return (\n <svg viewBox=\"0 0 24 24\" className={className} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </svg>\n );\n}\n\nexport type CommandRootProps = React.ComponentProps<typeof CommandPrimitive> & {\n /** The accessible name of the palette — passed translated (\"Search\", \"Commands\"). */\n label: string;\n};\n\n/** The palette: `value` / `onValueChange` (the active item), `shouldFilter`, `filter`, `loop`. */\nexport function CommandRoot({ className, ...props }: CommandRootProps) {\n return <CommandPrimitive className={s.root({ className })} {...props} />;\n}\n\nexport type CommandInputProps = Omit<React.ComponentProps<typeof CommandPrimitive.Input>, \"placeholder\"> & {\n /** What to type — passed translated. The input is named by the palette's `label`. */\n placeholder: string;\n /** After the input, inside the bar: a spinner, a clear button. */\n trailing?: React.ReactNode;\n};\n\n/** The search field (`value` / `onValueChange` for the query), with a magnifier. */\nexport function CommandInput({ className, placeholder, trailing, ...props }: CommandInputProps) {\n return (\n <div className={s.inputWrap()} cmdk-input-wrapper=\"\">\n <SearchIcon className={s.icon()} />\n <CommandPrimitive.Input placeholder={placeholder} className={s.input({ className })} {...props} />\n {trailing}\n </div>\n );\n}\n\n// Set inside `Command.List`: a listbox may only hold options and groups (axe `aria-required-children`), so the empty\n// message must live beside it, not in it.\nconst InsideList = React.createContext(false);\n\nexport type CommandListProps = React.ComponentProps<typeof CommandPrimitive.List> & {\n /** The accessible name of the list of results — passed translated (\"Suggestions\", \"Results\"). */\n label: string;\n /**\n * Shown when nothing matches — passed translated. Rendered after the listbox, never inside it: an empty listbox is\n * valid, a listbox holding text is not.\n */\n emptyLabel?: React.ReactNode;\n};\n\nexport function CommandList({ className, emptyLabel, children, ...props }: CommandListProps) {\n return (\n <>\n <CommandPrimitive.List className={s.list({ className })} {...props}>\n <InsideList.Provider value>{children}</InsideList.Provider>\n </CommandPrimitive.List>\n {emptyLabel === undefined ? null : <CommandEmpty emptyLabel={emptyLabel} />}\n </>\n );\n}\n\nexport type CommandEmptyProps = Omit<React.ComponentProps<typeof CommandPrimitive.Empty>, \"children\"> & {\n /** Shown when nothing matches — passed translated. */\n emptyLabel: React.ReactNode;\n};\n\n/**\n * Shown by cmdk only when no item matches. Place it beside `Command.List` (or pass `emptyLabel` to the list), never\n * inside it: inside, it is rendered as before but warns in development — the listbox then holds text, which axe\n * refuses (`aria-required-children`).\n */\nexport function CommandEmpty({ className, emptyLabel, ...props }: CommandEmptyProps) {\n const inside = React.useContext(InsideList);\n React.useEffect(() => {\n if (inside && typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\") {\n console.warn(\"@krizaka/ui/command: <Command.Empty> inside <Command.List> breaks the listbox (axe aria-required-children). Pass `emptyLabel` to <Command.List> instead.\");\n }\n }, [inside]);\n return (\n <CommandPrimitive.Empty className={s.empty({ className })} {...props}>\n {emptyLabel}\n </CommandPrimitive.Empty>\n );\n}\n\n/** While results load (`label` is announced). */\nexport function CommandLoading({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Loading>) {\n return <CommandPrimitive.Loading className={s.loading({ className })} {...props} />;\n}\n\n/** A group of items under a `heading`. */\nexport function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return <CommandPrimitive.Group className={s.group({ className })} {...props} />;\n}\n\n/** An option: `onSelect` on Enter or click, `value` and `keywords` for the filter, `disabled`. */\nexport function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return <CommandPrimitive.Item className={s.item({ className })} {...props} />;\n}\n\n/** A line between groups. Decorative (`role=\"none\"`): cmdk's own separator is a role a listbox may not hold (axe). */\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div role=\"none\" className={s.separator({ className })} {...props} />;\n}\n\n/** A keyboard hint at the end of an item. */\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return <span className={s.shortcut({ className })} {...props} />;\n}\n\n/** `Command.Root/Input/List/Empty/Loading/Group/Item/Separator/Shortcut`. */\nexport const Command = {\n Root: CommandRoot,\n Input: CommandInput,\n List: CommandList,\n Empty: CommandEmpty,\n Loading: CommandLoading,\n Group: CommandGroup,\n Item: CommandItem,\n Separator: CommandSeparator,\n Shortcut: CommandShortcut,\n};\n\nexport type CommandDialogProps = Omit<CommandRootProps, \"title\"> & {\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 (Escape, a click outside, a shortcut of the product). */\n onOpenChange?: (open: boolean) => void;\n /** Under the palette, inside the dialog: shortcuts, a \"see all results\" link. */\n footer?: React.ReactNode;\n /** Passed to `Dialog.Content`: `size`, `container`, `className` of the dialog. */\n contentProps?: Omit<Extract<DialogContentProps, { hideClose: true }>, \"hideClose\" | \"children\" | \"placement\">;\n};\n\n/**\n * The palette in a dialog (Radix: focus trap, Escape, focus return). The dialog is named by `label`; it has no close\n * button — Escape and a click outside close it — and the input takes the focus when it opens.\n */\nexport function CommandDialog({ open, defaultOpen, onOpenChange, footer, contentProps, label, className, children, ...props }: CommandDialogProps) {\n const { className: contentClassName, ...content } = contentProps ?? {};\n return (\n <DialogPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n <DialogContent\n hideClose\n size=\"lg\"\n aria-describedby={undefined}\n className={cn(\"top-[12dvh] translate-y-0 sm:top-[14dvh]\", contentClassName)}\n {...content}\n >\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <CommandRoot label={label} className={className} {...props}>\n {children}\n </CommandRoot>\n {footer}\n </DialogContent>\n </DialogPrimitive.Root>\n );\n}\n"
220
235
  },
221
236
  {
222
237
  "path": "command/index.ts",
@@ -224,7 +239,7 @@
224
239
  },
225
240
  {
226
241
  "path": "command/meta.ts",
227
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Command\",\n summary: \"A command palette on cmdk: a search input over a filtered list of commands, inline or in a dialog.\",\n status: \"beta\",\n category: \"navigation\",\n platforms: \"web\",\n whenToUse: [\n \"To reach any page or action of an app from the keyboard (⌘K): `CommandDialog`.\",\n \"For a searchable list of choices inside a panel: `Command.Root` inline.\",\n \"When results come from the server: `shouldFilter={false}` and `Command.Loading`.\",\n ],\n whenNotToUse: [\n { when: \"For a short list of actions on one item.\", use: \"dropdown-menu\" },\n { when: \"For a choice among a few options in a form: use a select or radios.\", use: \"radio-group\" },\n { when: \"As the only way to navigate: keep the visible navigation.\" },\n ],\n bestPractices: [\n \"Group the commands (`Command.Group heading`) and show their shortcuts with `Command.Shortcut`.\",\n \"Add `keywords` so a command is found by the words people type, not only by its label.\",\n \"Always say what an empty search means with `Command.Empty emptyLabel`.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Arrow keys\", action: \"Move through the results.\" },\n { keys: \"Enter\", action: \"Runs the highlighted command.\" },\n { keys: \"Esc\", action: \"Closes the `CommandDialog` and returns the focus.\" },\n ],\n notes: [\n \"A combobox and a listbox: the input owns `aria-activedescendant`, the results are options.\",\n \"`label` names the palette, `Command.List label` the results.\",\n \"`CommandDialog` traps the focus in the platform's Dialog and puts it in the input when it opens.\",\n ],\n },\n related: [\"dialog\", \"dropdown-menu\", \"kbd\"],\n web: {\n imports: [\"Command\", \"CommandDialog\"],\n examples: [\n { name: \"inline\", title: \"Inline\", description: \"In a card: the input, groups, shortcuts, a disabled item.\" },\n { name: \"dialog\", title: \"In a dialog\", description: \"`CommandDialog`: the palette of an app, opened from a button or a shortcut.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
242
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Command\",\n summary: \"A command palette on cmdk: a search input over a filtered list of commands, inline or in a dialog.\",\n status: \"beta\",\n category: \"navigation\",\n platforms: \"web\",\n whenToUse: [\n \"To reach any page or action of an app from the keyboard (⌘K): `CommandDialog`.\",\n \"For a searchable list of choices inside a panel: `Command.Root` inline.\",\n \"When results come from the server: `shouldFilter={false}` and `Command.Loading`.\",\n ],\n whenNotToUse: [\n { when: \"For a short list of actions on one item.\", use: \"dropdown-menu\" },\n { when: \"For a choice among a few options in a form: use a select or radios.\", use: \"radio-group\" },\n { when: \"As the only way to navigate: keep the visible navigation.\" },\n ],\n bestPractices: [\n \"Group the commands (`Command.Group heading`) and show their shortcuts with `Command.Shortcut`.\",\n \"Add `keywords` so a command is found by the words people type, not only by its label.\",\n \"Always say what an empty search means with `emptyLabel` on `Command.List` (said beside the listbox, never inside it).\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Arrow keys\", action: \"Move through the results.\" },\n { keys: \"Enter\", action: \"Runs the highlighted command.\" },\n { keys: \"Esc\", action: \"Closes the `CommandDialog` and returns the focus.\" },\n ],\n notes: [\n \"A combobox and a listbox: the input owns `aria-activedescendant`, the results are options.\",\n \"`label` names the palette, `Command.List label` the results.\",\n \"`CommandDialog` traps the focus in the platform's Dialog and puts it in the input when it opens.\",\n ],\n },\n related: [\"dialog\", \"dropdown-menu\", \"kbd\"],\n web: {\n imports: [\"Command\", \"CommandDialog\"],\n examples: [\n { name: \"inline\", title: \"Inline\", description: \"In a card: the input, groups, shortcuts, a disabled item.\" },\n { name: \"no-results\", title: \"No results\", description: \"`emptyLabel` on `Command.List`: said beside the listbox when nothing matches.\" },\n { name: \"dialog\", title: \"In a dialog\", description: \"`CommandDialog`: the palette of an app, opened from a button or a shortcut.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
228
243
  },
229
244
  {
230
245
  "path": "meta.ts",
@@ -242,7 +257,7 @@
242
257
  ],
243
258
  "demo": {
244
259
  "path": "examples/command/inline.tsx",
245
- "content": "import { Command } from \"@krizaka/ui/command\";\n\nexport default function CommandInline() {\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" />\n <Command.List label=\"Suggestions\">\n <Command.Empty emptyLabel=\"Nothing matches.\" />\n <Command.Group heading=\"Pages\">\n <Command.Item>\n Home\n <Command.Shortcut>G H</Command.Shortcut>\n </Command.Item>\n <Command.Item>\n Wallet\n <Command.Shortcut>G W</Command.Shortcut>\n </Command.Item>\n <Command.Item disabled>Studio</Command.Item>\n </Command.Group>\n <Command.Separator />\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n <Command.Item>Switch the theme</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
260
+ "content": "import { Command } from \"@krizaka/ui/command\";\n\nexport default function CommandInline() {\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" />\n <Command.List label=\"Suggestions\" emptyLabel=\"Nothing matches.\">\n <Command.Group heading=\"Pages\">\n <Command.Item>\n Home\n <Command.Shortcut>G H</Command.Shortcut>\n </Command.Item>\n <Command.Item>\n Wallet\n <Command.Shortcut>G W</Command.Shortcut>\n </Command.Item>\n <Command.Item disabled>Studio</Command.Item>\n </Command.Group>\n <Command.Separator />\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n <Command.Item>Switch the theme</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
246
261
  },
247
262
  "props": [
248
263
  {
@@ -282,6 +297,13 @@
282
297
  "component": "CommandList",
283
298
  "description": "",
284
299
  "props": [
300
+ {
301
+ "name": "emptyLabel",
302
+ "type": "ReactNode",
303
+ "default": null,
304
+ "description": "Shown when nothing matches — passed translated. Rendered after the listbox, never inside it: an empty listbox is\nvalid, a listbox holding text is not.",
305
+ "required": false
306
+ },
285
307
  {
286
308
  "name": "label",
287
309
  "type": "string",
@@ -293,7 +315,7 @@
293
315
  },
294
316
  {
295
317
  "component": "CommandEmpty",
296
- "description": "Shown by cmdk only when no item matches.",
318
+ "description": "Shown by cmdk only when no item matches. Place it beside `Command.List` (or pass `emptyLabel` to the list), never\ninside it: inside, it is rendered as before but warns in development — the listbox then holds text, which axe\nrefuses (`aria-required-children`).",
297
319
  "props": [
298
320
  {
299
321
  "name": "emptyLabel",
@@ -125,9 +125,9 @@
125
125
  },
126
126
  {
127
127
  "name": "variant",
128
- "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
128
+ "type": "\"danger\" | \"primary\" | \"gradient\" | \"secondary\" | \"outline\" | \"ghost\"",
129
129
  "default": "ghost",
130
- "description": "The look: `primary` (the accent), `secondary` (a raised surface), `outline`, `ghost`, `danger`.",
130
+ "description": "The look: `primary` (the accent), `gradient` (accent → accent-2), `secondary` (a raised surface), `outline`,\n`ghost`, `danger`.",
131
131
  "required": false
132
132
  }
133
133
  ]
@@ -260,9 +260,9 @@
260
260
  },
261
261
  {
262
262
  "name": "variant",
263
- "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
263
+ "type": "\"danger\" | \"primary\" | \"gradient\" | \"secondary\" | \"outline\" | \"ghost\"",
264
264
  "default": "ghost",
265
- "description": "The look: `primary` (the accent), `secondary` (a raised surface), `outline`, `ghost`, `danger`.",
265
+ "description": "The look: `primary` (the accent), `gradient` (accent → accent-2), `secondary` (a raised surface), `outline`,\n`ghost`, `danger`.",
266
266
  "required": false
267
267
  }
268
268
  ]
@@ -47,7 +47,7 @@
47
47
  "props": [
48
48
  {
49
49
  "component": "Countdown",
50
- "description": "Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n`urgentBelowMs` it turns to the danger role and its last segment pulses (no motion under prefers-reduced-motion).\nWords come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\nannounces what matters. State: `data-urgent`, `data-ended`.\n\nThe danger role is large-text safe (≥ 3:1) from `size=\"md\"`; at `size=\"sm\"` an urgent countdown in light mode is\nunder the 4.5:1 of small text.",
50
+ "description": "Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n`urgentBelowMs` it turns to the danger text role and its last segment pulses (no motion under prefers-reduced-motion).\nWords come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\nannounces what matters. State: `data-urgent`, `data-ended`.\n\nThe urgent colour is `--kz-danger-text` (≥ 4.5:1 on every surface in both themes), so `size=\"sm\"` is safe too.",
51
51
  "props": [
52
52
  {
53
53
  "name": "label",
@@ -77,11 +77,18 @@
77
77
  "description": "Classes merged last, over the primitive's.",
78
78
  "required": false
79
79
  },
80
+ {
81
+ "name": "showLabel",
82
+ "type": "boolean",
83
+ "default": "false",
84
+ "description": "Also show `label` before the segments, in the secondary text role (default: only read by screen readers).",
85
+ "required": false
86
+ },
80
87
  {
81
88
  "name": "size",
82
89
  "type": "\"sm\" | \"md\" | \"lg\"",
83
90
  "default": "md",
84
- "description": "sm · md · lg (the danger role is large-text safe from `md`).",
91
+ "description": "sm · md · lg — the danger text role reads at 4.5:1 at every size, in both themes.",
85
92
  "required": false
86
93
  },
87
94
  {
@@ -95,7 +102,7 @@
95
102
  "name": "urgentBelowMs",
96
103
  "type": "number",
97
104
  "default": "60000",
98
- "description": "Below this many milliseconds it turns urgent (`text-danger`, the last segment pulses). Default 60 s.",
105
+ "description": "Below this many milliseconds it turns urgent (`text-fg-danger`, the last segment pulses). Default 60 s.",
99
106
  "required": false
100
107
  }
101
108
  ]
@@ -129,11 +136,19 @@
129
136
  {
130
137
  "name": "urgent",
131
138
  "title": "Urgent",
132
- "description": "Under a minute: the danger role, the last segment pulses.",
139
+ "description": "Under a minute: the danger text role, the last segment pulses.",
133
140
  "path": "examples/countdown/urgent.tsx",
134
141
  "module": "@krizaka/ui/registry/examples/countdown/urgent",
135
142
  "code": "\"use client\";\n\nimport { Countdown } from \"@krizaka/ui/countdown\";\nimport { useState } from \"react\";\n\n// Under `urgentBelowMs` (one minute by default): the danger role, the last segment pulses.\nexport default function CountdownUrgent() {\n const [target] = useState(() => Date.now() + 42_000);\n return <Countdown target={target} units={{ d: \"d\", h: \"h\", m: \"m\", s: \"s\" }} label=\"Ends in\" />;\n}\n"
136
143
  },
144
+ {
145
+ "name": "with-label",
146
+ "title": "With its label",
147
+ "description": "`showLabel`: the label written before the segments.",
148
+ "path": "examples/countdown/with-label.tsx",
149
+ "module": "@krizaka/ui/registry/examples/countdown/with-label",
150
+ "code": "\"use client\";\n\nimport { Countdown } from \"@krizaka/ui/countdown\";\nimport { useState } from \"react\";\n\n// `showLabel` writes the label before the segments (it stays the accessible name).\nexport default function CountdownWithLabel() {\n const [target] = useState(() => Date.now() + 3_723_000);\n return <Countdown target={target} units={{ d: \"d\", h: \"h\", m: \"m\", s: \"s\" }} label=\"Ends in\" showLabel />;\n}\n"
151
+ },
137
152
  {
138
153
  "name": "ended",
139
154
  "title": "Ended",
@@ -154,7 +169,7 @@
154
169
  "props": [
155
170
  {
156
171
  "component": "Countdown",
157
- "description": "Time left until a moment, as segments (2d 04h 13m — days only when there are some) in tabular figures, on the same\nclock as the web `Countdown` (one interval for every countdown on screen). Under `urgentBelowMs` it turns to the\ndanger role and its last segment breathes (still when the system reduces motion).",
172
+ "description": "Time left until a moment, as segments (2d 04h 13m — days only when there are some) in tabular figures, on the same\nclock as the web `Countdown` (one interval for every countdown on screen). Under `urgentBelowMs` it turns to the\ndanger text role (≥ 4.5:1 in both themes) and its last segment breathes (still when the system reduces motion).\n`showLabel` writes `label` before the segments; it is the accessible name either way.",
158
173
  "props": [
159
174
  {
160
175
  "name": "label",
@@ -177,6 +192,13 @@
177
192
  "description": "The short unit labels, passed translated.",
178
193
  "required": true
179
194
  },
195
+ {
196
+ "name": "showLabel",
197
+ "type": "boolean",
198
+ "default": "false",
199
+ "description": "Also show `label` before the segments, in the secondary text role (default: only read by screen readers).",
200
+ "required": false
201
+ },
180
202
  {
181
203
  "name": "size",
182
204
  "type": "\"sm\" | \"md\" | \"lg\"",
@@ -202,7 +224,7 @@
202
224
  "name": "urgentBelowMs",
203
225
  "type": "number",
204
226
  "default": "60000",
205
- "description": "Below this many milliseconds it turns urgent (the danger role, the last segment breathes). Default 60 s.",
227
+ "description": "Below this many milliseconds it turns urgent (the danger text role, the last segment breathes). Default 60 s.",
206
228
  "required": false
207
229
  }
208
230
  ]
@@ -253,7 +275,7 @@
253
275
  {
254
276
  "name": "urgent",
255
277
  "title": "Urgent",
256
- "description": "Under a minute: the danger role.",
278
+ "description": "Under a minute: the danger text role.",
257
279
  "path": "examples/countdown/native/urgent.tsx",
258
280
  "module": "@krizaka/ui/registry/examples/countdown/native/urgent",
259
281
  "code": "import { Countdown } from \"@krizaka/ui/native\";\nimport { useState } from \"react\";\n\nexport default function NativeCountdownUrgent() {\n const [target] = useState(() => Date.now() + 42_000);\n return <Countdown target={target} units={{ d: \"d\", h: \"h\", m: \"m\", s: \"s\" }} label=\"Ends in\" size=\"lg\" />;\n}\n",
@@ -261,6 +283,18 @@
261
283
  "dark": "examples/countdown/native/urgent.dark.png",
262
284
  "light": "examples/countdown/native/urgent.light.png"
263
285
  }
286
+ },
287
+ {
288
+ "name": "with-label",
289
+ "title": "With its label",
290
+ "description": "`showLabel`: the label written before the segments.",
291
+ "path": "examples/countdown/native/with-label.tsx",
292
+ "module": "@krizaka/ui/registry/examples/countdown/native/with-label",
293
+ "code": "import { Countdown } from \"@krizaka/ui/native\";\nimport { useState } from \"react\";\n\nexport default function NativeCountdownWithLabel() {\n const [target] = useState(() => Date.now() + 3_723_000);\n return <Countdown target={target} units={{ d: \"d\", h: \"h\", m: \"m\", s: \"s\" }} label=\"Ends in\" showLabel />;\n}\n",
294
+ "screenshots": {
295
+ "dark": "examples/countdown/native/with-label.dark.png",
296
+ "light": "examples/countdown/native/with-label.light.png"
297
+ }
264
298
  }
265
299
  ]
266
300
  },
@@ -271,7 +305,7 @@
271
305
  },
272
306
  {
273
307
  "path": "countdown/countdown.tsx",
274
- "content": "// Client: the clock is a hook.\nimport { tv } from \"tailwind-variants\";\n\nimport { countdownParts, type CountdownUnits, useCountdown } from \"./core\";\n\nconst countdown = tv({\n slots: {\n root: \"inline-flex items-baseline gap-1.5 font-display font-black tabular-nums tracking-tight text-fg\",\n segment: \"inline-flex items-baseline\",\n unit: \"ml-0.5 text-[0.5em] font-bold uppercase text-fg-secondary\",\n },\n variants: {\n size: { sm: { root: \"text-sm\" }, md: { root: \"text-xl\" }, lg: { root: \"text-3xl sm:text-4xl\" } },\n urgent: { true: { root: \"text-danger\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type CountdownProps = {\n /** The moment it counts down to: a Date, an ISO string or epoch milliseconds. */\n target: Date | string | number;\n /** Server clock − this clock, in milliseconds. */\n skewMs?: number;\n /** The short unit labels, passed translated. */\n units: CountdownUnits;\n /** Below this many milliseconds it turns urgent (`text-danger`, the last segment pulses). Default 60 s. */\n urgentBelowMs?: number;\n /** sm · md · lg (the danger role is large-text safe from `md`). */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Accessible name, e.g. \"Ends in\". */\n label: string;\n /** Classes merged last, over the primitive's. */\n className?: string;\n};\n\n/**\n * Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n * `urgentBelowMs` it turns to the danger role and its last segment pulses (no motion under prefers-reduced-motion).\n * Words come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\n * announces what matters. State: `data-urgent`, `data-ended`.\n *\n * The danger role is large-text safe (≥ 3:1) from `size=\"md\"`; at `size=\"sm\"` an urgent countdown in light mode is\n * under the 4.5:1 of small text.\n */\nexport function Countdown({ target, skewMs = 0, units, urgentBelowMs = 60_000, size = \"md\", label, className }: CountdownProps) {\n const ms = useCountdown(target, skewMs);\n const urgent = ms > 0 && ms < urgentBelowMs;\n const parts = countdownParts(ms, units);\n const s = countdown({ size, urgent });\n return (\n <span\n role=\"timer\"\n aria-label={label}\n data-urgent={urgent ? \"\" : undefined}\n data-ended={ms === 0 ? \"\" : undefined}\n suppressHydrationWarning\n className={s.root({ className })}\n >\n {parts.map(([value, unit], i) => (\n <span key={unit} className={s.segment({ className: urgent && i === parts.length - 1 ? \"motion-safe:animate-pulse\" : undefined })}>\n {String(value).padStart(2, \"0\")}\n <span className={s.unit()}>{unit}</span>\n </span>\n ))}\n </span>\n );\n}\n"
308
+ "content": "// Client: the clock is a hook.\nimport { tv } from \"tailwind-variants\";\n\nimport { countdownParts, type CountdownUnits, useCountdown } from \"./core\";\n\nconst countdown = tv({\n slots: {\n root: \"inline-flex items-baseline gap-1.5 font-display font-black tabular-nums tracking-tight text-fg\",\n segment: \"inline-flex items-baseline\",\n unit: \"ml-0.5 text-[0.5em] font-bold uppercase text-fg-secondary\",\n label: \"mr-0.5 self-center font-sans text-[0.6em] font-semibold tracking-normal text-fg-secondary\",\n },\n variants: {\n size: { sm: { root: \"text-sm\" }, md: { root: \"text-xl\" }, lg: { root: \"text-3xl sm:text-4xl\" } },\n urgent: { true: { root: \"text-fg-danger\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type CountdownProps = {\n /** The moment it counts down to: a Date, an ISO string or epoch milliseconds. */\n target: Date | string | number;\n /** Server clock − this clock, in milliseconds. */\n skewMs?: number;\n /** The short unit labels, passed translated. */\n units: CountdownUnits;\n /** Below this many milliseconds it turns urgent (`text-fg-danger`, the last segment pulses). Default 60 s. */\n urgentBelowMs?: number;\n /** sm · md · lg — the danger text role reads at 4.5:1 at every size, in both themes. */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Accessible name, e.g. \"Ends in\". */\n label: string;\n /** Also show `label` before the segments, in the secondary text role (default: only read by screen readers). */\n showLabel?: boolean;\n /** Classes merged last, over the primitive's. */\n className?: string;\n};\n\n/**\n * Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n * `urgentBelowMs` it turns to the danger text role and its last segment pulses (no motion under prefers-reduced-motion).\n * Words come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\n * announces what matters. State: `data-urgent`, `data-ended`.\n *\n * The urgent colour is `--kz-danger-text` (≥ 4.5:1 on every surface in both themes), so `size=\"sm\"` is safe too.\n */\nexport function Countdown({ target, skewMs = 0, units, urgentBelowMs = 60_000, size = \"md\", label, showLabel = false, className }: CountdownProps) {\n const ms = useCountdown(target, skewMs);\n const urgent = ms > 0 && ms < urgentBelowMs;\n const parts = countdownParts(ms, units);\n const s = countdown({ size, urgent });\n return (\n <span\n role=\"timer\"\n aria-label={label}\n data-urgent={urgent ? \"\" : undefined}\n data-ended={ms === 0 ? \"\" : undefined}\n suppressHydrationWarning\n className={s.root({ className })}\n >\n {showLabel ? (\n <span aria-hidden className={s.label()}>\n {label}\n </span>\n ) : null}\n {parts.map(([value, unit], i) => (\n <span key={unit} className={s.segment({ className: urgent && i === parts.length - 1 ? \"motion-safe:animate-pulse\" : undefined })}>\n {String(value).padStart(2, \"0\")}\n <span className={s.unit()}>{unit}</span>\n </span>\n ))}\n </span>\n );\n}\n"
275
309
  },
276
310
  {
277
311
  "path": "countdown/index.ts",
@@ -279,7 +313,7 @@
279
313
  },
280
314
  {
281
315
  "path": "countdown/meta.ts",
282
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Countdown\",\n summary: \"The time left until a moment, in tabular figures, turning urgent under a threshold.\",\n status: \"stable\",\n category: \"data-display\",\n platforms: \"both\",\n whenToUse: [\n \"For a deadline the user acts against: an auction, an offer, a live event about to start.\",\n \"With the server's clock (`skewMs`) when the end is decided by the server.\",\n ],\n whenNotToUse: [\n { when: \"For a date far away: write the date (“Ends on 12 May”).\" },\n { when: \"For the progress of a task.\", use: \"progress\" },\n { when: \"For an elapsed time that keeps growing: format a duration in the text.\" },\n ],\n bestPractices: [\n \"Pass `units` and `label` translated: they are the app's words.\",\n \"Use `size=\\\"md\\\"` or larger when it can turn urgent: the danger colour is large-text safe from `md`.\",\n \"Say what happens at zero next to it: the countdown only shows zeros.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"`role=\\\"timer\\\"` named by `label`, without live announcements: a screen reader is not interrupted every second.\",\n \"Announce the end yourself (a toast, a status) if it matters.\",\n \"The pulse of the last segment stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"progress\", \"badge\", \"stat\"],\n web: {\n imports: [\"Countdown\"],\n examples: [\n { name: \"hours\", title: \"Hours\", description: \"Hours, minutes and seconds.\" },\n { name: \"days\", title: \"Days\", description: \"Days appear when there are some.\" },\n { name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md` (default), `lg`.\" },\n { name: \"urgent\", title: \"Urgent\", description: \"Under a minute: the danger role, the last segment pulses.\" },\n { name: \"ended\", title: \"Ended\", description: \"Past the target: zeros and `data-ended`.\" },\n ],\n },\n native: {\n imports: [\"Countdown\"],\n differences: [\"Same props and same clock as the web; style with `style`.\", \"`useCountdown` and `splitDuration` are exported for a custom layout.\"],\n examples: [\n { name: \"hours\", title: \"Hours\", description: \"Hours, minutes, seconds.\" },\n { name: \"days\", title: \"Days\", description: \"Days, hours, minutes.\" },\n { name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md`, `lg`.\" },\n { name: \"urgent\", title: \"Urgent\", description: \"Under a minute: the danger role.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
316
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Countdown\",\n summary: \"The time left until a moment, in tabular figures, turning urgent under a threshold.\",\n status: \"stable\",\n category: \"data-display\",\n platforms: \"both\",\n whenToUse: [\n \"For a deadline the user acts against: an auction, an offer, a live event about to start.\",\n \"With the server's clock (`skewMs`) when the end is decided by the server.\",\n ],\n whenNotToUse: [\n { when: \"For a date far away: write the date (“Ends on 12 May”).\" },\n { when: \"For the progress of a task.\", use: \"progress\" },\n { when: \"For an elapsed time that keeps growing: format a duration in the text.\" },\n ],\n bestPractices: [\n \"Pass `units` and `label` translated: they are the app's words.\",\n \"Use `size=\\\"md\\\"` or larger when it can turn urgent: the danger colour is large-text safe from `md`.\",\n \"Say what happens at zero next to it: the countdown only shows zeros.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"`role=\\\"timer\\\"` named by `label`, without live announcements: a screen reader is not interrupted every second.\",\n \"Announce the end yourself (a toast, a status) if it matters.\",\n \"The pulse of the last segment stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"progress\", \"badge\", \"stat\"],\n web: {\n imports: [\"Countdown\"],\n examples: [\n { name: \"hours\", title: \"Hours\", description: \"Hours, minutes and seconds.\" },\n { name: \"days\", title: \"Days\", description: \"Days appear when there are some.\" },\n { name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md` (default), `lg`.\" },\n { name: \"urgent\", title: \"Urgent\", description: \"Under a minute: the danger text role, the last segment pulses.\" },\n { name: \"with-label\", title: \"With its label\", description: \"`showLabel`: the label written before the segments.\" },\n { name: \"ended\", title: \"Ended\", description: \"Past the target: zeros and `data-ended`.\" },\n ],\n },\n native: {\n imports: [\"Countdown\"],\n differences: [\"Same props and same clock as the web; style with `style`.\", \"`useCountdown` and `splitDuration` are exported for a custom layout.\"],\n examples: [\n { name: \"hours\", title: \"Hours\", description: \"Hours, minutes, seconds.\" },\n { name: \"days\", title: \"Days\", description: \"Days, hours, minutes.\" },\n { name: \"sizes\", title: \"Sizes\", description: \"`sm`, `md`, `lg`.\" },\n { name: \"urgent\", title: \"Urgent\", description: \"Under a minute: the danger text role.\" },\n { name: \"with-label\", title: \"With its label\", description: \"`showLabel`: the label written before the segments.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
283
317
  },
284
318
  {
285
319
  "path": "meta.ts",
@@ -297,7 +331,7 @@
297
331
  "props": [
298
332
  {
299
333
  "component": "Countdown",
300
- "description": "Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n`urgentBelowMs` it turns to the danger role and its last segment pulses (no motion under prefers-reduced-motion).\nWords come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\nannounces what matters. State: `data-urgent`, `data-ended`.\n\nThe danger role is large-text safe (≥ 3:1) from `size=\"md\"`; at `size=\"sm\"` an urgent countdown in light mode is\nunder the 4.5:1 of small text.",
334
+ "description": "Time left until a moment, as segments (2d 04h 13m 09s — days only when there are some) in tabular figures. Under\n`urgentBelowMs` it turns to the danger text role and its last segment pulses (no motion under prefers-reduced-motion).\nWords come from the app: `units` are the short unit labels. `role=\"timer\"` without live announcements — the app\nannounces what matters. State: `data-urgent`, `data-ended`.\n\nThe urgent colour is `--kz-danger-text` (≥ 4.5:1 on every surface in both themes), so `size=\"sm\"` is safe too.",
301
335
  "props": [
302
336
  {
303
337
  "name": "label",
@@ -327,11 +361,18 @@
327
361
  "description": "Classes merged last, over the primitive's.",
328
362
  "required": false
329
363
  },
364
+ {
365
+ "name": "showLabel",
366
+ "type": "boolean",
367
+ "default": "false",
368
+ "description": "Also show `label` before the segments, in the secondary text role (default: only read by screen readers).",
369
+ "required": false
370
+ },
330
371
  {
331
372
  "name": "size",
332
373
  "type": "\"sm\" | \"md\" | \"lg\"",
333
374
  "default": "md",
334
- "description": "sm · md · lg (the danger role is large-text safe from `md`).",
375
+ "description": "sm · md · lg — the danger text role reads at 4.5:1 at every size, in both themes.",
335
376
  "required": false
336
377
  },
337
378
  {
@@ -345,7 +386,7 @@
345
386
  "name": "urgentBelowMs",
346
387
  "type": "number",
347
388
  "default": "60000",
348
- "description": "Below this many milliseconds it turns urgent (`text-danger`, the last segment pulses). Default 60 s.",
389
+ "description": "Below this many milliseconds it turns urgent (`text-fg-danger`, the last segment pulses). Default 60 s.",
349
390
  "required": false
350
391
  }
351
392
  ]
@@ -110,7 +110,7 @@
110
110
  "files": [
111
111
  {
112
112
  "path": "dropdown-menu/dropdown-menu.tsx",
113
- "content": "// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The danger token is too light for small text on a light surface: the label stays a text role, the icon and the\n // highlight carry the danger.\n tone: { default: {}, danger: { item: \"data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n};\n"
113
+ "content": "// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n};\n"
114
114
  },
115
115
  {
116
116
  "path": "dropdown-menu/index.ts",
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ // registry/examples/button/gradient.tsx
4
+ import { Button } from "@krizaka/ui/button";
5
+ import { jsx } from "react/jsx-runtime";
6
+ function ButtonGradient() {
7
+ return /* @__PURE__ */ jsx(Button, { variant: "gradient", children: "Start free" });
8
+ }
9
+ export {
10
+ ButtonGradient as default
11
+ };
@@ -0,0 +1,5 @@
1
+ import { Button } from "@krizaka/ui/button";
2
+
3
+ export default function ButtonGradient() {
4
+ return <Button variant="gradient">Start free</Button>;
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+
3
+ // registry/examples/button/native/gradient.tsx
4
+ import { Button } from "@krizaka/ui/native";
5
+ import { jsx } from "react/jsx-runtime";
6
+ function NativeButtonGradient() {
7
+ return /* @__PURE__ */ jsx(Button, { variant: "gradient", label: "Start free", onPress: () => {
8
+ } });
9
+ }
10
+ export {
11
+ NativeButtonGradient as default
12
+ };
@@ -0,0 +1,5 @@
1
+ import { Button } from "@krizaka/ui/native";
2
+
3
+ export default function NativeButtonGradient() {
4
+ return <Button variant="gradient" label="Start free" onPress={() => {}} />;
5
+ }
@@ -22,8 +22,7 @@ function CommandDialogExample({ defaultOpen = false }) {
22
22
  footer: /* @__PURE__ */ jsx("p", { className: "border-t border-border-subtle bg-surface-2 px-4 py-2.5 text-xs text-fg-secondary", children: "\u2191\u2193 to move, \u21B5 to open" }),
23
23
  children: [
24
24
  /* @__PURE__ */ jsx(Command.Input, { placeholder: "Search creators, videos, tags" }),
25
- /* @__PURE__ */ jsxs(Command.List, { label: "Results", children: [
26
- /* @__PURE__ */ jsx(Command.Empty, { emptyLabel: "Nothing matches." }),
25
+ /* @__PURE__ */ jsxs(Command.List, { label: "Results", emptyLabel: "Nothing matches.", children: [
27
26
  /* @__PURE__ */ jsxs(Command.Group, { heading: "Pages", children: [
28
27
  /* @__PURE__ */ jsx(Command.Item, { onSelect: () => setOpen(false), children: "Home" }),
29
28
  /* @__PURE__ */ jsx(Command.Item, { onSelect: () => setOpen(false), children: "Wallet" })
@@ -20,8 +20,7 @@ export default function CommandDialogExample({ defaultOpen = false }: { defaultO
20
20
  footer={<p className="border-t border-border-subtle bg-surface-2 px-4 py-2.5 text-xs text-fg-secondary">↑↓ to move, ↵ to open</p>}
21
21
  >
22
22
  <Command.Input placeholder="Search creators, videos, tags" />
23
- <Command.List label="Results">
24
- <Command.Empty emptyLabel="Nothing matches." />
23
+ <Command.List label="Results" emptyLabel="Nothing matches.">
25
24
  <Command.Group heading="Pages">
26
25
  <Command.Item onSelect={() => setOpen(false)}>Home</Command.Item>
27
26
  <Command.Item onSelect={() => setOpen(false)}>Wallet</Command.Item>
@@ -6,8 +6,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  function CommandInline() {
7
7
  return /* @__PURE__ */ jsxs(Command.Root, { label: "Commands", className: "w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg", children: [
8
8
  /* @__PURE__ */ jsx(Command.Input, { placeholder: "Type a command or search" }),
9
- /* @__PURE__ */ jsxs(Command.List, { label: "Suggestions", children: [
10
- /* @__PURE__ */ jsx(Command.Empty, { emptyLabel: "Nothing matches." }),
9
+ /* @__PURE__ */ jsxs(Command.List, { label: "Suggestions", emptyLabel: "Nothing matches.", children: [
11
10
  /* @__PURE__ */ jsxs(Command.Group, { heading: "Pages", children: [
12
11
  /* @__PURE__ */ jsxs(Command.Item, { children: [
13
12
  "Home",
@@ -4,8 +4,7 @@ export default function CommandInline() {
4
4
  return (
5
5
  <Command.Root label="Commands" className="w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg">
6
6
  <Command.Input placeholder="Type a command or search" />
7
- <Command.List label="Suggestions">
8
- <Command.Empty emptyLabel="Nothing matches." />
7
+ <Command.List label="Suggestions" emptyLabel="Nothing matches.">
9
8
  <Command.Group heading="Pages">
10
9
  <Command.Item>
11
10
  Home
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ // registry/examples/command/no-results.tsx
4
+ import { Command } from "@krizaka/ui/command";
5
+ import { useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function CommandNoResults() {
8
+ const [query, setQuery] = useState("zzz");
9
+ return /* @__PURE__ */ jsxs(Command.Root, { label: "Commands", className: "w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg", children: [
10
+ /* @__PURE__ */ jsx(Command.Input, { placeholder: "Type a command or search", value: query, onValueChange: setQuery }),
11
+ /* @__PURE__ */ jsx(Command.List, { label: "Suggestions", emptyLabel: `Nothing matches \u201C${query}\u201D.`, children: /* @__PURE__ */ jsxs(Command.Group, { heading: "Pages", children: [
12
+ /* @__PURE__ */ jsx(Command.Item, { children: "Home" }),
13
+ /* @__PURE__ */ jsx(Command.Item, { children: "Wallet" })
14
+ ] }) })
15
+ ] });
16
+ }
17
+ export {
18
+ CommandNoResults as default
19
+ };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import { Command } from "@krizaka/ui/command";
4
+ import { useState } from "react";
5
+
6
+ // Nothing matches: `emptyLabel` is said beside the results, the listbox stays empty (valid for screen readers).
7
+ export default function CommandNoResults() {
8
+ const [query, setQuery] = useState("zzz");
9
+ return (
10
+ <Command.Root label="Commands" className="w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg">
11
+ <Command.Input placeholder="Type a command or search" value={query} onValueChange={setQuery} />
12
+ <Command.List label="Suggestions" emptyLabel={`Nothing matches “${query}”.`}>
13
+ <Command.Group heading="Pages">
14
+ <Command.Item>Home</Command.Item>
15
+ <Command.Item>Wallet</Command.Item>
16
+ </Command.Group>
17
+ </Command.List>
18
+ </Command.Root>
19
+ );
20
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;