@krizaka/ui 2.0.0-beta.6 → 2.0.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 (67) hide show
  1. package/README.md +7 -6
  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/native.d.ts +20 -8
  26. package/dist/native.js +129 -85
  27. package/dist/native.js.map +1 -1
  28. package/dist/theme.d.ts +1 -1
  29. package/dist/theme.js +1 -1
  30. package/package.json +3 -3
  31. package/registry/avatar.json +20 -5
  32. package/registry/button.json +36 -15
  33. package/registry/card.json +17 -2
  34. package/registry/command.json +30 -8
  35. package/registry/confirm-button.json +4 -4
  36. package/registry/countdown.json +53 -12
  37. package/registry/dropdown-menu.json +1 -1
  38. package/registry/examples/button/gradient.d.ts +4 -0
  39. package/registry/examples/button/gradient.js +11 -0
  40. package/registry/examples/button/gradient.tsx +5 -0
  41. package/registry/examples/button/native/gradient.d.ts +4 -0
  42. package/registry/examples/button/native/gradient.dark.png +0 -0
  43. package/registry/examples/button/native/gradient.js +12 -0
  44. package/registry/examples/button/native/gradient.light.png +0 -0
  45. package/registry/examples/button/native/gradient.tsx +5 -0
  46. package/registry/examples/command/dialog.js +1 -2
  47. package/registry/examples/command/dialog.tsx +1 -2
  48. package/registry/examples/command/inline.js +1 -2
  49. package/registry/examples/command/inline.tsx +1 -2
  50. package/registry/examples/command/no-results.d.ts +4 -0
  51. package/registry/examples/command/no-results.js +19 -0
  52. package/registry/examples/command/no-results.tsx +20 -0
  53. package/registry/examples/countdown/native/urgent.dark.png +0 -0
  54. package/registry/examples/countdown/native/urgent.light.png +0 -0
  55. package/registry/examples/countdown/native/with-label.d.ts +4 -0
  56. package/registry/examples/countdown/native/with-label.dark.png +0 -0
  57. package/registry/examples/countdown/native/with-label.js +13 -0
  58. package/registry/examples/countdown/native/with-label.light.png +0 -0
  59. package/registry/examples/countdown/native/with-label.tsx +7 -0
  60. package/registry/examples/countdown/with-label.d.ts +4 -0
  61. package/registry/examples/countdown/with-label.js +13 -0
  62. package/registry/examples/countdown/with-label.tsx +10 -0
  63. package/registry/field.json +1 -1
  64. package/registry/index.json +8 -8
  65. package/registry/theme.json +2 -2
  66. package/dist/chunk-6DJ6SQY3.js.map +0 -1
  67. /package/dist/{chunk-IUGBKMJC.js.map → chunk-FHYZSO2S.js.map} +0 -0
@@ -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;
@@ -0,0 +1,13 @@
1
+ "use client";
2
+
3
+ // registry/examples/countdown/native/with-label.tsx
4
+ import { Countdown } from "@krizaka/ui/native";
5
+ import { useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ function NativeCountdownWithLabel() {
8
+ const [target] = useState(() => Date.now() + 3723e3);
9
+ return /* @__PURE__ */ jsx(Countdown, { target, units: { d: "d", h: "h", m: "m", s: "s" }, label: "Ends in", showLabel: true });
10
+ }
11
+ export {
12
+ NativeCountdownWithLabel as default
13
+ };
@@ -0,0 +1,7 @@
1
+ import { Countdown } from "@krizaka/ui/native";
2
+ import { useState } from "react";
3
+
4
+ export default function NativeCountdownWithLabel() {
5
+ const [target] = useState(() => Date.now() + 3_723_000);
6
+ return <Countdown target={target} units={{ d: "d", h: "h", m: "m", s: "s" }} label="Ends in" showLabel />;
7
+ }
@@ -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,13 @@
1
+ "use client";
2
+
3
+ // registry/examples/countdown/with-label.tsx
4
+ import { Countdown } from "@krizaka/ui/countdown";
5
+ import { useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ function CountdownWithLabel() {
8
+ const [target] = useState(() => Date.now() + 3723e3);
9
+ return /* @__PURE__ */ jsx(Countdown, { target, units: { d: "d", h: "h", m: "m", s: "s" }, label: "Ends in", showLabel: true });
10
+ }
11
+ export {
12
+ CountdownWithLabel as default
13
+ };
@@ -0,0 +1,10 @@
1
+ "use client";
2
+
3
+ import { Countdown } from "@krizaka/ui/countdown";
4
+ import { useState } from "react";
5
+
6
+ // `showLabel` writes the label before the segments (it stays the accessible name).
7
+ export default function CountdownWithLabel() {
8
+ const [target] = useState(() => Date.now() + 3_723_000);
9
+ return <Countdown target={target} units={{ d: "d", h: "h", m: "m", s: "s" }} label="Ends in" showLabel />;
10
+ }
@@ -135,7 +135,7 @@
135
135
  "files": [
136
136
  {
137
137
  "path": "field/field.tsx",
138
- "content": "// Server-safe. The label ↔ control wiring is HTML (htmlFor / id, aria-describedby): no context.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\n\nexport const field = tv({\n slots: {\n root: \"flex flex-col gap-1.5\",\n label: \"text-xs font-semibold text-fg-secondary\",\n control:\n \"flex h-11 w-full rounded-lg border border-border-default bg-surface-2 px-3 text-sm text-fg placeholder:text-fg-muted \" +\n \"transition-[border-color,box-shadow] focus-visible:border-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-danger/30\",\n hint: \"text-xs text-fg-secondary\",\n // The danger token is too light for small text on a light surface: the message is a text role, the dot says danger.\n error: \"flex items-center gap-1.5 text-xs font-medium text-fg before:h-1.5 before:w-1.5 before:shrink-0 before:rounded-full before:bg-danger\",\n selectWrap: \"relative flex w-full\",\n chevron: \"pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-fg-secondary\",\n },\n});\nconst s = field();\n\nexport const Field = {\n Root: ({ className, ...props }: React.ComponentProps<\"div\">) => <div className={s.root({ className })} {...props} />,\n Label: ({ className, ...props }: React.ComponentProps<\"label\">) => <label className={s.label({ className })} {...props} />,\n Hint: ({ className, ...props }: React.ComponentProps<\"p\">) => <p className={s.hint({ className })} {...props} />,\n Error: ({ className, ...props }: React.ComponentProps<\"p\">) => <p role=\"alert\" className={s.error({ className })} {...props} />,\n};\n\ntype ControlProps = {\n /** Marks the control invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby). */\n invalid?: boolean;\n};\n\nconst invalidProps = (invalid?: boolean) => (invalid ? { \"aria-invalid\": true as const, \"data-invalid\": \"\" } : {});\n\nexport function Input({ invalid, className, ...props }: React.ComponentProps<\"input\"> & ControlProps) {\n return <input {...invalidProps(invalid)} className={s.control({ className })} {...props} />;\n}\n\nexport function Textarea({ invalid, className, ...props }: React.ComponentProps<\"textarea\"> & ControlProps) {\n return <textarea {...invalidProps(invalid)} className={s.control({ className: cn(\"h-auto min-h-24 py-2\", className) })} {...props} />;\n}\n\n/** A native <select>, styled like the other controls (the platform's menu: accessible and right on phones). */\nexport function Select({ invalid, className, ...props }: React.ComponentProps<\"select\"> & ControlProps) {\n return (\n <span className={s.selectWrap()}>\n <select {...invalidProps(invalid)} className={s.control({ className: cn(\"appearance-none pr-9\", className) })} {...props} />\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={s.chevron()}>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n );\n}\n"
138
+ "content": "// Server-safe. The label ↔ control wiring is HTML (htmlFor / id, aria-describedby): no context.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\n\nexport const field = tv({\n slots: {\n root: \"flex flex-col gap-1.5\",\n label: \"text-xs font-semibold text-fg-secondary\",\n control:\n \"flex h-11 w-full rounded-lg border border-border-default bg-surface-2 px-3 text-sm text-fg placeholder:text-fg-muted \" +\n \"transition-[border-color,box-shadow] focus-visible:border-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-danger/30\",\n hint: \"text-xs text-fg-secondary\",\n // The message reads in the danger text role (≥ 4.5:1 on every surface, both themes); the dot is the danger fill.\n error: \"flex items-center gap-1.5 text-xs font-medium text-fg-danger before:h-1.5 before:w-1.5 before:shrink-0 before:rounded-full before:bg-danger\",\n selectWrap: \"relative flex w-full\",\n chevron: \"pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-fg-secondary\",\n },\n});\nconst s = field();\n\nexport const Field = {\n Root: ({ className, ...props }: React.ComponentProps<\"div\">) => <div className={s.root({ className })} {...props} />,\n Label: ({ className, ...props }: React.ComponentProps<\"label\">) => <label className={s.label({ className })} {...props} />,\n Hint: ({ className, ...props }: React.ComponentProps<\"p\">) => <p className={s.hint({ className })} {...props} />,\n Error: ({ className, ...props }: React.ComponentProps<\"p\">) => <p role=\"alert\" className={s.error({ className })} {...props} />,\n};\n\ntype ControlProps = {\n /** Marks the control invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby). */\n invalid?: boolean;\n};\n\nconst invalidProps = (invalid?: boolean) => (invalid ? { \"aria-invalid\": true as const, \"data-invalid\": \"\" } : {});\n\nexport function Input({ invalid, className, ...props }: React.ComponentProps<\"input\"> & ControlProps) {\n return <input {...invalidProps(invalid)} className={s.control({ className })} {...props} />;\n}\n\nexport function Textarea({ invalid, className, ...props }: React.ComponentProps<\"textarea\"> & ControlProps) {\n return <textarea {...invalidProps(invalid)} className={s.control({ className: cn(\"h-auto min-h-24 py-2\", className) })} {...props} />;\n}\n\n/** A native <select>, styled like the other controls (the platform's menu: accessible and right on phones). */\nexport function Select({ invalid, className, ...props }: React.ComponentProps<\"select\"> & ControlProps) {\n return (\n <span className={s.selectWrap()}>\n <select {...invalidProps(invalid)} className={s.control({ className: cn(\"appearance-none pr-9\", className) })} {...props} />\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={s.chevron()}>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n );\n}\n"
139
139
  },
140
140
  {
141
141
  "path": "field/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "2.0.0-beta.6",
3
+ "version": "2.0.0",
4
4
  "items": [
5
5
  {
6
6
  "name": "alert",
@@ -61,14 +61,14 @@
61
61
  "name": "button",
62
62
  "type": "primitive",
63
63
  "title": "Button",
64
- "summary": "The action: a button in five looks (primary, secondary, outline, ghost, danger), three sizes and two shapes.",
65
- "description": "The action: a button in five looks (primary, secondary, outline, ghost, danger), three sizes and two shapes.",
64
+ "summary": "The action: a button in six looks (primary, gradient, secondary, outline, ghost, danger), three sizes and two shapes.",
65
+ "description": "The action: a button in six looks (primary, gradient, secondary, outline, ghost, danger), three sizes and two shapes.",
66
66
  "status": "stable",
67
67
  "category": "actions",
68
68
  "platforms": "both",
69
69
  "examples": {
70
- "web": 11,
71
- "native": 10
70
+ "web": 12,
71
+ "native": 11
72
72
  },
73
73
  "dependencies": [
74
74
  "radix-ui@^1.7.0",
@@ -164,7 +164,7 @@
164
164
  "category": "navigation",
165
165
  "platforms": "web",
166
166
  "examples": {
167
- "web": 2,
167
+ "web": 3,
168
168
  "native": 0
169
169
  },
170
170
  "dependencies": [
@@ -208,8 +208,8 @@
208
208
  "category": "data-display",
209
209
  "platforms": "both",
210
210
  "examples": {
211
- "web": 5,
212
- "native": 4
211
+ "web": 6,
212
+ "native": 5
213
213
  },
214
214
  "dependencies": [
215
215
  "tailwind-variants@^3.3.1"
@@ -91,7 +91,7 @@
91
91
  },
92
92
  {
93
93
  "name": "variant",
94
- "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
94
+ "type": "\"danger\" | \"primary\" | \"gradient\" | \"secondary\" | \"outline\" | \"ghost\"",
95
95
  "default": "ghost",
96
96
  "description": "Any `Button` variant.",
97
97
  "required": false
@@ -253,7 +253,7 @@
253
253
  },
254
254
  {
255
255
  "name": "variant",
256
- "type": "\"danger\" | \"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
256
+ "type": "\"danger\" | \"primary\" | \"gradient\" | \"secondary\" | \"outline\" | \"ghost\"",
257
257
  "default": "ghost",
258
258
  "description": "Any `Button` variant.",
259
259
  "required": false
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/button/button.tsx"],"sourcesContent":["// Server-safe: no hook, no context. `buttonVariants` styles a <Link> or a Server Component without the component.\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const buttonVariants = tv({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 \" +\n \"disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]\",\n variants: {\n variant: {\n primary: \"bg-accent text-on-accent shadow-sm hover:bg-accent-hover\",\n secondary: \"border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3\",\n outline: \"border border-border-default bg-transparent text-fg hover:border-accent hover:text-fg-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The 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"],"mappings":";;;AACA,SAAS,YAAY;AAErB,SAAS,UAA6B;AAmDlC;AAjDG,IAAM,iBAAiB,GAAG;AAAA,EAC/B,MACE;AAAA,EAGF,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA;AAAA;AAAA,MAGP,QAAQ;AAAA,IACV;AAAA,IACA,MAAM,EAAE,IAAI,oBAAoB,IAAI,qBAAqB,IAAI,qBAAqB,MAAM,YAAY;AAAA,IACpG,OAAO,EAAE,SAAS,cAAc,MAAM,eAAe;AAAA,EACvD;AAAA,EACA,iBAAiB,EAAE,SAAS,aAAa,MAAM,MAAM,OAAO,UAAU;AACxE,CAAC;AA2BM,SAAS,OAAO,EAAE,SAAS,SAAS,SAAS,MAAM,OAAO,WAAW,UAAU,GAAG,MAAM,GAAgB;AAC7G,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,UAAU,SAAY;AAAA,MAC5B,aAAW,WAAW;AAAA,MACtB,gBAAc,UAAU,KAAK;AAAA,MAC7B,gBAAc,WAAW;AAAA,MACzB,UAAU,UAAU,SAAY,YAAY;AAAA,MAC5C,iBAAe,YAAY,YAAY,WAAW,OAAO;AAAA,MACzD,WAAW,eAAe,EAAE,SAAS,MAAM,OAAO,UAAU,CAAC;AAAA,MAC5D,GAAG;AAAA;AAAA,EACN;AAEJ;AAaO,SAAS,WAAW,EAAE,OAAO,OAAO,QAAQ,GAAG,MAAM,GAAoB;AAC9E,SAAO,oBAAC,UAAO,cAAY,OAAO,OAAO,OAAO,MAAa,GAAG,OAAO;AACzE;","names":[]}