@krizaka/ui 1.3.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 +137 -4
- package/dist/alert.d.ts +95 -0
- package/dist/alert.js +38 -0
- package/dist/alert.js.map +1 -0
- package/dist/avatar.d.ts +98 -0
- package/dist/avatar.js +48 -0
- package/dist/avatar.js.map +1 -0
- package/dist/badge.d.ts +64 -0
- package/dist/badge.js +31 -0
- package/dist/badge.js.map +1 -0
- package/dist/button.d.ts +76 -0
- package/dist/button.js +46 -0
- package/dist/button.js.map +1 -0
- package/dist/card.d.ts +227 -0
- package/dist/card.js +108 -0
- package/dist/card.js.map +1 -0
- package/dist/chunk-CPMZS4XA.js +11 -0
- package/dist/chunk-CPMZS4XA.js.map +1 -0
- package/dist/chunk-J3NC6ER4.js +9 -0
- package/dist/chunk-J3NC6ER4.js.map +1 -0
- package/dist/chunk-QRG5RYEI.js +49 -0
- package/dist/chunk-QRG5RYEI.js.map +1 -0
- package/dist/chunk-RLOXWF2G.js +13 -0
- package/dist/chunk-RLOXWF2G.js.map +1 -0
- package/dist/cn.d.ts +10 -0
- package/dist/cn.js +7 -0
- package/dist/cn.js.map +1 -0
- package/dist/countdown.d.ts +46 -0
- package/dist/countdown.js +74 -0
- package/dist/countdown.js.map +1 -0
- package/dist/dialog.d.ts +155 -0
- package/dist/dialog.js +157 -0
- package/dist/dialog.js.map +1 -0
- package/dist/dropdown-menu.d.ts +71 -0
- package/dist/dropdown-menu.js +66 -0
- package/dist/dropdown-menu.js.map +1 -0
- package/dist/empty-state.d.ts +63 -0
- package/dist/empty-state.js +26 -0
- package/dist/empty-state.js.map +1 -0
- package/dist/field.d.ts +80 -0
- package/dist/field.js +47 -0
- package/dist/field.js.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/kbd.d.ts +24 -0
- package/dist/kbd.js +16 -0
- package/dist/kbd.js.map +1 -0
- package/dist/page-header.d.ts +77 -0
- package/dist/page-header.js +32 -0
- package/dist/page-header.js.map +1 -0
- package/dist/popover.d.ts +18 -0
- package/dist/popover.js +34 -0
- package/dist/popover.js.map +1 -0
- package/dist/separator.d.ts +24 -0
- package/dist/separator.js +17 -0
- package/dist/separator.js.map +1 -0
- package/dist/skeleton.d.ts +28 -0
- package/dist/skeleton.js +18 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/slot.d.ts +11 -0
- package/dist/slot.js +9 -0
- package/dist/slot.js.map +1 -0
- package/dist/spinner.d.ts +31 -0
- package/dist/spinner.js +31 -0
- package/dist/spinner.js.map +1 -0
- package/dist/stat.d.ts +75 -0
- package/dist/stat.js +42 -0
- package/dist/stat.js.map +1 -0
- package/dist/theme.d.ts +95 -0
- package/dist/theme.js +119 -0
- package/dist/theme.js.map +1 -0
- package/dist/toast.d.ts +32 -0
- package/dist/toast.js +49 -0
- package/dist/toast.js.map +1 -0
- package/dist/tooltip.d.ts +21 -0
- package/dist/tooltip.js +44 -0
- package/dist/tooltip.js.map +1 -0
- package/package.json +333 -7
package/README.md
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
# @krizaka/ui
|
|
6
6
|
|
|
7
|
-
**The Krizaka
|
|
8
|
-
The
|
|
9
|
-
reveal, roll, shine
|
|
7
|
+
**The Krizaka front-end platform's components, shared by every product.**
|
|
8
|
+
The primitives (button, badge, avatar, field, theme, countdown…) on the semantic `--kz-*` tokens, the animated marks
|
|
9
|
+
of Krizaka, Orazaka and Orochia, and the Krizaka motion signature — one easing, one way to enter, reveal, roll, shine
|
|
10
|
+
and open.
|
|
10
11
|
|
|
11
12
|
[](https://www.npmjs.com/package/@krizaka/ui)
|
|
12
13
|
[](https://github.com/krizaka/krizaka-ui/actions/workflows/ci.yml)
|
|
@@ -20,9 +21,141 @@ reveal, roll, shine and open.
|
|
|
20
21
|
npm install @krizaka/ui
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
React 18 or 19
|
|
24
|
+
The marks and the motion: React 18 or 19, no framework, no CSS library — the marks carry their own styles, the motion
|
|
25
|
+
is one CSS file. The primitives: **React 19** and Tailwind CSS v4 with the Krizaka preset (below).
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
## Primitives
|
|
29
|
+
|
|
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.
|
|
32
|
+
|
|
33
|
+
### Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @krizaka/ui@beta @krizaka/tailwind@beta @krizaka/tokens@beta tailwindcss
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```css
|
|
40
|
+
/* app/globals.css — the preset (tokens as utilities), then the classes the primitives use */
|
|
41
|
+
@import "tailwindcss";
|
|
42
|
+
@import "@krizaka/tailwind";
|
|
43
|
+
@import "@krizaka/ui/tailwind.css";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { Button, IconButton } from "@krizaka/ui/button";
|
|
48
|
+
import { Badge } from "@krizaka/ui/badge";
|
|
49
|
+
import { Field, Input } from "@krizaka/ui/field";
|
|
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";
|
|
54
|
+
|
|
55
|
+
<Button variant="primary" loading={saving}>{t("save")}</Button>
|
|
56
|
+
<Button asChild variant="outline"><Link href="/docs">{t("docs")}</Link></Button>
|
|
57
|
+
<Badge tone="danger" dot pulse>{t("endsSoon")}</Badge>
|
|
58
|
+
<Field.Root>
|
|
59
|
+
<Field.Label htmlFor="email">{t("email")}</Field.Label>
|
|
60
|
+
<Input id="email" invalid={!!error} aria-describedby="email-error" />
|
|
61
|
+
{error && <Field.Error id="email-error">{error}</Field.Error>}
|
|
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")} />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**React 19 is required by the primitives** (`ref` is a prop, no `forwardRef`). The package keeps `react >= 18` as its
|
|
87
|
+
peer range, because the marks and the motion still run on React 18: one `package.json` cannot hold both ranges.
|
|
88
|
+
`tailwindcss` and `@krizaka/tailwind` are optional peers — a native app or a marks-only site never installs them.
|
|
89
|
+
|
|
90
|
+
### The component contract (12 points)
|
|
91
|
+
|
|
92
|
+
1. **Composed when there is structure** (`Card.*`, `Dialog.*`, `Field.*`, `Avatar.Group`), **flat for an atom**
|
|
93
|
+
(`Button`, `Badge`, `Input`, `Stat`).
|
|
94
|
+
2. **Variants exported next to the component** (`buttonVariants`, `badgeVariants`): a `<Link>` or a Server Component
|
|
95
|
+
is styled without importing the component.
|
|
96
|
+
3. **`asChild`** (Radix `Slot`) rather than `as`: props, `className` and `ref` are merged into the child.
|
|
97
|
+
4. **`className` is always merged last** (tailwind-merge, through `cn`): the product's override wins.
|
|
98
|
+
5. **Words are props** (`label`, `closeLabel`, `units`, `title`) — there is no `t()` in the kit. Each app re-exports the
|
|
99
|
+
primitives with its words from its own `components/ui/`.
|
|
100
|
+
6. **Controlled and uncontrolled**: `value`/`defaultValue` + `onValueChange`, `open`/`defaultOpen` + `onOpenChange`.
|
|
101
|
+
7. **State is exposed as `data-*`** (`data-tone`, `data-loading`, `data-invalid`, `data-urgent`, `data-mode`): a
|
|
102
|
+
product styles a state without a new variant.
|
|
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`).
|
|
105
|
+
One build entry per component; the directive is kept per entry.
|
|
106
|
+
9. **React 19 minimum for the primitives**; the marks stay `react >= 18`.
|
|
107
|
+
10. **Accessibility is delegated**: non-trivial interactions on Radix UI (`radix-ui`), the command palette on cmdk,
|
|
108
|
+
toasts on sonner — no hand-written focus trap.
|
|
109
|
+
11. **Every primitive ships** types, a story (every variant, dark and light, screenshot-tested), an RTL + axe test and
|
|
110
|
+
an entry here.
|
|
111
|
+
12. **Motion**: `prefers-reduced-motion` is honoured (`motion-safe:`); no component animates a layout property.
|
|
112
|
+
|
|
113
|
+
Colours are roles of the preset (`bg-surface-2`, `text-fg-secondary`, `border-border-default`, `text-accent`,
|
|
114
|
+
`text-danger`), never a palette step nor `light:`: a theme — dark, light or a product's — is a set of tokens.
|
|
115
|
+
|
|
116
|
+
### Entries
|
|
117
|
+
|
|
118
|
+
| Entry | Exports | Runs |
|
|
119
|
+
| :-- | :-- | :-- |
|
|
120
|
+
| `@krizaka/ui/cn` | `cn(...classes)` — clsx + tailwind-merge (the engine `tailwind-variants` ships) | server |
|
|
121
|
+
| `@krizaka/ui/slot` | `Slot`, `VisuallyHidden` (Radix) | server |
|
|
122
|
+
| `@krizaka/ui/button` | `Button` (`variant` primary·secondary·outline·ghost·danger, `size` sm·md·lg·icon, `shape` rounded·pill, `loading`, `asChild`), `IconButton` (`label` required), `buttonVariants` | server |
|
|
123
|
+
| `@krizaka/ui/badge` | `Badge` (`tone` neutral·accent·success·warning·danger·scrim, `size` sm·md, `dot`, `pulse`), `badgeVariants` | server |
|
|
124
|
+
| `@krizaka/ui/avatar` | `Avatar` (`src`, `alt`, `fallback`, `size` xs·sm·md·lg·xl), `Avatar.Group` (`max`) | client |
|
|
125
|
+
| `@krizaka/ui/field` | `Field.Root/Label/Hint/Error`, `Input`, `Textarea`, `Select` (native, styled) — `invalid` | server |
|
|
126
|
+
| `@krizaka/ui/theme` | `ThemeScript` (in `<head>`, no flash), `ThemeProvider`, `useTheme()` → `{ mode, setMode, theme, setTheme }`, `ThemeToggle` (`label`) | client |
|
|
127
|
+
| `@krizaka/ui/skeleton` | `Skeleton` (`shape` text·circle·rect) | server |
|
|
128
|
+
| `@krizaka/ui/empty-state` | `EmptyState` (`icon`, `title`, `description`, `action`) | server |
|
|
129
|
+
| `@krizaka/ui/spinner` | `Spinner` (`label`, `size`) | server |
|
|
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 |
|
|
142
|
+
| `@krizaka/ui` | the marks, `MotionObserver`, `RotatingWord`, `cx` (unchanged) | client |
|
|
143
|
+
| `@krizaka/ui/native` | the marks for React Native (unchanged) | native |
|
|
144
|
+
| `@krizaka/ui/motion.css`, `@krizaka/ui/tailwind.css` | the motion signature; the `@source` of the primitives' classes | CSS |
|
|
145
|
+
|
|
146
|
+
The theme is one mechanism for every product: dark by default, `html.light` for light, `html.theme-<name>` for a named
|
|
147
|
+
theme, persisted as `kz-theme` (and `kz-theme-name`).
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// app/layout.tsx
|
|
151
|
+
<html lang={locale} suppressHydrationWarning>
|
|
152
|
+
<head><ThemeScript /></head>
|
|
153
|
+
<body><ThemeProvider>{children}</ThemeProvider></body>
|
|
154
|
+
</html>
|
|
155
|
+
|
|
156
|
+
<ThemeToggle label={(mode) => t(`theme.${mode}`)} />
|
|
157
|
+
```
|
|
158
|
+
|
|
26
159
|
## React Native
|
|
27
160
|
|
|
28
161
|
The marks have one geometry; `@krizaka/ui/native` draws it with `react-native-svg` for the apps
|
package/dist/alert.d.ts
ADDED
|
@@ -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/avatar.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import { VariantProps } from 'tailwind-variants';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
declare const avatar: tailwind_variants.TVReturnType<{
|
|
6
|
+
size: {
|
|
7
|
+
xs: {
|
|
8
|
+
root: string;
|
|
9
|
+
};
|
|
10
|
+
sm: {
|
|
11
|
+
root: string;
|
|
12
|
+
};
|
|
13
|
+
md: {
|
|
14
|
+
root: string;
|
|
15
|
+
};
|
|
16
|
+
lg: {
|
|
17
|
+
root: string;
|
|
18
|
+
};
|
|
19
|
+
xl: {
|
|
20
|
+
root: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}, {
|
|
24
|
+
root: string;
|
|
25
|
+
image: string;
|
|
26
|
+
fallback: string;
|
|
27
|
+
group: string;
|
|
28
|
+
}, undefined, {
|
|
29
|
+
size: {
|
|
30
|
+
xs: {
|
|
31
|
+
root: string;
|
|
32
|
+
};
|
|
33
|
+
sm: {
|
|
34
|
+
root: string;
|
|
35
|
+
};
|
|
36
|
+
md: {
|
|
37
|
+
root: string;
|
|
38
|
+
};
|
|
39
|
+
lg: {
|
|
40
|
+
root: string;
|
|
41
|
+
};
|
|
42
|
+
xl: {
|
|
43
|
+
root: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}, {
|
|
47
|
+
root: string;
|
|
48
|
+
image: string;
|
|
49
|
+
fallback: string;
|
|
50
|
+
group: string;
|
|
51
|
+
}, tailwind_variants.TVReturnTypeLike<{
|
|
52
|
+
size: {
|
|
53
|
+
xs: {
|
|
54
|
+
root: string;
|
|
55
|
+
};
|
|
56
|
+
sm: {
|
|
57
|
+
root: string;
|
|
58
|
+
};
|
|
59
|
+
md: {
|
|
60
|
+
root: string;
|
|
61
|
+
};
|
|
62
|
+
lg: {
|
|
63
|
+
root: string;
|
|
64
|
+
};
|
|
65
|
+
xl: {
|
|
66
|
+
root: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}, {
|
|
70
|
+
root: string;
|
|
71
|
+
image: string;
|
|
72
|
+
fallback: string;
|
|
73
|
+
group: string;
|
|
74
|
+
}>>;
|
|
75
|
+
type AvatarSize = NonNullable<VariantProps<typeof avatar>["size"]>;
|
|
76
|
+
type AvatarProps = Omit<React.ComponentProps<"span">, "children"> & {
|
|
77
|
+
src?: string | null;
|
|
78
|
+
/** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */
|
|
79
|
+
alt?: string;
|
|
80
|
+
/** Shown while the image loads, when it fails or when there is none: initials, an icon. */
|
|
81
|
+
fallback?: React.ReactNode;
|
|
82
|
+
size?: AvatarSize;
|
|
83
|
+
};
|
|
84
|
+
declare function AvatarRoot({ src, alt, fallback, size, className, ...props }: AvatarProps): React.JSX.Element;
|
|
85
|
+
type AvatarGroupProps = React.ComponentProps<"div"> & {
|
|
86
|
+
/** How many avatars are shown; the rest becomes a `+n` avatar. */
|
|
87
|
+
max?: number;
|
|
88
|
+
/** The size of the `+n` avatar (match the avatars of the group). */
|
|
89
|
+
size?: AvatarSize;
|
|
90
|
+
};
|
|
91
|
+
/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */
|
|
92
|
+
declare function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps): React.JSX.Element;
|
|
93
|
+
/** `Avatar` (an image with a fallback) and `Avatar.Group`. */
|
|
94
|
+
declare const Avatar: typeof AvatarRoot & {
|
|
95
|
+
Group: typeof AvatarGroup;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, AvatarRoot, type AvatarSize };
|
package/dist/avatar.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/avatar/avatar.tsx
|
|
4
|
+
import { Avatar as AvatarPrimitive } from "radix-ui";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { tv } from "tailwind-variants";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var avatar = tv({
|
|
9
|
+
slots: {
|
|
10
|
+
root: "relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle",
|
|
11
|
+
image: "h-full w-full object-cover",
|
|
12
|
+
fallback: "flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary",
|
|
13
|
+
group: "flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0"
|
|
14
|
+
},
|
|
15
|
+
variants: {
|
|
16
|
+
size: {
|
|
17
|
+
xs: { root: "h-5 w-5 text-[9px]" },
|
|
18
|
+
sm: { root: "h-8 w-8 text-xs" },
|
|
19
|
+
md: { root: "h-10 w-10 text-sm" },
|
|
20
|
+
lg: { root: "h-14 w-14 text-base" },
|
|
21
|
+
xl: { root: "h-20 w-20 text-lg" }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: { size: "md" }
|
|
25
|
+
});
|
|
26
|
+
function AvatarRoot({ src, alt = "", fallback, size, className, ...props }) {
|
|
27
|
+
const s = avatar({ size });
|
|
28
|
+
return /* @__PURE__ */ jsxs(AvatarPrimitive.Root, { "data-size": size ?? "md", className: s.root({ className }), ...props, children: [
|
|
29
|
+
src && /* @__PURE__ */ jsx(AvatarPrimitive.Image, { src, alt, className: s.image() }),
|
|
30
|
+
/* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { delayMs: src ? 300 : 0, className: s.fallback(), children: fallback })
|
|
31
|
+
] });
|
|
32
|
+
}
|
|
33
|
+
function AvatarGroup({ max, size, className, children, ...props }) {
|
|
34
|
+
const items = React.Children.toArray(children);
|
|
35
|
+
const shown = max === void 0 ? items : items.slice(0, max);
|
|
36
|
+
const rest = items.length - shown.length;
|
|
37
|
+
return /* @__PURE__ */ jsxs("div", { className: avatar().group({ className }), ...props, children: [
|
|
38
|
+
shown,
|
|
39
|
+
rest > 0 && /* @__PURE__ */ jsx(AvatarRoot, { size, fallback: `+${rest}` })
|
|
40
|
+
] });
|
|
41
|
+
}
|
|
42
|
+
var Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });
|
|
43
|
+
export {
|
|
44
|
+
Avatar,
|
|
45
|
+
AvatarGroup,
|
|
46
|
+
AvatarRoot
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=avatar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/avatar/avatar.tsx"],"sourcesContent":["// Client: Radix tracks the image's loading state and shows the fallback until it has loaded (or when it fails).\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nconst avatar = tv({\n slots: {\n root: \"relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle\",\n image: \"h-full w-full object-cover\",\n fallback: \"flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary\",\n group: \"flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0\",\n },\n variants: {\n size: {\n xs: { root: \"h-5 w-5 text-[9px]\" },\n sm: { root: \"h-8 w-8 text-xs\" },\n md: { root: \"h-10 w-10 text-sm\" },\n lg: { root: \"h-14 w-14 text-base\" },\n xl: { root: \"h-20 w-20 text-lg\" },\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type AvatarSize = NonNullable<VariantProps<typeof avatar>[\"size\"]>;\n\nexport type AvatarProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n src?: string | null;\n /** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */\n alt?: string;\n /** Shown while the image loads, when it fails or when there is none: initials, an icon. */\n fallback?: React.ReactNode;\n size?: AvatarSize;\n};\n\nexport function AvatarRoot({ src, alt = \"\", fallback, size, className, ...props }: AvatarProps) {\n const s = avatar({ size });\n return (\n <AvatarPrimitive.Root data-size={size ?? \"md\"} className={s.root({ className })} {...props}>\n {src && <AvatarPrimitive.Image src={src} alt={alt} className={s.image()} />}\n <AvatarPrimitive.Fallback delayMs={src ? 300 : 0} className={s.fallback()}>\n {fallback}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n );\n}\n\nexport type AvatarGroupProps = React.ComponentProps<\"div\"> & {\n /** How many avatars are shown; the rest becomes a `+n` avatar. */\n max?: number;\n /** The size of the `+n` avatar (match the avatars of the group). */\n size?: AvatarSize;\n};\n\n/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */\nexport function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps) {\n const items = React.Children.toArray(children);\n const shown = max === undefined ? items : items.slice(0, max);\n const rest = items.length - shown.length;\n return (\n <div className={avatar().group({ className })} {...props}>\n {shown}\n {rest > 0 && <AvatarRoot size={size} fallback={`+${rest}`} />}\n </div>\n );\n}\n\n/** `Avatar` (an image with a fallback) and `Avatar.Group`. */\nexport const Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });\n"],"mappings":";;;AACA,SAAS,UAAU,uBAAuB;AAC1C,YAAY,WAAW;AACvB,SAAS,UAA6B;AAmClC,SACU,KADV;AAjCJ,IAAM,SAAS,GAAG;AAAA,EAChB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI,EAAE,MAAM,qBAAqB;AAAA,MACjC,IAAI,EAAE,MAAM,kBAAkB;AAAA,MAC9B,IAAI,EAAE,MAAM,oBAAoB;AAAA,MAChC,IAAI,EAAE,MAAM,sBAAsB;AAAA,MAClC,IAAI,EAAE,MAAM,oBAAoB;AAAA,IAClC;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;AAaM,SAAS,WAAW,EAAE,KAAK,MAAM,IAAI,UAAU,MAAM,WAAW,GAAG,MAAM,GAAgB;AAC9F,QAAM,IAAI,OAAO,EAAE,KAAK,CAAC;AACzB,SACE,qBAAC,gBAAgB,MAAhB,EAAqB,aAAW,QAAQ,MAAM,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAClF;AAAA,WAAO,oBAAC,gBAAgB,OAAhB,EAAsB,KAAU,KAAU,WAAW,EAAE,MAAM,GAAG;AAAA,IACzE,oBAAC,gBAAgB,UAAhB,EAAyB,SAAS,MAAM,MAAM,GAAG,WAAW,EAAE,SAAS,GACrE,oBACH;AAAA,KACF;AAEJ;AAUO,SAAS,YAAY,EAAE,KAAK,MAAM,WAAW,UAAU,GAAG,MAAM,GAAqB;AAC1F,QAAM,QAAc,eAAS,QAAQ,QAAQ;AAC7C,QAAM,QAAQ,QAAQ,SAAY,QAAQ,MAAM,MAAM,GAAG,GAAG;AAC5D,QAAM,OAAO,MAAM,SAAS,MAAM;AAClC,SACE,qBAAC,SAAI,WAAW,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,GAAI,GAAG,OAChD;AAAA;AAAA,IACA,OAAO,KAAK,oBAAC,cAAW,MAAY,UAAU,IAAI,IAAI,IAAI;AAAA,KAC7D;AAEJ;AAGO,IAAM,SAAS,OAAO,OAAO,YAAY,EAAE,OAAO,YAAY,CAAC;","names":[]}
|
package/dist/badge.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import { VariantProps } from 'tailwind-variants';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
declare const badgeVariants: tailwind_variants.TVReturnType<{
|
|
6
|
+
tone: {
|
|
7
|
+
neutral: "bg-surface-3 text-fg-secondary";
|
|
8
|
+
accent: "bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent";
|
|
9
|
+
success: "bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success";
|
|
10
|
+
warning: "bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning";
|
|
11
|
+
danger: "bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger";
|
|
12
|
+
/** On a media: veil and text invariant, identical in both themes. */
|
|
13
|
+
scrim: "bg-scrim text-fg-on-media backdrop-blur-md";
|
|
14
|
+
};
|
|
15
|
+
size: {
|
|
16
|
+
sm: "px-2 py-0.5 text-[10px]";
|
|
17
|
+
md: "px-2.5 py-1 text-[11px]";
|
|
18
|
+
};
|
|
19
|
+
pulse: {
|
|
20
|
+
true: "[&>[data-dot]]:motion-safe:animate-pulse";
|
|
21
|
+
};
|
|
22
|
+
}, undefined, "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-full font-semibold uppercase tracking-wider", {
|
|
23
|
+
tone: {
|
|
24
|
+
neutral: "bg-surface-3 text-fg-secondary";
|
|
25
|
+
accent: "bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent";
|
|
26
|
+
success: "bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success";
|
|
27
|
+
warning: "bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning";
|
|
28
|
+
danger: "bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger";
|
|
29
|
+
/** On a media: veil and text invariant, identical in both themes. */
|
|
30
|
+
scrim: "bg-scrim text-fg-on-media backdrop-blur-md";
|
|
31
|
+
};
|
|
32
|
+
size: {
|
|
33
|
+
sm: "px-2 py-0.5 text-[10px]";
|
|
34
|
+
md: "px-2.5 py-1 text-[11px]";
|
|
35
|
+
};
|
|
36
|
+
pulse: {
|
|
37
|
+
true: "[&>[data-dot]]:motion-safe:animate-pulse";
|
|
38
|
+
};
|
|
39
|
+
}, undefined, tailwind_variants.TVReturnTypeLike<{
|
|
40
|
+
tone: {
|
|
41
|
+
neutral: "bg-surface-3 text-fg-secondary";
|
|
42
|
+
accent: "bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent";
|
|
43
|
+
success: "bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success";
|
|
44
|
+
warning: "bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning";
|
|
45
|
+
danger: "bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger";
|
|
46
|
+
/** On a media: veil and text invariant, identical in both themes. */
|
|
47
|
+
scrim: "bg-scrim text-fg-on-media backdrop-blur-md";
|
|
48
|
+
};
|
|
49
|
+
size: {
|
|
50
|
+
sm: "px-2 py-0.5 text-[10px]";
|
|
51
|
+
md: "px-2.5 py-1 text-[11px]";
|
|
52
|
+
};
|
|
53
|
+
pulse: {
|
|
54
|
+
true: "[&>[data-dot]]:motion-safe:animate-pulse";
|
|
55
|
+
};
|
|
56
|
+
}, undefined>>;
|
|
57
|
+
type BadgeVariants = VariantProps<typeof badgeVariants>;
|
|
58
|
+
type BadgeProps = React.ComponentProps<"span"> & BadgeVariants & {
|
|
59
|
+
/** A small dot before the text, in the tone's colour (pulsing with `pulse`). */
|
|
60
|
+
dot?: boolean;
|
|
61
|
+
};
|
|
62
|
+
declare function Badge({ tone, size, pulse, dot, className, children, ...props }: BadgeProps): React.JSX.Element;
|
|
63
|
+
|
|
64
|
+
export { Badge, type BadgeProps, type BadgeVariants, badgeVariants };
|
package/dist/badge.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/badge/badge.tsx
|
|
2
|
+
import { tv } from "tailwind-variants";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var badgeVariants = tv({
|
|
5
|
+
base: "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-full font-semibold uppercase tracking-wider",
|
|
6
|
+
variants: {
|
|
7
|
+
tone: {
|
|
8
|
+
neutral: "bg-surface-3 text-fg-secondary",
|
|
9
|
+
accent: "bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent",
|
|
10
|
+
success: "bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success",
|
|
11
|
+
warning: "bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning",
|
|
12
|
+
danger: "bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger",
|
|
13
|
+
/** On a media: veil and text invariant, identical in both themes. */
|
|
14
|
+
scrim: "bg-scrim text-fg-on-media backdrop-blur-md"
|
|
15
|
+
},
|
|
16
|
+
size: { sm: "px-2 py-0.5 text-[10px]", md: "px-2.5 py-1 text-[11px]" },
|
|
17
|
+
pulse: { true: "[&>[data-dot]]:motion-safe:animate-pulse" }
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { tone: "neutral", size: "sm" }
|
|
20
|
+
});
|
|
21
|
+
function Badge({ tone, size, pulse, dot, className, children, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsxs("span", { "data-tone": tone ?? "neutral", className: badgeVariants({ tone, size, pulse, className }), ...props, children: [
|
|
23
|
+
dot && /* @__PURE__ */ jsx("span", { "data-dot": "", "aria-hidden": true, className: "h-1.5 w-1.5 rounded-full bg-current" }),
|
|
24
|
+
children
|
|
25
|
+
] });
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
Badge,
|
|
29
|
+
badgeVariants
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=badge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/badge/badge.tsx"],"sourcesContent":["// Server-safe: no hook, no context.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\n// Status tones are soft (a tint, a coloured dot, a text role): the invariant status tokens are too light to carry\n// small text on a light surface, or to sit under white text (WCAG AA).\nexport const badgeVariants = tv({\n base: \"inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-full font-semibold uppercase tracking-wider\",\n variants: {\n tone: {\n neutral: \"bg-surface-3 text-fg-secondary\",\n accent: \"bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent\",\n success: \"bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success\",\n warning: \"bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning\",\n danger: \"bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger\",\n /** On a media: veil and text invariant, identical in both themes. */\n scrim: \"bg-scrim text-fg-on-media backdrop-blur-md\",\n },\n size: { sm: \"px-2 py-0.5 text-[10px]\", md: \"px-2.5 py-1 text-[11px]\" },\n pulse: { true: \"[&>[data-dot]]:motion-safe:animate-pulse\" },\n },\n defaultVariants: { tone: \"neutral\", size: \"sm\" },\n});\n\nexport type BadgeVariants = VariantProps<typeof badgeVariants>;\n\nexport type BadgeProps = React.ComponentProps<\"span\"> &\n BadgeVariants & {\n /** A small dot before the text, in the tone's colour (pulsing with `pulse`). */\n dot?: boolean;\n };\n\nexport function Badge({ tone, size, pulse, dot, className, children, ...props }: BadgeProps) {\n return (\n <span data-tone={tone ?? \"neutral\"} className={badgeVariants({ tone, size, pulse, className })} {...props}>\n {dot && <span data-dot=\"\" aria-hidden className=\"h-1.5 w-1.5 rounded-full bg-current\" />}\n {children}\n </span>\n );\n}\n"],"mappings":";AAEA,SAAS,UAA6B;AAgClC,SACU,KADV;AA5BG,IAAM,gBAAgB,GAAG;AAAA,EAC9B,MAAM;AAAA,EACN,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA;AAAA,MAER,OAAO;AAAA,IACT;AAAA,IACA,MAAM,EAAE,IAAI,2BAA2B,IAAI,0BAA0B;AAAA,IACrE,OAAO,EAAE,MAAM,2CAA2C;AAAA,EAC5D;AAAA,EACA,iBAAiB,EAAE,MAAM,WAAW,MAAM,KAAK;AACjD,CAAC;AAUM,SAAS,MAAM,EAAE,MAAM,MAAM,OAAO,KAAK,WAAW,UAAU,GAAG,MAAM,GAAe;AAC3F,SACE,qBAAC,UAAK,aAAW,QAAQ,WAAW,WAAW,cAAc,EAAE,MAAM,MAAM,OAAO,UAAU,CAAC,GAAI,GAAG,OACjG;AAAA,WAAO,oBAAC,UAAK,YAAS,IAAG,eAAW,MAAC,WAAU,uCAAsC;AAAA,IACrF;AAAA,KACH;AAEJ;","names":[]}
|
package/dist/button.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import { VariantProps } from 'tailwind-variants';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
declare const buttonVariants: tailwind_variants.TVReturnType<{
|
|
6
|
+
variant: {
|
|
7
|
+
primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
|
|
8
|
+
secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
|
|
9
|
+
outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
|
|
10
|
+
ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
|
|
11
|
+
danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
|
|
12
|
+
};
|
|
13
|
+
size: {
|
|
14
|
+
sm: "h-8 px-3 text-xs";
|
|
15
|
+
md: "h-10 px-4 text-sm";
|
|
16
|
+
lg: "h-12 px-6 text-sm";
|
|
17
|
+
icon: "h-10 w-10";
|
|
18
|
+
};
|
|
19
|
+
shape: {
|
|
20
|
+
rounded: "rounded-lg";
|
|
21
|
+
pill: "rounded-full";
|
|
22
|
+
};
|
|
23
|
+
}, undefined, string, {
|
|
24
|
+
variant: {
|
|
25
|
+
primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
|
|
26
|
+
secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
|
|
27
|
+
outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
|
|
28
|
+
ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
|
|
29
|
+
danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
|
|
30
|
+
};
|
|
31
|
+
size: {
|
|
32
|
+
sm: "h-8 px-3 text-xs";
|
|
33
|
+
md: "h-10 px-4 text-sm";
|
|
34
|
+
lg: "h-12 px-6 text-sm";
|
|
35
|
+
icon: "h-10 w-10";
|
|
36
|
+
};
|
|
37
|
+
shape: {
|
|
38
|
+
rounded: "rounded-lg";
|
|
39
|
+
pill: "rounded-full";
|
|
40
|
+
};
|
|
41
|
+
}, undefined, tailwind_variants.TVReturnTypeLike<{
|
|
42
|
+
variant: {
|
|
43
|
+
primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover";
|
|
44
|
+
secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3";
|
|
45
|
+
outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent";
|
|
46
|
+
ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg";
|
|
47
|
+
danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20";
|
|
48
|
+
};
|
|
49
|
+
size: {
|
|
50
|
+
sm: "h-8 px-3 text-xs";
|
|
51
|
+
md: "h-10 px-4 text-sm";
|
|
52
|
+
lg: "h-12 px-6 text-sm";
|
|
53
|
+
icon: "h-10 w-10";
|
|
54
|
+
};
|
|
55
|
+
shape: {
|
|
56
|
+
rounded: "rounded-lg";
|
|
57
|
+
pill: "rounded-full";
|
|
58
|
+
};
|
|
59
|
+
}, undefined>>;
|
|
60
|
+
type ButtonVariants = VariantProps<typeof buttonVariants>;
|
|
61
|
+
type ButtonProps = React.ComponentProps<"button"> & ButtonVariants & {
|
|
62
|
+
/** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */
|
|
63
|
+
asChild?: boolean;
|
|
64
|
+
/** Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one. */
|
|
65
|
+
loading?: boolean;
|
|
66
|
+
};
|
|
67
|
+
declare function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }: ButtonProps): React.JSX.Element;
|
|
68
|
+
type IconButtonProps = Omit<ButtonProps, "size"> & {
|
|
69
|
+
/** The accessible name (and tooltip) of the button — required, passed translated. */
|
|
70
|
+
label: string;
|
|
71
|
+
size?: ButtonProps["size"];
|
|
72
|
+
};
|
|
73
|
+
/** An icon-only button: `label` is required, it is its accessible name. */
|
|
74
|
+
declare function IconButton({ label, size, ...props }: IconButtonProps): React.JSX.Element;
|
|
75
|
+
|
|
76
|
+
export { Button, type ButtonProps, type ButtonVariants, IconButton, type IconButtonProps, buttonVariants };
|