@nphuonha/ui-kit 0.0.0-stage → 0.2.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 (105) hide show
  1. package/CONVENTIONS.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -2
  4. package/dist/components/ui/_shared/icon-provider.d.ts +18 -0
  5. package/dist/components/ui/_shared/icon-provider.js +26 -0
  6. package/dist/components/ui/alert/alert-variants.d.ts +5 -0
  7. package/dist/components/ui/alert/alert-variants.js +13 -0
  8. package/dist/components/ui/alert/alert.d.ts +9 -0
  9. package/dist/components/ui/alert/alert.js +13 -0
  10. package/dist/components/ui/alert/index.d.ts +2 -0
  11. package/dist/components/ui/alert/index.js +2 -0
  12. package/dist/components/ui/badge/badge-variants.d.ts +5 -0
  13. package/dist/components/ui/badge/badge-variants.js +16 -0
  14. package/dist/components/ui/badge/badge.d.ts +6 -0
  15. package/dist/components/ui/badge/badge.js +6 -0
  16. package/dist/components/ui/badge/index.d.ts +2 -0
  17. package/dist/components/ui/badge/index.js +2 -0
  18. package/dist/components/ui/button/button-variants.d.ts +7 -0
  19. package/dist/components/ui/button/button-variants.js +29 -0
  20. package/dist/components/ui/button/button.d.ts +10 -0
  21. package/dist/components/ui/button/button.js +12 -0
  22. package/dist/components/ui/button/index.d.ts +2 -0
  23. package/dist/components/ui/button/index.js +2 -0
  24. package/dist/components/ui/card/card.d.ts +7 -0
  25. package/dist/components/ui/card/card.js +20 -0
  26. package/dist/components/ui/card/index.d.ts +1 -0
  27. package/dist/components/ui/card/index.js +1 -0
  28. package/dist/components/ui/checkbox/checkbox.d.ts +8 -0
  29. package/dist/components/ui/checkbox/checkbox.js +18 -0
  30. package/dist/components/ui/checkbox/index.d.ts +1 -0
  31. package/dist/components/ui/checkbox/index.js +1 -0
  32. package/dist/components/ui/dialog/dialog.d.ts +15 -0
  33. package/dist/components/ui/dialog/dialog.js +10 -0
  34. package/dist/components/ui/dialog/index.d.ts +1 -0
  35. package/dist/components/ui/dialog/index.js +1 -0
  36. package/dist/components/ui/field/field.d.ts +11 -0
  37. package/dist/components/ui/field/field.js +22 -0
  38. package/dist/components/ui/field/index.d.ts +1 -0
  39. package/dist/components/ui/field/index.js +1 -0
  40. package/dist/components/ui/icon-button/icon-button.d.ts +7 -0
  41. package/dist/components/ui/icon-button/icon-button.js +7 -0
  42. package/dist/components/ui/icon-button/index.d.ts +1 -0
  43. package/dist/components/ui/icon-button/index.js +1 -0
  44. package/dist/components/ui/input/index.d.ts +1 -0
  45. package/dist/components/ui/input/index.js +1 -0
  46. package/dist/components/ui/input/input.d.ts +5 -0
  47. package/dist/components/ui/input/input.js +6 -0
  48. package/dist/components/ui/label/index.d.ts +1 -0
  49. package/dist/components/ui/label/index.js +1 -0
  50. package/dist/components/ui/label/label.d.ts +5 -0
  51. package/dist/components/ui/label/label.js +6 -0
  52. package/dist/components/ui/popover/index.d.ts +1 -0
  53. package/dist/components/ui/popover/index.js +1 -0
  54. package/dist/components/ui/popover/popover.d.ts +14 -0
  55. package/dist/components/ui/popover/popover.js +7 -0
  56. package/dist/components/ui/radio-group/index.d.ts +1 -0
  57. package/dist/components/ui/radio-group/index.js +1 -0
  58. package/dist/components/ui/radio-group/radio-group.d.ts +17 -0
  59. package/dist/components/ui/radio-group/radio-group.js +17 -0
  60. package/dist/components/ui/select/index.d.ts +1 -0
  61. package/dist/components/ui/select/index.js +1 -0
  62. package/dist/components/ui/select/select.d.ts +21 -0
  63. package/dist/components/ui/select/select.js +10 -0
  64. package/dist/components/ui/separator/index.d.ts +1 -0
  65. package/dist/components/ui/separator/index.js +1 -0
  66. package/dist/components/ui/separator/separator.d.ts +6 -0
  67. package/dist/components/ui/separator/separator.js +5 -0
  68. package/dist/components/ui/sheet/index.d.ts +1 -0
  69. package/dist/components/ui/sheet/index.js +1 -0
  70. package/dist/components/ui/sheet/sheet.d.ts +15 -0
  71. package/dist/components/ui/sheet/sheet.js +15 -0
  72. package/dist/components/ui/spinner/index.d.ts +1 -0
  73. package/dist/components/ui/spinner/index.js +1 -0
  74. package/dist/components/ui/spinner/spinner.d.ts +5 -0
  75. package/dist/components/ui/spinner/spinner.js +8 -0
  76. package/dist/components/ui/switch/index.d.ts +1 -0
  77. package/dist/components/ui/switch/index.js +1 -0
  78. package/dist/components/ui/switch/switch.d.ts +7 -0
  79. package/dist/components/ui/switch/switch.js +15 -0
  80. package/dist/components/ui/tabs/index.d.ts +1 -0
  81. package/dist/components/ui/tabs/index.js +1 -0
  82. package/dist/components/ui/tabs/tabs.d.ts +14 -0
  83. package/dist/components/ui/tabs/tabs.js +7 -0
  84. package/dist/components/ui/textarea/index.d.ts +1 -0
  85. package/dist/components/ui/textarea/index.js +1 -0
  86. package/dist/components/ui/textarea/textarea.d.ts +5 -0
  87. package/dist/components/ui/textarea/textarea.js +6 -0
  88. package/dist/components/ui/toast/index.d.ts +1 -0
  89. package/dist/components/ui/toast/index.js +1 -0
  90. package/dist/components/ui/toast/toast.d.ts +19 -0
  91. package/dist/components/ui/toast/toast.js +20 -0
  92. package/dist/components/ui/tooltip/index.d.ts +1 -0
  93. package/dist/components/ui/tooltip/index.js +1 -0
  94. package/dist/components/ui/tooltip/tooltip.d.ts +12 -0
  95. package/dist/components/ui/tooltip/tooltip.js +9 -0
  96. package/dist/index.d.ts +27 -0
  97. package/dist/index.js +29 -0
  98. package/dist/lib/color.d.ts +16 -0
  99. package/dist/lib/color.js +118 -0
  100. package/dist/lib/utils.d.ts +7 -0
  101. package/dist/lib/utils.js +5 -0
  102. package/dist/styles/base.css +212 -0
  103. package/dist/styles/theme.css +121 -0
  104. package/dist/styles.css +5 -0
  105. package/package.json +184 -4
package/CONVENTIONS.md ADDED
@@ -0,0 +1,38 @@
1
+ # UI Kit Conventions (Solid / Clean / Flat / Modern)
2
+
3
+ Single-suite, not-shadcn component kit: **Base UI** for behavior/a11y + owned tokens, styles, and a consistent **prop-driven** API. Visual direction: solid surfaces, clean hierarchy, flat-first depth, modern rounded shapes, vibrant-but-customizable accent (inspired by M3 Expressive, not M3 Expressive).
4
+
5
+ ## Foundations
6
+ - **Semantic tokens only** in components (no app-specific variables, no hardcoded brand paints). Core contract lives in `src/styles/theme.css` and is mapped in `src/styles/base.css`.
7
+ - **Accent is customizable** via CSS vars (`--primary`, `--primary-foreground`, `--primary-hover/active`, `--primary-soft*`, `--ring`). Use `generateAccentVars`/`accentStyle` from `@nphuonha/ui-kit` to derive a palette from one color (light/dark).
8
+ - **Scope**: wrap with `data-ui="kit"` (put on `<html>`/portal root so overlay portals inherit tokens). Dark mode via `.dark`.
9
+
10
+ ## Visual
11
+ - **Flat-first, softly curved**: separate surfaces with tonal solid fills; use a **top-lit rim + micro vertical gradient** for convexity instead of full outlines.
12
+ - `surface-curved` — cards, dialogs, popovers, primary/danger buttons.
13
+ - `surface-curved-soft` — secondary buttons, tabs, alerts, badges, switch tracks.
14
+ - `surface-curved-pill` — switch thumbs and pill controls.
15
+ - `surface-inset` — native fields (input/textarea/select), where `::before` is unavailable.
16
+ - Lighting tokens: `--light-top`, `--light-sheen`, `--light-bottom`, `--light-rim` (defined per light/dark theme).
17
+ - **Shadows only lift, never outline**: keep subtle outer shadows for overlays (`--shadow-overlay`) and raised cards/buttons (`--shadow-soft`/`--shadow-raised`); the curved utilities only add inset edge light.
18
+ - **Shape** via tokens: `--radius-control` (~12px), `--radius-card` (~20px), `--radius-overlay`, `--radius-field`; pill (`rounded-pill`) for actions and switch thumbs.
19
+ - **Typography**: tokenized (`--font-body`), clear hierarchy.
20
+ - **Motion**: restrained 150–220ms, `--ease-out-ui`, CSS transitions for feedback/enter-exit; **no bouncy springs** by default. Honor `prefers-reduced-motion` (global guard in `base.css`).
21
+
22
+ ## Component API
23
+ - **Prop-driven & consistent**: prefer clear props over compound anatomy for core controls.
24
+ - **Controls**: comfortable `h-10` default; shared focus/disabled/error states.
25
+ - **Forms**: `label`, `description`, `error` (with `aria-invalid`/`aria-describedby`/`role="alert"` where relevant), `required` mark.
26
+ - **Icons**: default internal affordances use **Phosphor**. `startIcon`/`endIcon`, `Alert.icon` and `IconButton` children accept any icon component (`IconComponent { size?, className? }`), so user-facing icons are pack-agnostic. The internal set (`check`, `minus`, `chevron`, `close`, `spinner`) is replaceable via `IconProvider`; `Toaster` exposes its own `icons` prop.
27
+ - **Loading**: preserve layout (spinner + label), set `disabled`/`aria-busy`.
28
+
29
+ ## Structure & exports
30
+ - One folder per component: `src/components/ui/<name>/<name>.tsx`, `<name>-variants.ts`, `<name>.test.tsx`, `index.ts`.
31
+ - Extract `cva` into `<name>-variants.ts` **without** `"use client"`, so variants can be used in server components.
32
+ - The folder `index.ts` exports the component and its variants explicitly. The main `src/index.ts` barrel re-exports folders, and `package.json` exposes subpaths (`@nphuonha/ui-kit/<name>`) for smaller client bundles.
33
+ - Keep tests colocated with the component. Prefer a11y smoke tests plus token/scale regressions for components with meaningful state.
34
+
35
+ ## Stack notes
36
+ - Overlays/behavior: `@base-ui/react`. Toast: `sonner` (restyled via tokens).
37
+ - Prefer CSS transitions; avoid adding animation libraries for core MVP unless necessary.
38
+ - When adding components, follow the token contract + these conventions and add/extend colocated tests.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nphuonha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # @nphuonha/ui-kit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Solid, clean, flat, modern React UI kit. **Base UI** for behavior/a11y + owned tokens/styles + a consistent prop-driven API. Accent is vibrant by default and **customizable by color generation** (M3 Expressive-inspired, not M3 Expressive).
4
+
5
+ > Status: MVP rewrite (single suite). Breaking changes from previous `warm-editorial`/`celebration` suites.
6
+
7
+ ## Install / use (workspace or package)
8
+
9
+ ```tsx
10
+ import "@nphuonha/ui-kit/styles.css";
11
+ import { Button, Card, CardContent, Input, Toaster, accentStyle } from "@nphuonha/ui-kit";
12
+
13
+ export function App() {
14
+ return (
15
+ <div data-ui="kit">
16
+ <Toaster />
17
+ <Card>
18
+ <CardContent className="flex gap-2 pt-5">
19
+ <Input placeholder="Email" />
20
+ <Button>Continue</Button>
21
+ </CardContent>
22
+ </Card>
23
+ </div>
24
+ );
25
+ }
26
+ ```
27
+
28
+ - Scope tokens/overlays with `data-ui="kit"` (add to `<html>` for portalled overlays).
29
+ - Dark mode: add `class="dark"` (and/or `data-ui="kit"` dark variant as supported by your setup).
30
+ - Change accent at runtime: `style={accentStyle("#0d9488", "light")}` or `generateAccentVars(color, mode)` to override `--primary*`/`--ring`.
31
+
32
+ ### Framework setup
33
+
34
+ The published package ships compiled ESM + `.d.ts`, so it works with Next.js, Vite, Remix and other bundlers. **No `transpilePackages` is required for the published package.**
35
+
36
+ If you are consuming the TypeScript source from this monorepo instead, add:
37
+
38
+ ```ts
39
+ // next.config.ts
40
+ const nextConfig = {
41
+ transpilePackages: ["@nphuonha/ui-kit"],
42
+ };
43
+
44
+ export default nextConfig;
45
+ ```
46
+
47
+ The published stylesheet scans its own components with `@source "./components"` inside `dist`. If your Tailwind setup ignores `@source` directives from dependencies, add this to your app CSS as a fallback:
48
+
49
+ ```css
50
+ @source "../node_modules/@nphuonha/ui-kit/dist/components";
51
+ ```
52
+
53
+ ## Granular imports
54
+
55
+ The main barrel is convenient. For smaller client bundles, import from a component subpath:
56
+
57
+ ```tsx
58
+ import { Button, buttonVariants } from "@nphuonha/ui-kit/button";
59
+ import { Field } from "@nphuonha/ui-kit/field";
60
+ import { Input } from "@nphuonha/ui-kit/input";
61
+ ```
62
+
63
+ Variants live in server-safe `*-variants.ts` files and can be used from server components.
64
+
65
+ ## Structure
66
+
67
+ ```
68
+ src/components/ui/button/
69
+ button.tsx # "use client" behavior/render
70
+ button-variants.ts # cva, server-safe
71
+ button.test.tsx # colocated test
72
+ index.ts # explicit exports
73
+ ```
74
+
75
+ ## Icons
76
+
77
+ The kit uses **Phosphor** as its default internal icon set (check, minus, chevron, close, spinner). User-facing icons stay icon-agnostic — pass any icon component that accepts `size` and `className`:
78
+
79
+ ```tsx
80
+ <Button startIcon={Plus}>New item</Button>
81
+ <IconButton aria-label="Search"><Search /></IconButton>
82
+ <Alert icon={Info} title="Heads up">...</Alert>
83
+ ```
84
+
85
+ Swap the internal set at any time with `IconProvider`:
86
+
87
+ ```tsx
88
+ import { IconProvider } from "@nphuonha/ui-kit";
89
+ import { Check, ChevronUpDown, Loader, Minus, X } from "your-icon-pack";
90
+
91
+ <IconProvider
92
+ icons={{ check: Check, minus: Minus, chevron: ChevronUpDown, close: X, spinner: Loader }}
93
+ >
94
+ <App />
95
+ </IconProvider>
96
+ ```
97
+
98
+ `Toaster` also accepts an `icons` prop for `success`, `info`, `warning`, `error`, `loading` and `close`.
99
+
100
+ The docs site uses Phosphor too. Swapping to another pack only affects the kit's internal affordances; icons you pass to `Button`, `IconButton` and `Alert` are always yours.
101
+
102
+ ## MVP components
103
+
104
+ `Button`, `IconButton`, `Input`, `Textarea`, `Field`/`Label`, `Checkbox`, `RadioGroup`, `Switch`, `Select`, `Dialog`, `Sheet`, `Popover`, `Tooltip`, `Tabs`, `Card` (+Header/Title/Description/Content/Footer), `Badge`, `Alert`, `Separator`, `Spinner`, `Toaster`/`toast`.
105
+
106
+ ## Scripts
107
+
108
+ ```bash
109
+ npm run build -w @nphuonha/ui-kit # emit dist (ESM + .d.ts + CSS)
110
+ npm run typecheck -w @nphuonha/ui-kit
111
+ npm run test -w @nphuonha/ui-kit
112
+ ```
113
+
114
+ ## Publishing
115
+
116
+ The package builds to `dist` as ESM + `.d.ts` with `npm run build`. `prepack` builds automatically before `npm pack`/`npm publish`; `prepublishOnly` runs typecheck + tests.
117
+
118
+ ```bash
119
+ npm login
120
+ npm publish -w @nphuonha/ui-kit
121
+ ```
122
+
123
+ Or from the repo root:
124
+
125
+ ```bash
126
+ npm run publish:kit
127
+ ```
128
+
129
+ Bump the version with:
130
+
131
+ ```bash
132
+ npm version patch -w @nphuonha/ui-kit
133
+ ```
134
+
135
+ The published package is compiled and works with Vite, Remix, Next.js and plain bundler-based JS apps. The repository's docs app consumes the TypeScript source via a local path alias for fast iteration.
136
+
137
+ See `CONVENTIONS.md` for design/API rules.
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ export type KitIconName = "check" | "minus" | "chevron" | "close" | "spinner";
3
+ export type KitIcon = React.ComponentType<{
4
+ size?: number;
5
+ } & React.SVGProps<SVGSVGElement>>;
6
+ export interface IconProviderProps {
7
+ icons?: Partial<Record<KitIconName, KitIcon>>;
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * Default internal icons come from Phosphor. Swap any of them for another pack:
12
+ *
13
+ * <IconProvider icons={{ check: LucideCheck, close: LucideX }}>
14
+ * <App />
15
+ * </IconProvider>
16
+ */
17
+ export declare function IconProvider({ icons, children }: IconProviderProps): React.JSX.Element;
18
+ export declare function useKitIcon(name: KitIconName): KitIcon;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { CaretUpDownIcon, CheckIcon, MinusIcon, SpinnerIcon, XIcon, } from "@phosphor-icons/react";
5
+ const defaultIcons = {
6
+ check: (props) => _jsx(CheckIcon, { ...props, weight: "bold" }),
7
+ minus: (props) => _jsx(MinusIcon, { ...props, weight: "bold" }),
8
+ chevron: (props) => _jsx(CaretUpDownIcon, { ...props, weight: "bold" }),
9
+ close: (props) => _jsx(XIcon, { ...props, weight: "bold" }),
10
+ spinner: SpinnerIcon,
11
+ };
12
+ const IconContext = React.createContext({});
13
+ /**
14
+ * Default internal icons come from Phosphor. Swap any of them for another pack:
15
+ *
16
+ * <IconProvider icons={{ check: LucideCheck, close: LucideX }}>
17
+ * <App />
18
+ * </IconProvider>
19
+ */
20
+ export function IconProvider({ icons, children }) {
21
+ return _jsx(IconContext.Provider, { value: icons ?? {}, children: children });
22
+ }
23
+ export function useKitIcon(name) {
24
+ const icons = React.useContext(IconContext);
25
+ return icons[name] ?? defaultIcons[name];
26
+ }
@@ -0,0 +1,5 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ export declare const alertVariants: (props?: ({
3
+ variant?: "neutral" | "info" | "success" | "warning" | "danger" | null | undefined;
4
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
5
+ export type AlertVariants = VariantProps<typeof alertVariants>;
@@ -0,0 +1,13 @@
1
+ import { cva } from "class-variance-authority";
2
+ export const alertVariants = cva("surface-curved-soft relative flex w-full items-start gap-3 rounded-card px-4 py-3.5 text-sm", {
3
+ variants: {
4
+ variant: {
5
+ neutral: "bg-surface text-foreground",
6
+ info: "bg-info/10 text-foreground",
7
+ success: "bg-success/10 text-foreground",
8
+ warning: "bg-warning/12 text-foreground",
9
+ danger: "bg-destructive/10 text-foreground",
10
+ },
11
+ },
12
+ defaultVariants: { variant: "neutral" },
13
+ });
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import type { VariantProps } from "class-variance-authority";
3
+ import { type IconComponent } from "../../../lib/utils.js";
4
+ import { alertVariants } from "./alert-variants.js";
5
+ export interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> {
6
+ title?: string;
7
+ icon?: IconComponent;
8
+ }
9
+ export declare function Alert({ className, variant, title, icon: Icon, children, ...props }: AlertProps): React.JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../../lib/utils.js";
3
+ import { alertVariants } from "./alert-variants.js";
4
+ const iconTone = {
5
+ neutral: "text-muted-foreground",
6
+ info: "text-info",
7
+ success: "text-success",
8
+ warning: "text-warning",
9
+ danger: "text-destructive",
10
+ };
11
+ export function Alert({ className, variant = "neutral", title, icon: Icon, children, ...props }) {
12
+ return (_jsxs("div", { role: "status", className: cn(alertVariants({ variant }), className), ...props, children: [Icon ? _jsx(Icon, { size: 18, className: cn("mt-0.5 shrink-0", iconTone[variant ?? "neutral"]) }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [title ? _jsx("div", { className: "font-semibold leading-5 tracking-[-0.01em]", children: title }) : null, children ? _jsx("div", { className: cn("text-muted-foreground", title && "mt-0.5"), children: children }) : null] })] }));
13
+ }
@@ -0,0 +1,2 @@
1
+ export { Alert, type AlertProps } from "./alert.js";
2
+ export { alertVariants, type AlertVariants } from "./alert-variants.js";
@@ -0,0 +1,2 @@
1
+ export { Alert } from "./alert.js";
2
+ export { alertVariants } from "./alert-variants.js";
@@ -0,0 +1,5 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ export declare const badgeVariants: (props?: ({
3
+ variant?: "neutral" | "info" | "success" | "warning" | "danger" | "primary" | "solid" | "outline" | null | undefined;
4
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
5
+ export type BadgeVariants = VariantProps<typeof badgeVariants>;
@@ -0,0 +1,16 @@
1
+ import { cva } from "class-variance-authority";
2
+ export const badgeVariants = cva("surface-curved-soft relative inline-flex items-center gap-1 whitespace-nowrap rounded-pill px-2.5 py-0.5 text-xs font-semibold tracking-[-0.01em] transition-colors", {
3
+ variants: {
4
+ variant: {
5
+ neutral: "bg-surface text-foreground",
6
+ primary: "bg-primary-soft text-primary-soft-foreground",
7
+ success: "bg-success/12 text-success",
8
+ warning: "bg-warning/14 text-warning",
9
+ danger: "bg-destructive/12 text-destructive",
10
+ info: "bg-info/12 text-info",
11
+ solid: "bg-primary text-primary-foreground",
12
+ outline: "bg-transparent text-foreground shadow-[inset_0_0_0_1px_var(--border)]",
13
+ },
14
+ },
15
+ defaultVariants: { variant: "neutral" },
16
+ });
@@ -0,0 +1,6 @@
1
+ import * as React from "react";
2
+ import type { VariantProps } from "class-variance-authority";
3
+ import { badgeVariants } from "./badge-variants.js";
4
+ export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {
5
+ }
6
+ export declare function Badge({ className, variant, ...props }: BadgeProps): React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../lib/utils.js";
3
+ import { badgeVariants } from "./badge-variants.js";
4
+ export function Badge({ className, variant, ...props }) {
5
+ return _jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
6
+ }
@@ -0,0 +1,2 @@
1
+ export { Badge, type BadgeProps } from "./badge.js";
2
+ export { badgeVariants, type BadgeVariants } from "./badge-variants.js";
@@ -0,0 +1,2 @@
1
+ export { Badge } from "./badge.js";
2
+ export { badgeVariants } from "./badge-variants.js";
@@ -0,0 +1,7 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ export declare const buttonVariants: (props?: ({
3
+ variant?: "danger" | "primary" | "outline" | "secondary" | "ghost" | "soft" | null | undefined;
4
+ size?: "icon" | "sm" | "default" | "lg" | "icon-sm" | null | undefined;
5
+ shape?: "rounded" | "pill" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export type ButtonVariants = VariantProps<typeof buttonVariants>;
@@ -0,0 +1,29 @@
1
+ import { cva } from "class-variance-authority";
2
+ export const buttonVariants = cva("relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap text-sm font-semibold tracking-[-0.01em] transition-[background-color,color,box-shadow,transform] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)] outline-none select-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-45 data-[loading]:opacity-80 motion-safe:active:scale-[0.97]", {
3
+ variants: {
4
+ variant: {
5
+ primary: "surface-curved bg-primary text-primary-foreground shadow-[var(--shadow-raised)] hover:bg-primary-hover active:bg-primary-active",
6
+ secondary: "surface-curved-soft bg-field text-field-foreground hover:bg-field-hover",
7
+ outline: "bg-transparent text-foreground shadow-[inset_0_0_0_1.5px_var(--border)] hover:bg-surface",
8
+ ghost: "bg-transparent text-foreground hover:bg-surface active:bg-surface-hover",
9
+ danger: "surface-curved bg-destructive text-destructive-foreground shadow-[var(--shadow-raised)] hover:bg-destructive/90",
10
+ soft: "surface-curved-soft bg-primary-soft text-primary-soft-foreground hover:bg-primary-soft/75",
11
+ },
12
+ size: {
13
+ sm: "h-9 px-3.5 text-[13px]",
14
+ default: "h-10 px-4",
15
+ lg: "h-11 px-5 text-[15px]",
16
+ icon: "size-10 p-0",
17
+ "icon-sm": "size-9 p-0",
18
+ },
19
+ shape: {
20
+ rounded: "rounded-control",
21
+ pill: "rounded-pill",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "primary",
26
+ size: "default",
27
+ shape: "rounded",
28
+ },
29
+ });
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
3
+ import { type ButtonVariants } from "./button-variants.js";
4
+ import { type IconComponent } from "../../../lib/utils.js";
5
+ export interface ButtonProps extends ButtonPrimitive.Props, ButtonVariants {
6
+ loading?: boolean;
7
+ startIcon?: IconComponent;
8
+ endIcon?: IconComponent;
9
+ }
10
+ export declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
5
+ import { Spinner } from "../spinner/index.js";
6
+ import { buttonVariants } from "./button-variants.js";
7
+ import { cn } from "../../../lib/utils.js";
8
+ export const Button = React.forwardRef(({ className, variant, size, shape, loading, startIcon: StartIcon, endIcon: EndIcon, children, disabled, ...props }, ref) => {
9
+ const iconOnly = size === "icon" || size === "icon-sm";
10
+ return (_jsxs(ButtonPrimitive, { ref: ref, disabled: loading || disabled, "aria-busy": loading || undefined, "data-loading": loading || undefined, className: cn(buttonVariants({ variant, size, shape }), className), ...props, children: [loading ? _jsx(Spinner, { size: 16, className: "shrink-0" }) : null, !loading && StartIcon ? _jsx(StartIcon, { size: 16, className: "shrink-0" }) : null, children != null && children !== "" && !(loading && iconOnly) ? (iconOnly ? (_jsx("span", { "aria-hidden": "true", className: "inline-flex items-center justify-center", children: children })) : (_jsx("span", { className: "truncate", children: children }))) : null, !loading && EndIcon ? _jsx(EndIcon, { size: 16, className: "shrink-0" }) : null] }));
11
+ });
12
+ Button.displayName = "Button";
@@ -0,0 +1,2 @@
1
+ export { Button, type ButtonProps } from "./button.js";
2
+ export { buttonVariants, type ButtonVariants } from "./button-variants.js";
@@ -0,0 +1,2 @@
1
+ export { Button } from "./button.js";
2
+ export { buttonVariants } from "./button-variants.js";
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ export declare function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
3
+ export declare function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
4
+ export declare function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>): React.JSX.Element;
5
+ export declare function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>): React.JSX.Element;
6
+ export declare function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
7
+ export declare function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../../lib/utils.js";
3
+ export function Card({ className, ...props }) {
4
+ return (_jsx("div", { className: cn("surface-curved relative rounded-card bg-card text-card-foreground shadow-[var(--shadow-soft)]", className), ...props }));
5
+ }
6
+ export function CardHeader({ className, ...props }) {
7
+ return _jsx("div", { className: cn("flex flex-col gap-1.5 p-6 pb-0", className), ...props });
8
+ }
9
+ export function CardTitle({ className, ...props }) {
10
+ return _jsx("h3", { className: cn("text-base font-semibold tracking-[-0.015em] leading-tight", className), ...props });
11
+ }
12
+ export function CardDescription({ className, ...props }) {
13
+ return _jsx("p", { className: cn("text-sm leading-relaxed text-muted-foreground", className), ...props });
14
+ }
15
+ export function CardContent({ className, ...props }) {
16
+ return _jsx("div", { className: cn("p-6", className), ...props });
17
+ }
18
+ export function CardFooter({ className, ...props }) {
19
+ return _jsx("div", { className: cn("flex items-center gap-2 p-6 pt-0", className), ...props });
20
+ }
@@ -0,0 +1 @@
1
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./card.js";
@@ -0,0 +1 @@
1
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./card.js";
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
3
+ export interface CheckboxProps extends Omit<React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>, "children"> {
4
+ label?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ error?: boolean;
7
+ }
8
+ export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
5
+ import { cn } from "../../../lib/utils.js";
6
+ import { useKitIcon } from "../_shared/icon-provider.js";
7
+ export const Checkbox = React.forwardRef(({ className, label, description, error, id, ...props }, ref) => {
8
+ const CheckIcon = useKitIcon("check");
9
+ const MinusIcon = useKitIcon("minus");
10
+ const autoId = React.useId();
11
+ const controlId = id ?? autoId;
12
+ const descriptionId = description ? `${controlId}-description` : undefined;
13
+ const control = (_jsx(CheckboxPrimitive.Root, { ref: ref, id: controlId, "aria-invalid": error || undefined, "aria-describedby": descriptionId, className: cn("surface-inset peer inline-flex size-5 shrink-0 items-center justify-center rounded-[7px] bg-field text-primary-foreground transition-[background-color,box-shadow] duration-[var(--duration-ui)] ease-[var(--ease-out-ui)] outline-none focus-visible:ring-2 focus-visible:ring-ring/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[checked]:bg-primary data-[indeterminate]:bg-primary disabled:cursor-not-allowed disabled:opacity-45", error && "bg-destructive/15 data-[checked]:bg-destructive", className), ...props, children: _jsxs(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current data-[unchecked]:hidden", children: [_jsx(CheckIcon, { size: 13, className: "in-data-[indeterminate]:hidden" }), _jsx(MinusIcon, { size: 13, className: "hidden in-data-[indeterminate]:block" })] }) }));
14
+ if (!label && !description)
15
+ return control;
16
+ return (_jsxs("div", { className: "flex items-start gap-2.5", children: [control, _jsxs("div", { className: "grid gap-0.5 pt-0.5", children: [label ? (_jsx("label", { htmlFor: controlId, className: "cursor-pointer text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-45", children: label })) : null, description ? _jsx("p", { id: descriptionId, className: "text-xs text-muted-foreground", children: description }) : null] })] }));
17
+ });
18
+ Checkbox.displayName = "Checkbox";
@@ -0,0 +1 @@
1
+ export { Checkbox, type CheckboxProps } from "./checkbox.js";
@@ -0,0 +1 @@
1
+ export { Checkbox } from "./checkbox.js";
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ export interface DialogProps {
3
+ open?: boolean;
4
+ defaultOpen?: boolean;
5
+ onOpenChange?: (open: boolean) => void;
6
+ trigger?: React.ReactElement;
7
+ title?: React.ReactNode;
8
+ description?: React.ReactNode;
9
+ children: React.ReactNode;
10
+ footer?: React.ReactNode;
11
+ className?: string;
12
+ showClose?: boolean;
13
+ "aria-label"?: string;
14
+ }
15
+ export declare function Dialog({ open, defaultOpen, onOpenChange, trigger, title, description, children, footer, className, showClose, "aria-label": ariaLabel, }: DialogProps): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
4
+ import { cn } from "../../../lib/utils.js";
5
+ import { IconButton } from "../icon-button/index.js";
6
+ import { useKitIcon } from "../_shared/icon-provider.js";
7
+ export function Dialog({ open, defaultOpen, onOpenChange, trigger, title, description, children, footer, className, showClose = true, "aria-label": ariaLabel, }) {
8
+ const CloseIcon = useKitIcon("close");
9
+ return (_jsxs(DialogPrimitive.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger ? _jsx(DialogPrimitive.Trigger, { render: trigger }) : null, _jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-overlay backdrop-blur-[2px] transition-opacity duration-[var(--duration-ui-slow)] data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(DialogPrimitive.Popup, { "aria-label": ariaLabel, className: cn("surface-curved fixed left-1/2 top-1/2 z-50 grid w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 grid-cols-1 gap-5 rounded-overlay bg-popover p-6 text-popover-foreground shadow-[var(--shadow-overlay)] outline-none", "transition-[opacity,transform] duration-[var(--duration-ui-slow)] ease-[var(--ease-out-ui)]", "data-[starting-style]:opacity-0 data-[starting-style]:-translate-x-1/2 data-[starting-style]:-translate-y-[47%] data-[starting-style]:scale-[0.97]", "data-[ending-style]:opacity-0 data-[ending-style]:-translate-x-1/2 data-[ending-style]:-translate-y-[47%] data-[ending-style]:scale-[0.97]", className), children: [(title || description || showClose) && (_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "grid gap-1", children: [title ? _jsx(DialogPrimitive.Title, { className: "text-lg font-semibold tracking-[-0.02em] leading-tight", children: title }) : null, description ? _jsx(DialogPrimitive.Description, { className: "text-sm leading-relaxed text-muted-foreground", children: description }) : null] }), showClose ? (_jsx(DialogPrimitive.Close, { render: _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "icon-sm", className: "-mr-2 -mt-2 shrink-0", children: _jsx(CloseIcon, { size: 16 }) }) })) : null] })), _jsx("div", { className: "min-w-0", children: children }), footer ? _jsx("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: footer }) : null] })] })] }));
10
+ }
@@ -0,0 +1 @@
1
+ export { Dialog, type DialogProps } from "./dialog.js";
@@ -0,0 +1 @@
1
+ export { Dialog } from "./dialog.js";
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ export interface FieldProps {
3
+ id?: string;
4
+ label?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ error?: React.ReactNode;
7
+ required?: boolean;
8
+ className?: string;
9
+ children: React.ReactNode;
10
+ }
11
+ export declare function Field({ id, label, description, error, required, className, children }: FieldProps): React.JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../../lib/utils.js";
5
+ import { Label } from "../label/index.js";
6
+ export function Field({ id, label, description, error, required, className, children }) {
7
+ const autoId = React.useId();
8
+ const controlId = id ?? autoId;
9
+ const descriptionId = description ? `${controlId}-description` : undefined;
10
+ const errorId = error ? `${controlId}-error` : undefined;
11
+ const describedBy = [descriptionId, errorId].filter(Boolean).join(" ") || undefined;
12
+ const child = React.isValidElement(children)
13
+ ? React.cloneElement(children, {
14
+ id: children.props.id ?? controlId,
15
+ "aria-describedby": [children.props["aria-describedby"], describedBy]
16
+ .filter(Boolean)
17
+ .join(" ") || undefined,
18
+ "aria-invalid": error ? true : children.props["aria-invalid"],
19
+ })
20
+ : children;
21
+ return (_jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [label ? (_jsx(Label, { htmlFor: controlId, requiredMark: required, children: label })) : null, child, description ? (_jsx("p", { id: descriptionId, className: "text-xs text-muted-foreground", children: description })) : null, error ? (_jsx("p", { id: errorId, role: "alert", className: "text-xs font-medium text-destructive", children: error })) : null] }));
22
+ }
@@ -0,0 +1 @@
1
+ export { Field, type FieldProps } from "./field.js";
@@ -0,0 +1 @@
1
+ export { Field } from "./field.js";
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ import { type ButtonProps } from "../button/index.js";
3
+ export interface IconButtonProps extends Omit<ButtonProps, "startIcon" | "endIcon" | "children"> {
4
+ "aria-label": string;
5
+ children: React.ReactNode;
6
+ }
7
+ export declare const IconButton: React.ForwardRefExoticComponent<Omit<IconButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "../button/index.js";
5
+ import { cn } from "../../../lib/utils.js";
6
+ export const IconButton = React.forwardRef(({ className, size = "icon", shape = "rounded", children, ...props }, ref) => (_jsx(Button, { ref: ref, size: size, shape: shape, className: cn(className), ...props, children: children })));
7
+ IconButton.displayName = "IconButton";
@@ -0,0 +1 @@
1
+ export { IconButton, type IconButtonProps } from "./icon-button.js";
@@ -0,0 +1 @@
1
+ export { IconButton } from "./icon-button.js";
@@ -0,0 +1 @@
1
+ export { Input, type InputProps } from "./input.js";
@@ -0,0 +1 @@
1
+ export { Input } from "./input.js";
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
3
+ error?: boolean;
4
+ }
5
+ export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;