@krizaka/ui 2.0.0-beta.0 → 2.0.0-beta.1

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.
package/README.md CHANGED
@@ -27,7 +27,8 @@ is one CSS file. The primitives: **React 19** and Tailwind CSS v4 with the Kriza
27
27
 
28
28
  ## Primitives
29
29
 
30
- > 2.0 beta: the first layer (atoms). Cards, dialogs, tabs, toasts and menus follow in the next betas.
30
+ > 2.0 beta: the atoms, then the structure of a page and what opens over it (cards, dialogs, sheets, toasts, popovers,
31
+ > menus, tooltips). Tabs, chips, switches and the command palette follow in the next betas.
31
32
 
32
33
  ### Install
33
34
 
@@ -47,6 +48,9 @@ import { Button, IconButton } from "@krizaka/ui/button";
47
48
  import { Badge } from "@krizaka/ui/badge";
48
49
  import { Field, Input } from "@krizaka/ui/field";
49
50
  import { ThemeProvider, ThemeScript, ThemeToggle } from "@krizaka/ui/theme";
51
+ import { Card } from "@krizaka/ui/card";
52
+ import { AlertDialog, Dialog } from "@krizaka/ui/dialog";
53
+ import { toast, Toaster } from "@krizaka/ui/toast";
50
54
 
51
55
  <Button variant="primary" loading={saving}>{t("save")}</Button>
52
56
  <Button asChild variant="outline"><Link href="/docs">{t("docs")}</Link></Button>
@@ -56,6 +60,27 @@ import { ThemeProvider, ThemeScript, ThemeToggle } from "@krizaka/ui/theme";
56
60
  <Input id="email" invalid={!!error} aria-describedby="email-error" />
57
61
  {error && <Field.Error id="email-error">{error}</Field.Error>}
58
62
  </Field.Root>
63
+
64
+ <Card.Root asChild interactive reveal={index}>
65
+ <Link href={href}>
66
+ <Card.Media><Card.Image src={cover} fallback={<PlayIcon />} /></Card.Media>
67
+ <Card.Body><Card.Title>{title}</Card.Title></Card.Body>
68
+ </Link>
69
+ </Card.Root>
70
+
71
+ <Dialog.Root>
72
+ <Dialog.Trigger asChild><Button>{t("edit")}</Button></Dialog.Trigger>
73
+ <Dialog.Content closeLabel={t("close")} placement="bottom">
74
+ <Dialog.Header><Dialog.Title>{t("editTitle")}</Dialog.Title></Dialog.Header>
75
+ <Dialog.Body>…</Dialog.Body>
76
+ </Dialog.Content>
77
+ </Dialog.Root>
78
+
79
+ <AlertDialog tone="danger" trigger={<Button variant="danger">{t("delete")}</Button>} title={t("deleteTitle")}
80
+ confirmLabel={t("delete")} cancelLabel={t("cancel")} onConfirm={() => deleteVideo(id)} />
81
+
82
+ // once, in the root layout — then toast.success(t("saved")) anywhere on the client
83
+ <Toaster label={t("notifications")} closeLabel={t("dismiss")} />
59
84
  ```
60
85
 
61
86
  **React 19 is required by the primitives** (`ref` is a prop, no `forwardRef`). The package keeps `react >= 18` as its
@@ -64,7 +89,8 @@ peer range, because the marks and the motion still run on React 18: one `package
64
89
 
65
90
  ### The component contract (12 points)
66
91
 
67
- 1. **Composed when there is structure** (`Field.*`, `Avatar.Group`), **flat for an atom** (`Button`, `Badge`, `Input`).
92
+ 1. **Composed when there is structure** (`Card.*`, `Dialog.*`, `Field.*`, `Avatar.Group`), **flat for an atom**
93
+ (`Button`, `Badge`, `Input`, `Stat`).
68
94
  2. **Variants exported next to the component** (`buttonVariants`, `badgeVariants`): a `<Link>` or a Server Component
69
95
  is styled without importing the component.
70
96
  3. **`asChild`** (Radix `Slot`) rather than `as`: props, `className` and `ref` are merged into the child.
@@ -74,7 +100,8 @@ peer range, because the marks and the motion still run on React 18: one `package
74
100
  6. **Controlled and uncontrolled**: `value`/`defaultValue` + `onValueChange`, `open`/`defaultOpen` + `onOpenChange`.
75
101
  7. **State is exposed as `data-*`** (`data-tone`, `data-loading`, `data-invalid`, `data-urgent`, `data-mode`): a
76
102
  product styles a state without a new variant.
77
- 8. **Server by default**: `"use client"` only on the entries with a hook or a portal (`avatar`, `theme`, `countdown`).
103
+ 8. **Server by default**: `"use client"` only on the entries with a hook or a portal (`avatar`, `theme`, `countdown`,
104
+ `dialog`, `toast`, `popover`, `dropdown-menu`, `tooltip`).
78
105
  One build entry per component; the directive is kept per entry.
79
106
  9. **React 19 minimum for the primitives**; the marks stay `react >= 18`.
80
107
  10. **Accessibility is delegated**: non-trivial interactions on Radix UI (`radix-ui`), the command palette on cmdk,
@@ -101,6 +128,17 @@ Colours are roles of the preset (`bg-surface-2`, `text-fg-secondary`, `border-bo
101
128
  | `@krizaka/ui/empty-state` | `EmptyState` (`icon`, `title`, `description`, `action`) | server |
102
129
  | `@krizaka/ui/spinner` | `Spinner` (`label`, `size`) | server |
103
130
  | `@krizaka/ui/countdown` | `Countdown` (`target`, `label`, `units`, `size`, `urgentBelowMs`, `skewMs`), `useCountdown`, `splitDuration` | client |
131
+ | `@krizaka/ui/card` | `Card.Root` (`asChild`, `reveal`, `radius` md·lg·xl, `tone` default·elevated·glass, `interactive`), `Card.Media` (`aspect` video·square·portrait·auto), `Card.Image` (`fallback`), `Card.Overlay` (`corner`), `Card.Body` (`padding`), `Card.Title` (`as`), `Card.Description`, `Card.Stat` (`label`), `Card.Footer`; `card` (the slots) | server |
132
+ | `@krizaka/ui/dialog` | `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close` (`placement` center·bottom·right, `size` sm·md·lg, `closeLabel` required), `Sheet` (= `placement="bottom"`), `AlertDialog` (`title`, `description`, `confirmLabel`, `cancelLabel`, `tone` danger·primary, async `onConfirm`, `trigger`) | client |
133
+ | `@krizaka/ui/toast` | `Toaster` (sonner styled by roles: `label`, `closeLabel`, `position`, `closeButton`; `richColors` off), `toast` (`.success/.warning/.error/.info/.custom`…) | client |
134
+ | `@krizaka/ui/popover` | `Popover.Root/Trigger/Anchor/Content/Close` | client |
135
+ | `@krizaka/ui/dropdown-menu` | `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group` (`Item` `tone` default·danger), `menu` (the slots) | client |
136
+ | `@krizaka/ui/tooltip` | `Tooltip` (`content`, `delayDuration` 300, `side`, `open`/`defaultOpen`), `TooltipProvider` | client |
137
+ | `@krizaka/ui/stat` | `Stat` (`label`, `value`, `hint`, `trend` up·down·flat, `trendLabel`) | server |
138
+ | `@krizaka/ui/page-header` | `PageHeader` (`title`, `description`, `actions`, `breadcrumb`, `as` h1·h2) | server |
139
+ | `@krizaka/ui/alert` | `Alert` (`tone` info·success·warning·danger, `icon`, `title`, `action`; `role="alert"` for danger and warning, `status` otherwise), `alertVariants` | server |
140
+ | `@krizaka/ui/separator` | `Separator` (`orientation`, `decorative`, Radix), `separatorVariants` | server |
141
+ | `@krizaka/ui/kbd` | `Kbd` (`size` sm·md), `kbdVariants` | server |
104
142
  | `@krizaka/ui` | the marks, `MotionObserver`, `RotatingWord`, `cx` (unchanged) | client |
105
143
  | `@krizaka/ui/native` | the marks for React Native (unchanged) | native |
106
144
  | `@krizaka/ui/motion.css`, `@krizaka/ui/tailwind.css` | the motion signature; the `@source` of the primitives' classes | CSS |
@@ -0,0 +1,95 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import { VariantProps } from 'tailwind-variants';
3
+ import * as React from 'react';
4
+
5
+ declare const alertVariants: tailwind_variants.TVReturnType<{
6
+ tone: {
7
+ info: {
8
+ root: string;
9
+ icon: string;
10
+ };
11
+ success: {
12
+ root: string;
13
+ icon: string;
14
+ };
15
+ warning: {
16
+ root: string;
17
+ icon: string;
18
+ };
19
+ danger: {
20
+ root: string;
21
+ icon: string;
22
+ };
23
+ };
24
+ }, {
25
+ root: string;
26
+ icon: string;
27
+ content: string;
28
+ title: string;
29
+ description: string;
30
+ action: string;
31
+ }, undefined, {
32
+ tone: {
33
+ info: {
34
+ root: string;
35
+ icon: string;
36
+ };
37
+ success: {
38
+ root: string;
39
+ icon: string;
40
+ };
41
+ warning: {
42
+ root: string;
43
+ icon: string;
44
+ };
45
+ danger: {
46
+ root: string;
47
+ icon: string;
48
+ };
49
+ };
50
+ }, {
51
+ root: string;
52
+ icon: string;
53
+ content: string;
54
+ title: string;
55
+ description: string;
56
+ action: string;
57
+ }, tailwind_variants.TVReturnTypeLike<{
58
+ tone: {
59
+ info: {
60
+ root: string;
61
+ icon: string;
62
+ };
63
+ success: {
64
+ root: string;
65
+ icon: string;
66
+ };
67
+ warning: {
68
+ root: string;
69
+ icon: string;
70
+ };
71
+ danger: {
72
+ root: string;
73
+ icon: string;
74
+ };
75
+ };
76
+ }, {
77
+ root: string;
78
+ icon: string;
79
+ content: string;
80
+ title: string;
81
+ description: string;
82
+ action: string;
83
+ }>>;
84
+ type AlertTone = NonNullable<VariantProps<typeof alertVariants>["tone"]>;
85
+ type AlertProps = Omit<React.ComponentProps<"div">, "title"> & {
86
+ tone?: AlertTone;
87
+ /** A decorative icon (hidden from assistive technology). */
88
+ icon?: React.ReactNode;
89
+ title?: React.ReactNode;
90
+ /** What to do: a Button, a link. */
91
+ action?: React.ReactNode;
92
+ };
93
+ declare function Alert({ tone, icon, title, action, className, children, ...props }: AlertProps): React.JSX.Element;
94
+
95
+ export { Alert, type AlertProps, type AlertTone, alertVariants };
package/dist/alert.js ADDED
@@ -0,0 +1,38 @@
1
+ // src/alert/alert.tsx
2
+ import { tv } from "tailwind-variants";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ var alertVariants = tv({
5
+ slots: {
6
+ root: "relative flex w-full items-start gap-3 rounded-xl border p-4 text-sm text-fg",
7
+ icon: "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:h-5 [&>svg]:w-5",
8
+ content: "flex min-w-0 flex-1 flex-col gap-1",
9
+ title: "font-semibold text-fg",
10
+ description: "text-fg-secondary",
11
+ action: "flex shrink-0 items-center gap-2 self-center"
12
+ },
13
+ variants: {
14
+ tone: {
15
+ info: { root: "border-info/40 bg-info/10", icon: "text-info" },
16
+ success: { root: "border-success/40 bg-success/10", icon: "text-success" },
17
+ warning: { root: "border-warning/50 bg-warning/10", icon: "text-warning" },
18
+ danger: { root: "border-danger/50 bg-danger/10", icon: "text-danger" }
19
+ }
20
+ },
21
+ defaultVariants: { tone: "info" }
22
+ });
23
+ function Alert({ tone = "info", icon, title, action, className, children, ...props }) {
24
+ const s = alertVariants({ tone });
25
+ return /* @__PURE__ */ jsxs("div", { role: tone === "danger" || tone === "warning" ? "alert" : "status", "data-tone": tone, className: s.root({ className }), ...props, children: [
26
+ icon && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: s.icon(), children: icon }),
27
+ /* @__PURE__ */ jsxs("div", { className: s.content(), children: [
28
+ title && /* @__PURE__ */ jsx("p", { className: s.title(), children: title }),
29
+ children && /* @__PURE__ */ jsx("div", { className: s.description(), children })
30
+ ] }),
31
+ action && /* @__PURE__ */ jsx("div", { className: s.action(), children: action })
32
+ ] });
33
+ }
34
+ export {
35
+ Alert,
36
+ alertVariants
37
+ };
38
+ //# sourceMappingURL=alert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/alert/alert.tsx"],"sourcesContent":["// Server-safe. A message in the flow of the page (not a toast): a tone, an icon, a title, a text, an action.\n// danger and warning are announced at once (role=\"alert\"); info and success politely (role=\"status\").\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\n// Soft tones (a tint, a tinted border, a coloured icon): the text stays a text role — the invariant status tokens are\n// too light for small text on a light surface (WCAG AA).\nexport const alertVariants = tv({\n slots: {\n root: \"relative flex w-full items-start gap-3 rounded-xl border p-4 text-sm text-fg\",\n icon: \"mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center [&>svg]:h-5 [&>svg]:w-5\",\n content: \"flex min-w-0 flex-1 flex-col gap-1\",\n title: \"font-semibold text-fg\",\n description: \"text-fg-secondary\",\n action: \"flex shrink-0 items-center gap-2 self-center\",\n },\n variants: {\n tone: {\n info: { root: \"border-info/40 bg-info/10\", icon: \"text-info\" },\n success: { root: \"border-success/40 bg-success/10\", icon: \"text-success\" },\n warning: { root: \"border-warning/50 bg-warning/10\", icon: \"text-warning\" },\n danger: { root: \"border-danger/50 bg-danger/10\", icon: \"text-danger\" },\n },\n },\n defaultVariants: { tone: \"info\" },\n});\n\nexport type AlertTone = NonNullable<VariantProps<typeof alertVariants>[\"tone\"]>;\n\nexport type AlertProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n tone?: AlertTone;\n /** A decorative icon (hidden from assistive technology). */\n icon?: React.ReactNode;\n title?: React.ReactNode;\n /** What to do: a Button, a link. */\n action?: React.ReactNode;\n};\n\nexport function Alert({ tone = \"info\", icon, title, action, className, children, ...props }: AlertProps) {\n const s = alertVariants({ tone });\n return (\n <div role={tone === \"danger\" || tone === \"warning\" ? \"alert\" : \"status\"} data-tone={tone} className={s.root({ className })} {...props}>\n {icon && (\n <span aria-hidden className={s.icon()}>\n {icon}\n </span>\n )}\n <div className={s.content()}>\n {title && <p className={s.title()}>{title}</p>}\n {children && <div className={s.description()}>{children}</div>}\n </div>\n {action && <div className={s.action()}>{action}</div>}\n </div>\n );\n}\n"],"mappings":";AAGA,SAAS,UAA6B;AAwC9B,cAIF,YAJE;AApCD,IAAM,gBAAgB,GAAG;AAAA,EAC9B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,QAAQ;AAAA,EACV;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,MAAM,EAAE,MAAM,6BAA6B,MAAM,YAAY;AAAA,MAC7D,SAAS,EAAE,MAAM,mCAAmC,MAAM,eAAe;AAAA,MACzE,SAAS,EAAE,MAAM,mCAAmC,MAAM,eAAe;AAAA,MACzE,QAAQ,EAAE,MAAM,iCAAiC,MAAM,cAAc;AAAA,IACvE;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,OAAO;AAClC,CAAC;AAaM,SAAS,MAAM,EAAE,OAAO,QAAQ,MAAM,OAAO,QAAQ,WAAW,UAAU,GAAG,MAAM,GAAe;AACvG,QAAM,IAAI,cAAc,EAAE,KAAK,CAAC;AAChC,SACE,qBAAC,SAAI,MAAM,SAAS,YAAY,SAAS,YAAY,UAAU,UAAU,aAAW,MAAM,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC7H;AAAA,YACC,oBAAC,UAAK,eAAW,MAAC,WAAW,EAAE,KAAK,GACjC,gBACH;AAAA,IAEF,qBAAC,SAAI,WAAW,EAAE,QAAQ,GACvB;AAAA,eAAS,oBAAC,OAAE,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,MACzC,YAAY,oBAAC,SAAI,WAAW,EAAE,YAAY,GAAI,UAAS;AAAA,OAC1D;AAAA,IACC,UAAU,oBAAC,SAAI,WAAW,EAAE,OAAO,GAAI,kBAAO;AAAA,KACjD;AAEJ;","names":[]}
package/dist/card.d.ts ADDED
@@ -0,0 +1,227 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import { VariantProps } from 'tailwind-variants';
3
+ import * as React from 'react';
4
+
5
+ declare const card: tailwind_variants.TVReturnType<{
6
+ radius: {
7
+ md: {
8
+ root: string;
9
+ };
10
+ lg: {
11
+ root: string;
12
+ };
13
+ xl: {
14
+ root: string;
15
+ };
16
+ };
17
+ tone: {
18
+ default: {};
19
+ elevated: {
20
+ root: string;
21
+ };
22
+ glass: {
23
+ root: string;
24
+ };
25
+ };
26
+ interactive: {
27
+ true: {
28
+ root: string;
29
+ };
30
+ };
31
+ padding: {
32
+ none: {
33
+ body: string;
34
+ };
35
+ sm: {
36
+ body: string;
37
+ };
38
+ md: {
39
+ body: string;
40
+ };
41
+ lg: {
42
+ body: string;
43
+ };
44
+ };
45
+ }, {
46
+ root: string;
47
+ media: string;
48
+ image: string;
49
+ fallback: string;
50
+ overlay: string;
51
+ body: string;
52
+ title: string;
53
+ description: string;
54
+ stat: string;
55
+ statLabel: string;
56
+ statValue: string;
57
+ footer: string;
58
+ }, undefined, {
59
+ radius: {
60
+ md: {
61
+ root: string;
62
+ };
63
+ lg: {
64
+ root: string;
65
+ };
66
+ xl: {
67
+ root: string;
68
+ };
69
+ };
70
+ tone: {
71
+ default: {};
72
+ elevated: {
73
+ root: string;
74
+ };
75
+ glass: {
76
+ root: string;
77
+ };
78
+ };
79
+ interactive: {
80
+ true: {
81
+ root: string;
82
+ };
83
+ };
84
+ padding: {
85
+ none: {
86
+ body: string;
87
+ };
88
+ sm: {
89
+ body: string;
90
+ };
91
+ md: {
92
+ body: string;
93
+ };
94
+ lg: {
95
+ body: string;
96
+ };
97
+ };
98
+ }, {
99
+ root: string;
100
+ media: string;
101
+ image: string;
102
+ fallback: string;
103
+ overlay: string;
104
+ body: string;
105
+ title: string;
106
+ description: string;
107
+ stat: string;
108
+ statLabel: string;
109
+ statValue: string;
110
+ footer: string;
111
+ }, tailwind_variants.TVReturnTypeLike<{
112
+ radius: {
113
+ md: {
114
+ root: string;
115
+ };
116
+ lg: {
117
+ root: string;
118
+ };
119
+ xl: {
120
+ root: string;
121
+ };
122
+ };
123
+ tone: {
124
+ default: {};
125
+ elevated: {
126
+ root: string;
127
+ };
128
+ glass: {
129
+ root: string;
130
+ };
131
+ };
132
+ interactive: {
133
+ true: {
134
+ root: string;
135
+ };
136
+ };
137
+ padding: {
138
+ none: {
139
+ body: string;
140
+ };
141
+ sm: {
142
+ body: string;
143
+ };
144
+ md: {
145
+ body: string;
146
+ };
147
+ lg: {
148
+ body: string;
149
+ };
150
+ };
151
+ }, {
152
+ root: string;
153
+ media: string;
154
+ image: string;
155
+ fallback: string;
156
+ overlay: string;
157
+ body: string;
158
+ title: string;
159
+ description: string;
160
+ stat: string;
161
+ statLabel: string;
162
+ statValue: string;
163
+ footer: string;
164
+ }>>;
165
+ type CardVariants = VariantProps<typeof card>;
166
+ type CardRootProps = React.ComponentProps<"div"> & Pick<CardVariants, "radius" | "tone" | "interactive"> & {
167
+ /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */
168
+ asChild?: boolean;
169
+ /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */
170
+ reveal?: number;
171
+ };
172
+ declare function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps): React.JSX.Element;
173
+ declare const ASPECT: {
174
+ readonly video: "aspect-video";
175
+ readonly square: "aspect-square";
176
+ readonly portrait: "aspect-[3/4]";
177
+ readonly auto: "";
178
+ };
179
+ type CardMediaProps = React.ComponentProps<"div"> & {
180
+ aspect?: keyof typeof ASPECT;
181
+ };
182
+ declare function CardMedia({ aspect, className, ...props }: CardMediaProps): React.JSX.Element;
183
+ type CardImageProps = Omit<React.ComponentProps<"img">, "src"> & {
184
+ src?: string | null;
185
+ /** Shown when there is no image: an icon, an illustration (decorative). */
186
+ fallback?: React.ReactNode;
187
+ };
188
+ /** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */
189
+ declare function CardImage({ src, alt, fallback, className, ...props }: CardImageProps): React.JSX.Element;
190
+ declare const CORNER: {
191
+ readonly "top-left": "left-2.5 top-2.5";
192
+ readonly "top-right": "right-2.5 top-2.5";
193
+ readonly "bottom-left": "bottom-2.5 left-2.5";
194
+ readonly "bottom-right": "bottom-2.5 right-2.5";
195
+ };
196
+ type CardOverlayProps = React.ComponentProps<"div"> & {
197
+ corner?: keyof typeof CORNER;
198
+ };
199
+ /** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */
200
+ declare function CardOverlay({ corner, className, ...props }: CardOverlayProps): React.JSX.Element;
201
+ type CardBodyProps = React.ComponentProps<"div"> & Pick<CardVariants, "padding">;
202
+ declare function CardBody({ padding, className, ...props }: CardBodyProps): React.JSX.Element;
203
+ type CardTitleProps = React.ComponentProps<"h3"> & {
204
+ as?: "h2" | "h3" | "h4";
205
+ };
206
+ declare function CardTitle({ as: Tag, className, ...props }: CardTitleProps): React.JSX.Element;
207
+ declare function CardDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
208
+ type CardStatProps = React.ComponentProps<"div"> & {
209
+ label: React.ReactNode;
210
+ };
211
+ /** A figure put forward: the label in capitals, the value in tabular digits. */
212
+ declare function CardStat({ label, className, children, ...props }: CardStatProps): React.JSX.Element;
213
+ declare function CardFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
214
+ /** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */
215
+ declare const Card: {
216
+ Root: typeof CardRoot;
217
+ Media: typeof CardMedia;
218
+ Image: typeof CardImage;
219
+ Overlay: typeof CardOverlay;
220
+ Body: typeof CardBody;
221
+ Title: typeof CardTitle;
222
+ Description: typeof CardDescription;
223
+ Stat: typeof CardStat;
224
+ Footer: typeof CardFooter;
225
+ };
226
+
227
+ export { Card, CardBody, type CardBodyProps, CardDescription, CardFooter, CardImage, type CardImageProps, CardMedia, type CardMediaProps, CardOverlay, type CardOverlayProps, CardRoot, type CardRootProps, CardStat, type CardStatProps, CardTitle, type CardTitleProps, card };
package/dist/card.js ADDED
@@ -0,0 +1,108 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-CPMZS4XA.js";
4
+
5
+ // src/card/card.tsx
6
+ import { Slot } from "radix-ui";
7
+ import { tv } from "tailwind-variants";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var card = tv({
10
+ slots: {
11
+ root: "group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
12
+ media: "relative overflow-hidden bg-media",
13
+ image: "h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105",
14
+ fallback: "flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60",
15
+ overlay: "absolute z-10 flex items-center gap-1.5",
16
+ body: "relative flex flex-1 flex-col gap-3",
17
+ title: "line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-accent",
18
+ description: "line-clamp-2 text-xs text-fg-secondary",
19
+ stat: "min-w-0",
20
+ statLabel: "text-[10px] font-semibold uppercase tracking-wider text-fg-secondary",
21
+ statValue: "font-display text-xl font-black tabular-nums text-fg",
22
+ footer: "relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary"
23
+ },
24
+ variants: {
25
+ radius: { md: { root: "rounded-md" }, lg: { root: "rounded-lg" }, xl: { root: "rounded-xl" } },
26
+ tone: { default: {}, elevated: { root: "bg-surface-2 shadow-md" }, glass: { root: "bg-surface-1/70 backdrop-blur-md" } },
27
+ interactive: { true: { root: "kz-spotlight kz-lift hover:border-accent/50" } },
28
+ padding: { none: { body: "p-0" }, sm: { body: "p-3" }, md: { body: "p-4" }, lg: { body: "p-6" } }
29
+ },
30
+ defaultVariants: { radius: "xl", tone: "default", padding: "md" }
31
+ });
32
+ var slots = card();
33
+ function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }) {
34
+ const Comp = asChild ? Slot.Root : "div";
35
+ const revealStyle = reveal === void 0 ? style : { ...style, "--kz-delay": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` };
36
+ return /* @__PURE__ */ jsx(
37
+ Comp,
38
+ {
39
+ "data-reveal": reveal === void 0 ? void 0 : "",
40
+ "data-interactive": interactive ? "" : void 0,
41
+ style: revealStyle,
42
+ className: card({ radius, tone, interactive }).root({ className }),
43
+ ...props
44
+ }
45
+ );
46
+ }
47
+ var ASPECT = { video: "aspect-video", square: "aspect-square", portrait: "aspect-[3/4]", auto: "" };
48
+ function CardMedia({ aspect = "video", className, ...props }) {
49
+ return /* @__PURE__ */ jsx("div", { "data-aspect": aspect, className: slots.media({ className: cn(ASPECT[aspect], className) }), ...props });
50
+ }
51
+ function CardImage({ src, alt = "", fallback, className, ...props }) {
52
+ if (!src) {
53
+ return /* @__PURE__ */ jsx("div", { "data-fallback": "", "aria-hidden": true, className: slots.fallback({ className }), children: fallback });
54
+ }
55
+ return /* @__PURE__ */ jsx("img", { src, alt, loading: "lazy", decoding: "async", className: slots.image({ className }), ...props });
56
+ }
57
+ var CORNER = {
58
+ "top-left": "left-2.5 top-2.5",
59
+ "top-right": "right-2.5 top-2.5",
60
+ "bottom-left": "bottom-2.5 left-2.5",
61
+ "bottom-right": "bottom-2.5 right-2.5"
62
+ };
63
+ function CardOverlay({ corner = "top-left", className, ...props }) {
64
+ return /* @__PURE__ */ jsx("div", { "data-corner": corner, className: slots.overlay({ className: cn(CORNER[corner], className) }), ...props });
65
+ }
66
+ function CardBody({ padding, className, ...props }) {
67
+ return /* @__PURE__ */ jsx("div", { className: card({ padding }).body({ className }), ...props });
68
+ }
69
+ function CardTitle({ as: Tag = "h3", className, ...props }) {
70
+ return /* @__PURE__ */ jsx(Tag, { className: slots.title({ className }), ...props });
71
+ }
72
+ function CardDescription({ className, ...props }) {
73
+ return /* @__PURE__ */ jsx("p", { className: slots.description({ className }), ...props });
74
+ }
75
+ function CardStat({ label, className, children, ...props }) {
76
+ return /* @__PURE__ */ jsxs("div", { className: slots.stat({ className }), ...props, children: [
77
+ /* @__PURE__ */ jsx("p", { className: slots.statLabel(), children: label }),
78
+ /* @__PURE__ */ jsx("p", { className: slots.statValue(), children })
79
+ ] });
80
+ }
81
+ function CardFooter({ className, ...props }) {
82
+ return /* @__PURE__ */ jsx("div", { className: slots.footer({ className }), ...props });
83
+ }
84
+ var Card = {
85
+ Root: CardRoot,
86
+ Media: CardMedia,
87
+ Image: CardImage,
88
+ Overlay: CardOverlay,
89
+ Body: CardBody,
90
+ Title: CardTitle,
91
+ Description: CardDescription,
92
+ Stat: CardStat,
93
+ Footer: CardFooter
94
+ };
95
+ export {
96
+ Card,
97
+ CardBody,
98
+ CardDescription,
99
+ CardFooter,
100
+ CardImage,
101
+ CardMedia,
102
+ CardOverlay,
103
+ CardRoot,
104
+ CardStat,
105
+ CardTitle,
106
+ card
107
+ };
108
+ //# sourceMappingURL=card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/card/card.tsx"],"sourcesContent":["// Server-safe: no hook, no context. One `tv` definition with slots: each part takes its slot, and a product overrides\n// any part through `className` (merged last by tailwind-merge).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\n\nexport const card = tv({\n slots: {\n root:\n \"group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n media: \"relative overflow-hidden bg-media\",\n image: \"h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105\",\n fallback: \"flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60\",\n overlay: \"absolute z-10 flex items-center gap-1.5\",\n body: \"relative flex flex-1 flex-col gap-3\",\n title: \"line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-accent\",\n description: \"line-clamp-2 text-xs text-fg-secondary\",\n stat: \"min-w-0\",\n statLabel: \"text-[10px] font-semibold uppercase tracking-wider text-fg-secondary\",\n statValue: \"font-display text-xl font-black tabular-nums text-fg\",\n footer: \"relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary\",\n },\n variants: {\n radius: { md: { root: \"rounded-md\" }, lg: { root: \"rounded-lg\" }, xl: { root: \"rounded-xl\" } },\n tone: { default: {}, elevated: { root: \"bg-surface-2 shadow-md\" }, glass: { root: \"bg-surface-1/70 backdrop-blur-md\" } },\n interactive: { true: { root: \"kz-spotlight kz-lift hover:border-accent/50\" } },\n padding: { none: { body: \"p-0\" }, sm: { body: \"p-3\" }, md: { body: \"p-4\" }, lg: { body: \"p-6\" } },\n },\n defaultVariants: { radius: \"xl\", tone: \"default\", padding: \"md\" },\n});\n\ntype CardVariants = VariantProps<typeof card>;\n/** The parts without a variant read the default slots: no context, so server-safe. */\nconst slots = card();\n\nexport type CardRootProps = React.ComponentProps<\"div\"> &\n Pick<CardVariants, \"radius\" | \"tone\" | \"interactive\"> & {\n /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */\n reveal?: number;\n };\n\nexport function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n const revealStyle =\n reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <Comp\n data-reveal={reveal === undefined ? undefined : \"\"}\n data-interactive={interactive ? \"\" : undefined}\n style={revealStyle}\n className={card({ radius, tone, interactive }).root({ className })}\n {...props}\n />\n );\n}\n\nconst ASPECT = { video: \"aspect-video\", square: \"aspect-square\", portrait: \"aspect-[3/4]\", auto: \"\" } as const;\nexport type CardMediaProps = React.ComponentProps<\"div\"> & { aspect?: keyof typeof ASPECT };\n\nexport function CardMedia({ aspect = \"video\", className, ...props }: CardMediaProps) {\n return <div data-aspect={aspect} className={slots.media({ className: cn(ASPECT[aspect], className) })} {...props} />;\n}\n\nexport type CardImageProps = Omit<React.ComponentProps<\"img\">, \"src\"> & {\n src?: string | null;\n /** Shown when there is no image: an icon, an illustration (decorative). */\n fallback?: React.ReactNode;\n};\n\n/** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */\nexport function CardImage({ src, alt = \"\", fallback, className, ...props }: CardImageProps) {\n if (!src) {\n return (\n <div data-fallback=\"\" aria-hidden className={slots.fallback({ className })}>\n {fallback}\n </div>\n );\n }\n return <img src={src} alt={alt} loading=\"lazy\" decoding=\"async\" className={slots.image({ className })} {...props} />;\n}\n\nconst CORNER = {\n \"top-left\": \"left-2.5 top-2.5\",\n \"top-right\": \"right-2.5 top-2.5\",\n \"bottom-left\": \"bottom-2.5 left-2.5\",\n \"bottom-right\": \"bottom-2.5 right-2.5\",\n} as const;\nexport type CardOverlayProps = React.ComponentProps<\"div\"> & { corner?: keyof typeof CORNER };\n\n/** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */\nexport function CardOverlay({ corner = \"top-left\", className, ...props }: CardOverlayProps) {\n return <div data-corner={corner} className={slots.overlay({ className: cn(CORNER[corner], className) })} {...props} />;\n}\n\nexport type CardBodyProps = React.ComponentProps<\"div\"> & Pick<CardVariants, \"padding\">;\n\nexport function CardBody({ padding, className, ...props }: CardBodyProps) {\n return <div className={card({ padding }).body({ className })} {...props} />;\n}\n\nexport type CardTitleProps = React.ComponentProps<\"h3\"> & { as?: \"h2\" | \"h3\" | \"h4\" };\n\nexport function CardTitle({ as: Tag = \"h3\", className, ...props }: CardTitleProps) {\n return <Tag className={slots.title({ className })} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return <p className={slots.description({ className })} {...props} />;\n}\n\nexport type CardStatProps = React.ComponentProps<\"div\"> & { label: React.ReactNode };\n\n/** A figure put forward: the label in capitals, the value in tabular digits. */\nexport function CardStat({ label, className, children, ...props }: CardStatProps) {\n return (\n <div className={slots.stat({ className })} {...props}>\n <p className={slots.statLabel()}>{label}</p>\n <p className={slots.statValue()}>{children}</p>\n </div>\n );\n}\n\nexport function CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={slots.footer({ className })} {...props} />;\n}\n\n/** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */\nexport const Card = {\n Root: CardRoot,\n Media: CardMedia,\n Image: CardImage,\n Overlay: CardOverlay,\n Body: CardBody,\n Title: CardTitle,\n Description: CardDescription,\n Stat: CardStat,\n Footer: CardFooter,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ // src/floating.ts
4
+ var floating = "kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden";
5
+
6
+ export {
7
+ floating
8
+ };
9
+ //# sourceMappingURL=chunk-J3NC6ER4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/floating.ts"],"sourcesContent":["// The one surface of everything that floats next to its trigger (popover, menu, tooltip): a raised surface, a border,\n// the large shadow and the `kz-pop` entrance from the side Radix measured (`--radix-*-transform-origin`).\nexport const floating =\n \"kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden\";\n"],"mappings":";;;AAEO,IAAM,WACX;","names":[]}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ // src/button/button.tsx
4
+ import { Slot } from "radix-ui";
5
+ import { tv } from "tailwind-variants";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var buttonVariants = tv({
8
+ base: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]",
9
+ variants: {
10
+ variant: {
11
+ primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover",
12
+ secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3",
13
+ outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent",
14
+ ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg",
15
+ // The danger token is invariant and too light for small text on a light surface (3.6:1): the label stays a
16
+ // text role, the border and the tint carry the danger.
17
+ danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20"
18
+ },
19
+ 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" },
20
+ shape: { rounded: "rounded-lg", pill: "rounded-full" }
21
+ },
22
+ defaultVariants: { variant: "secondary", size: "md", shape: "rounded" }
23
+ });
24
+ function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }) {
25
+ const Comp = asChild ? Slot.Root : "button";
26
+ return /* @__PURE__ */ jsx(
27
+ Comp,
28
+ {
29
+ type: asChild ? void 0 : "button",
30
+ "aria-busy": loading || void 0,
31
+ "data-loading": loading ? "" : void 0,
32
+ "data-variant": variant ?? "secondary",
33
+ disabled: asChild ? void 0 : disabled || loading,
34
+ "aria-disabled": asChild && (disabled || loading) ? true : void 0,
35
+ className: buttonVariants({ variant, size, shape, className }),
36
+ ...props
37
+ }
38
+ );
39
+ }
40
+ function IconButton({ label, size = "icon", ...props }) {
41
+ return /* @__PURE__ */ jsx(Button, { "aria-label": label, title: label, size, ...props });
42
+ }
43
+
44
+ export {
45
+ buttonVariants,
46
+ Button,
47
+ IconButton
48
+ };
49
+ //# sourceMappingURL=chunk-QRG5RYEI.js.map
@@ -0,0 +1 @@
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-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 /** 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 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;AAoClC;AAlCG,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;AAYM,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;AASO,SAAS,WAAW,EAAE,OAAO,OAAO,QAAQ,GAAG,MAAM,GAAoB;AAC9E,SAAO,oBAAC,UAAO,cAAY,OAAO,OAAO,OAAO,MAAa,GAAG,OAAO;AACzE;","names":[]}