@preset.nz/ux-kit 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -2
  3. package/package.json +51 -3
  4. package/src/components/avatar.tsx +35 -0
  5. package/src/components/badge.tsx +42 -0
  6. package/src/components/breadcrumb.tsx +102 -0
  7. package/src/components/button.tsx +56 -0
  8. package/src/components/card.tsx +92 -0
  9. package/src/components/checkbox.tsx +27 -0
  10. package/src/components/color-field.tsx +248 -0
  11. package/src/components/color-swatch.tsx +52 -0
  12. package/src/components/context-menu.tsx +249 -0
  13. package/src/components/dialog.tsx +137 -0
  14. package/src/components/dropdown-menu.tsx +249 -0
  15. package/src/components/empty-state.tsx +32 -0
  16. package/src/components/input.tsx +20 -0
  17. package/src/components/label.tsx +18 -0
  18. package/src/components/navigation-menu.tsx +144 -0
  19. package/src/components/number-field.tsx +428 -0
  20. package/src/components/popover.tsx +82 -0
  21. package/src/components/select.tsx +201 -0
  22. package/src/components/separator.tsx +25 -0
  23. package/src/components/sheet.tsx +124 -0
  24. package/src/components/side-panel.tsx +166 -0
  25. package/src/components/sidebar.tsx +735 -0
  26. package/src/components/skeleton.tsx +14 -0
  27. package/src/components/slider.tsx +75 -0
  28. package/src/components/snackbar.tsx +141 -0
  29. package/src/components/status-bar.tsx +38 -0
  30. package/src/components/tabs.tsx +78 -0
  31. package/src/components/textarea.tsx +18 -0
  32. package/src/components/toggle-group.tsx +86 -0
  33. package/src/components/toggle.tsx +43 -0
  34. package/src/components/toolbar.tsx +307 -0
  35. package/src/components/tooltip.tsx +54 -0
  36. package/src/graphics/adsr.ts +70 -0
  37. package/src/graphics/curve-editor.tsx +525 -0
  38. package/src/graphics/curve-edits.ts +129 -0
  39. package/src/graphics/index.ts +16 -0
  40. package/src/graphics/scale.ts +22 -0
  41. package/src/graphics/use-canvas.ts +56 -0
  42. package/src/graphics/use-tokens.ts +32 -0
  43. package/src/icons.ts +3 -0
  44. package/src/index.css +199 -0
  45. package/src/index.ts +37 -0
  46. package/src/lib/use-mobile.ts +18 -0
  47. package/src/lib/utils.ts +6 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 preset.nz
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,91 @@
1
- # Temporary Holding Version
1
+ # @preset.nz/ux-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
+ One look and one way of working for the preset.nz desktop apps: tokens, palettes, fonts and Base UI primitives, in the studio's colours.
4
+
5
+ Pre-1.0: a minor version can break things, a patch never does.
6
+
7
+ ## What is in it
8
+
9
+ - `src/index.css`: the colour variables (light and dark), the type scale and the font imports.
10
+ - `src/components/`: one module per primitive, on Base UI where Base UI has the part, plain React and Tailwind where it does not. See the table below.
11
+ - `src/icons.ts`: Phosphor, the kit's icon set.
12
+ - `playground/`: a Tauri and React app that shows the kit. It is the only place that touches rhizome; the kit does not depend on it.
13
+
14
+ Like [facets](https://github.com/preset-nz/facets), the kit ships unbuilt TypeScript. The consumer's Vite compiles it.
15
+
16
+ ## Components
17
+
18
+ Union of the `src/components/ui` folders in Strata, Oblique, Shard and Map & Territory (Fault has none). Where several apps had the same component, the richest variant was taken. All are exported from the package index.
19
+
20
+ | Component | In kit | Came from | Notes |
21
+ |---|---|---|---|
22
+ | button | yes | Strata, Shard, M&T | Base UI Button |
23
+ | checkbox | yes | Strata, Oblique, Shard | |
24
+ | input | yes | all four | |
25
+ | label | yes | Strata, Oblique, Shard | |
26
+ | select | yes | Strata, Oblique, Shard | |
27
+ | separator | yes | all four | |
28
+ | dialog | yes | Oblique, Shard, M&T | Shard's themed variant. `render={<Button />}` replaces `asChild` |
29
+ | sheet | yes | M&T | Base UI Dialog docked to an edge (`side`) |
30
+ | popover | yes | Oblique | `PopoverAnchor` dropped: Base UI anchors through the Positioner's `anchor` prop |
31
+ | tooltip | yes | Oblique, M&T | `TooltipProvider delay` replaces `delayDuration` |
32
+ | tabs | yes | Oblique | `TabsTrigger` / `TabsContent` wrap Base UI `Tab` / `Panel`. Orientation uses `data-[orientation=...]` |
33
+ | toggle | yes | Oblique | `data-pressed` replaces `data-[state=on]` |
34
+ | toggle-group | yes | Oblique | Base UI `value` is always an array and `multiple` replaces `type="single" \| "multiple"` |
35
+ | slider | yes | Oblique, Shard, M&T | Takes a number or an array; one thumb per value |
36
+ | dropdown-menu | yes | Shard, M&T | M&T's fuller variant. `DropdownMenuLabel` is Base UI's `GroupLabel`, so it must sit inside a `DropdownMenuGroup` |
37
+ | context-menu | yes | Shard | Same shape as dropdown-menu, same `Label` caveat |
38
+ | navigation-menu | yes | M&T | Always the shared-viewport form. Radix's `NavigationMenuIndicator` and the `viewport={false}` mode are gone |
39
+ | snackbar | yes | Strata, Oblique | The kit's one toast, on Base UI Toast. `useSnackbar().show({ message, kind, description, action, persistent, timeout })` returns an id and `dismiss(id?)` closes one or all; kinds are `plain`, `success`, `warning`, `error`. Mount `SnackbarProvider` once and a `SnackbarViewport` inside it. `notify(opts)` and `dismissNotification(id?)` do the same from outside React |
40
+ | avatar | yes | M&T | Base UI Avatar |
41
+ | badge | yes | M&T | Plain, `render` prop replaces `asChild` |
42
+ | breadcrumb | yes | M&T | Plain `<a>`, no `next/link`. Use `render` for a router link |
43
+ | card | yes | M&T | Plain |
44
+ | skeleton | yes | M&T | Plain |
45
+ | textarea | yes | M&T | Plain, restyled to match Input |
46
+ | sidebar | yes | M&T | Plain, 700 lines. `asChild` became `render`. `useIsMobile` moved into the kit. Untested beyond the `collapsible="none"` form in the playground |
47
+ | toolbar | yes | Oblique, M&T | Base UI Toolbar (roving focus). `ToolbarItems` takes `groups` of `ToolbarItemSpec` (id, label, icon, shortcut, enabled, disabledReason, pressed, menu) and `onCommand(id)`; Oblique's `ToolButtonSpec` table with M&T's disabled reason. Disabled items stay focusable so the tooltip can say why. `ToolbarButton`, `ToolbarGroup`, `ToolbarSeparator`, `ToolbarSpacer` compose by hand. Shard's header buttons are inline JSX and Fault's are not a toolbar, so neither contributed |
48
+ | color-field | yes | M&T, Oblique, Strata | Swatch, hex text (commits on blur or Enter, bad input reverts), optional alpha (`#rrggbbaa`), preset grid in a Popover, native picker with `onPickStart`/`onPickEnd` for undo transactions (Oblique), `clearable` and null value (Strata, Oblique's optional colour), read-only, `trailing` slot for an eyedropper |
49
+ | number-field | yes | Base UI, Oblique | Base UI NumberField with the label as the ScrubArea (drag it; click it to type). Shift is coarse (x10), Alt/Option fine (x0.1), as Oblique's InputNumber, and `integer` keeps Alt on whole steps (integerStepFix). `onValueChange(value, { reason })` is `scrub`, `step` or `typed` (typed commits on blur or Enter, never per keystroke; Escape reverts). `onScrubStart`/`onScrubEnd` bracket one drag as the undo transaction, `onScrubCancel` is Escape mid-drag. `VectorField` is N of them with X/Y/Z/W labels. Scrubbing only on the label: no gutter scrub |
50
+ | color-swatch | yes | Strata, M&T, Oblique | Chip over a checkerboard; `null` is the dashed empty chip |
51
+ | side-panel | yes | Strata, M&T | Docked panel, props only (`open`, `width` owned by the app). Collapsed it leaves an icon rail that reopens it; the inner edge resizes by pointer drag (pointer capture) or arrow keys, clamped by `clampWidth`. `SidePanelHeader`, `SidePanelContent`. Strata's `SidePanel` ideas; persistence stays app-side |
52
+ | status-bar | yes | Oblique, Strata, M&T | `StatusBar`, `StatusItem` (optional label), `StatusSpacer`. Contents stay app-side |
53
+ | empty-state | yes | new | Icon, title, description, action slot |
54
+
55
+ M&T's `src/palettes/` (presets and derivation) was considered for the colour field and left out: it is map domain data and stays in the app, which passes its colours in through `presets`.
56
+
57
+ ## Using it
58
+
59
+ ```sh
60
+ pnpm add @preset.nz/ux-kit
61
+ ```
62
+
63
+ Then, in the app's CSS:
64
+
65
+ ```css
66
+ @import "tailwindcss";
67
+ @source "../node_modules/@preset.nz/ux-kit/src";
68
+ @import "@preset.nz/ux-kit/index.css";
69
+ ```
70
+
71
+ ```ts
72
+ // vite.config.ts
73
+ resolve: { dedupe: ["react", "react-dom", "@tauri-apps/api", "@preset.nz/ux-kit"] }
74
+ ```
75
+
76
+ Without the `@source` line the build passes and the components render unstyled, because Tailwind does not scan `node_modules`. React 19 is a peer.
77
+
78
+ [facets](https://github.com/preset-nz/facets) 0.2 draws its number, slider and colour fields with the host app's components. An app on the kit re-exports them, for example `export { NumberField } from "@preset.nz/ux-kit"` in `src/components/ui/number-field.tsx`.
79
+
80
+ ## Working on it
81
+
82
+ ```sh
83
+ just install # pnpm install
84
+ just dev # the playground as a desktop app
85
+ just check # typecheck and lint the kit
86
+ just check-all # the kit, the playground and cargo check
87
+ ```
88
+
89
+ The playground links the sibling packages (rhizome, app-kit, preferences, facets, math), so `just dev` and `just check-all` need them checked out next to this repo. `just check` needs only this repo.
90
+
91
+ MIT.
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@preset.nz/ux-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "One look and one way of working for the preset.nz desktop apps: tokens, palettes, fonts and Base UI primitives.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/preset-nz/ux-kit.git"
9
+ },
10
+ "type": "module",
11
+ "sideEffects": [
12
+ "**/*.css"
13
+ ],
14
+ "exports": {
15
+ ".": "./src/index.ts",
16
+ "./graphics": "./src/graphics/index.ts",
17
+ "./index.css": "./src/index.css"
18
+ },
19
+ "files": [
20
+ "src"
21
+ ],
22
+ "peerDependencies": {
23
+ "react": "^19",
24
+ "react-dom": "^19"
25
+ },
26
+ "dependencies": {
27
+ "@base-ui/react": "^1.6.0",
28
+ "@fontsource-variable/geist": "^5.2.9",
29
+ "@fontsource-variable/jetbrains-mono": "^5.2.8",
30
+ "@phosphor-icons/react": "^2.1.10",
31
+ "@preset.nz/math": "^0.1.0",
32
+ "class-variance-authority": "^0.7.1",
33
+ "clsx": "^2.1.1",
34
+ "shadcn": "^4.11.0",
35
+ "tailwind-merge": "^3.6.0",
36
+ "tw-animate-css": "^1.4.0"
37
+ },
38
+ "devDependencies": {
39
+ "@eslint/js": "^9.39.4",
40
+ "@types/react": "^19.2.17",
41
+ "@types/react-dom": "^19.2.3",
42
+ "eslint": "^9.39.4",
43
+ "eslint-plugin-react-hooks": "^7.1.1",
44
+ "eslint-plugin-react-refresh": "^0.5.3",
45
+ "globals": "^16.5.0",
46
+ "react": "^19.2.7",
47
+ "react-dom": "^19.2.7",
48
+ "typescript": "~5.9.3",
49
+ "typescript-eslint": "^8.61.1"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
6
54
  }
@@ -0,0 +1,35 @@
1
+ import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
2
+
3
+ import { cn } from "../lib/utils"
4
+
5
+ function Avatar({ className, ...props }: AvatarPrimitive.Root.Props) {
6
+ return (
7
+ <AvatarPrimitive.Root
8
+ data-slot="avatar"
9
+ className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
16
+ return (
17
+ <AvatarPrimitive.Image
18
+ data-slot="avatar-image"
19
+ className={cn("aspect-square size-full", className)}
20
+ {...props}
21
+ />
22
+ )
23
+ }
24
+
25
+ function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) {
26
+ return (
27
+ <AvatarPrimitive.Fallback
28
+ data-slot="avatar-fallback"
29
+ className={cn("flex size-full items-center justify-center rounded-full bg-muted", className)}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ export { Avatar, AvatarImage, AvatarFallback }
@@ -0,0 +1,42 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "../lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "border-transparent bg-primary text-primary-foreground [a]:hover:bg-primary/90",
13
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a]:hover:bg-secondary/90",
14
+ destructive:
15
+ "border-transparent bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/90",
16
+ outline: "text-foreground [a]:hover:bg-accent [a]:hover:text-accent-foreground",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ }
23
+ )
24
+
25
+ function Badge({
26
+ className,
27
+ variant = "default",
28
+ render,
29
+ ...props
30
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
31
+ return useRender({
32
+ defaultTagName: "span",
33
+ props: mergeProps<"span">(
34
+ { className: cn(badgeVariants({ variant }), className) },
35
+ props
36
+ ),
37
+ render,
38
+ state: { slot: "badge", variant },
39
+ })
40
+ }
41
+
42
+ export { Badge, badgeVariants }
@@ -0,0 +1,102 @@
1
+ import * as React from "react"
2
+ import { mergeProps } from "@base-ui/react/merge-props"
3
+ import { useRender } from "@base-ui/react/use-render"
4
+ import { CaretRightIcon, DotsThreeIcon } from "@phosphor-icons/react"
5
+
6
+ import { cn } from "../lib/utils"
7
+
8
+ function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
9
+ return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
10
+ }
11
+
12
+ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
13
+ return (
14
+ <ol
15
+ data-slot="breadcrumb-list"
16
+ className={cn(
17
+ "flex flex-wrap items-center gap-1.5 text-sm break-words text-muted-foreground sm:gap-2.5",
18
+ className
19
+ )}
20
+ {...props}
21
+ />
22
+ )
23
+ }
24
+
25
+ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
26
+ return (
27
+ <li
28
+ data-slot="breadcrumb-item"
29
+ className={cn("inline-flex items-center gap-1.5", className)}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ // Plain anchor by default. Pass `render={<RouterLink to="..." />}` for a router link.
36
+ function BreadcrumbLink({
37
+ className,
38
+ render,
39
+ ...props
40
+ }: useRender.ComponentProps<"a">) {
41
+ return useRender({
42
+ defaultTagName: "a",
43
+ props: mergeProps<"a">(
44
+ { className: cn("transition-colors hover:text-foreground", className) },
45
+ props
46
+ ),
47
+ render,
48
+ state: { slot: "breadcrumb-link" },
49
+ })
50
+ }
51
+
52
+ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
53
+ return (
54
+ <span
55
+ data-slot="breadcrumb-page"
56
+ role="link"
57
+ aria-disabled="true"
58
+ aria-current="page"
59
+ className={cn("font-normal text-foreground", className)}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) {
66
+ return (
67
+ <li
68
+ data-slot="breadcrumb-separator"
69
+ role="presentation"
70
+ aria-hidden="true"
71
+ className={cn("[&>svg]:size-3.5", className)}
72
+ {...props}
73
+ >
74
+ {children ?? <CaretRightIcon />}
75
+ </li>
76
+ )
77
+ }
78
+
79
+ function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">) {
80
+ return (
81
+ <span
82
+ data-slot="breadcrumb-ellipsis"
83
+ role="presentation"
84
+ aria-hidden="true"
85
+ className={cn("flex size-9 items-center justify-center", className)}
86
+ {...props}
87
+ >
88
+ <DotsThreeIcon className="size-4" />
89
+ <span className="sr-only">More</span>
90
+ </span>
91
+ )
92
+ }
93
+
94
+ export {
95
+ Breadcrumb,
96
+ BreadcrumbList,
97
+ BreadcrumbItem,
98
+ BreadcrumbLink,
99
+ BreadcrumbPage,
100
+ BreadcrumbSeparator,
101
+ BreadcrumbEllipsis,
102
+ }
@@ -0,0 +1,56 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "../lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-sm border border-transparent bg-clip-padding font-heading text-xs font-medium tracking-wide uppercase whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
12
+ outline:
13
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
14
+ secondary:
15
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
16
+ ghost:
17
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
18
+ destructive:
19
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ size: {
23
+ default:
24
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
25
+ xs: "h-6 gap-1 rounded-sm px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
26
+ sm: "h-7 gap-1 rounded-sm px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
27
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
28
+ icon: "size-8",
29
+ "icon-xs": "size-6 rounded-sm [&_svg:not([class*='size-'])]:size-3",
30
+ "icon-sm": "size-7 rounded-sm",
31
+ "icon-lg": "size-9",
32
+ },
33
+ },
34
+ defaultVariants: {
35
+ variant: "default",
36
+ size: "default",
37
+ },
38
+ }
39
+ )
40
+
41
+ function Button({
42
+ className,
43
+ variant = "default",
44
+ size = "default",
45
+ ...props
46
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
47
+ return (
48
+ <ButtonPrimitive
49
+ data-slot="button"
50
+ className={cn(buttonVariants({ variant, size, className }))}
51
+ {...props}
52
+ />
53
+ )
54
+ }
55
+
56
+ export { Button, buttonVariants }
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../lib/utils"
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-4 rounded-xl border py-4 shadow-sm",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-4",
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className,
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-4", className)}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-4 [.border-t]:pt-4", className)}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ }
@@ -0,0 +1,27 @@
1
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
2
+
3
+ import { cn } from "../lib/utils"
4
+ import { CheckIcon } from "@phosphor-icons/react"
5
+
6
+ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
7
+ return (
8
+ <CheckboxPrimitive.Root
9
+ data-slot="checkbox"
10
+ className={cn(
11
+ "peer relative flex size-4 shrink-0 items-center justify-center rounded-none border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
12
+ className
13
+ )}
14
+ {...props}
15
+ >
16
+ <CheckboxPrimitive.Indicator
17
+ data-slot="checkbox-indicator"
18
+ className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
19
+ >
20
+ <CheckIcon
21
+ />
22
+ </CheckboxPrimitive.Indicator>
23
+ </CheckboxPrimitive.Root>
24
+ )
25
+ }
26
+
27
+ export { Checkbox }