diff --git a/components.json b/components.json
new file mode 100644
index 0000000..ce18a67
--- /dev/null
+++ b/components.json
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://shadcn-svelte.com/schema.json",
+ "style": "new-york",
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "src/app.css",
+ "baseColor": "zinc"
+ },
+ "aliases": {
+ "components": "$lib/components",
+ "utils": "$lib/utils"
+ }
+}
\ No newline at end of file
diff --git a/src/lib/components/ui/avatar/avatar-fallback.svelte b/src/lib/components/ui/avatar/avatar-fallback.svelte
new file mode 100644
index 0000000..3b9b24c
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-fallback.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte
new file mode 100644
index 0000000..27b0e7d
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-image.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte
new file mode 100644
index 0000000..b8a6ee1
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts
new file mode 100644
index 0000000..b08c780
--- /dev/null
+++ b/src/lib/components/ui/avatar/index.ts
@@ -0,0 +1,13 @@
+import Root from "./avatar.svelte";
+import Image from "./avatar-image.svelte";
+import Fallback from "./avatar-fallback.svelte";
+
+export {
+ Root,
+ Image,
+ Fallback,
+ //
+ Root as Avatar,
+ Image as AvatarImage,
+ Fallback as AvatarFallback
+};
diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte
new file mode 100644
index 0000000..a128f14
--- /dev/null
+++ b/src/lib/components/ui/button/button.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts
new file mode 100644
index 0000000..9787e3e
--- /dev/null
+++ b/src/lib/components/ui/button/index.ts
@@ -0,0 +1,52 @@
+import type { Button as ButtonPrimitive } from "bits-ui";
+import { tv, type VariantProps } from "tailwind-variants";
+import Root from "./button.svelte";
+
+const buttonVariants = tv({
+ base: "inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline"
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9"
+ }
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default"
+ }
+});
+
+type Variant = VariantProps["variant"];
+type Size = VariantProps["size"];
+
+type Props = ButtonPrimitive.Props & {
+ variant?: Variant;
+ size?: Size;
+};
+
+type Events = ButtonPrimitive.Events;
+
+export {
+ Root,
+ type Props,
+ type Events,
+ //
+ Root as Button,
+ type Props as ButtonProps,
+ type Events as ButtonEvents,
+ buttonVariants
+};
diff --git a/src/lib/components/ui/card/card-content.svelte b/src/lib/components/ui/card/card-content.svelte
new file mode 100644
index 0000000..2388f2b
--- /dev/null
+++ b/src/lib/components/ui/card/card-content.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/card/card-description.svelte b/src/lib/components/ui/card/card-description.svelte
new file mode 100644
index 0000000..906782a
--- /dev/null
+++ b/src/lib/components/ui/card/card-description.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/card/card-footer.svelte b/src/lib/components/ui/card/card-footer.svelte
new file mode 100644
index 0000000..dab6252
--- /dev/null
+++ b/src/lib/components/ui/card/card-footer.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/card/card-header.svelte b/src/lib/components/ui/card/card-header.svelte
new file mode 100644
index 0000000..d7412d7
--- /dev/null
+++ b/src/lib/components/ui/card/card-header.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/card/card-title.svelte b/src/lib/components/ui/card/card-title.svelte
new file mode 100644
index 0000000..e87b5c6
--- /dev/null
+++ b/src/lib/components/ui/card/card-title.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte
new file mode 100644
index 0000000..4a11395
--- /dev/null
+++ b/src/lib/components/ui/card/card.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/src/lib/components/ui/card/index.ts b/src/lib/components/ui/card/index.ts
new file mode 100644
index 0000000..14a7f72
--- /dev/null
+++ b/src/lib/components/ui/card/index.ts
@@ -0,0 +1,24 @@
+import Root from "./card.svelte";
+import Content from "./card-content.svelte";
+import Description from "./card-description.svelte";
+import Footer from "./card-footer.svelte";
+import Header from "./card-header.svelte";
+import Title from "./card-title.svelte";
+
+export {
+ Root,
+ Content,
+ Description,
+ Footer,
+ Header,
+ Title,
+ //
+ Root as Card,
+ Content as CardContent,
+ Description as CardDescription,
+ Footer as CardFooter,
+ Header as CardHeader,
+ Title as CardTitle
+};
+
+export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte
new file mode 100644
index 0000000..9f78e8a
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-content.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+ Close
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte
new file mode 100644
index 0000000..7250e09
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-description.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte
new file mode 100644
index 0000000..36c0cca
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-footer.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte
new file mode 100644
index 0000000..82fbe51
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-header.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte
new file mode 100644
index 0000000..62f8f3f
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-overlay.svelte
@@ -0,0 +1,24 @@
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-portal.svelte b/src/lib/components/ui/dialog/dialog-portal.svelte
new file mode 100644
index 0000000..400e62b
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-portal.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte
new file mode 100644
index 0000000..fb863c9
--- /dev/null
+++ b/src/lib/components/ui/dialog/dialog-title.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts
new file mode 100644
index 0000000..c05c48e
--- /dev/null
+++ b/src/lib/components/ui/dialog/index.ts
@@ -0,0 +1,34 @@
+import { Dialog as DialogPrimitive } from "bits-ui";
+
+const Root = DialogPrimitive.Root;
+const Trigger = DialogPrimitive.Trigger;
+
+import Title from "./dialog-title.svelte";
+import Portal from "./dialog-portal.svelte";
+import Footer from "./dialog-footer.svelte";
+import Header from "./dialog-header.svelte";
+import Overlay from "./dialog-overlay.svelte";
+import Content from "./dialog-content.svelte";
+import Description from "./dialog-description.svelte";
+
+export {
+ Root,
+ Title,
+ Portal,
+ Footer,
+ Header,
+ Trigger,
+ Overlay,
+ Content,
+ Description,
+ //
+ Root as Dialog,
+ Title as DialogTitle,
+ Portal as DialogPortal,
+ Footer as DialogFooter,
+ Header as DialogHeader,
+ Trigger as DialogTrigger,
+ Overlay as DialogOverlay,
+ Content as DialogContent,
+ Description as DialogDescription
+};
diff --git a/src/lib/components/ui/drawer/drawer-content.svelte b/src/lib/components/ui/drawer/drawer-content.svelte
new file mode 100644
index 0000000..2f5d543
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-content.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer-description.svelte b/src/lib/components/ui/drawer/drawer-description.svelte
new file mode 100644
index 0000000..87de82c
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-description.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer-footer.svelte b/src/lib/components/ui/drawer/drawer-footer.svelte
new file mode 100644
index 0000000..2e714dd
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer-header.svelte b/src/lib/components/ui/drawer/drawer-header.svelte
new file mode 100644
index 0000000..4bd2f5a
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-header.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer-overlay.svelte b/src/lib/components/ui/drawer/drawer-overlay.svelte
new file mode 100644
index 0000000..5bda25d
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-overlay.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer-title.svelte b/src/lib/components/ui/drawer/drawer-title.svelte
new file mode 100644
index 0000000..1d64d92
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer-title.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/drawer.svelte b/src/lib/components/ui/drawer/drawer.svelte
new file mode 100644
index 0000000..135c007
--- /dev/null
+++ b/src/lib/components/ui/drawer/drawer.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/drawer/index.ts b/src/lib/components/ui/drawer/index.ts
new file mode 100644
index 0000000..162af4f
--- /dev/null
+++ b/src/lib/components/ui/drawer/index.ts
@@ -0,0 +1,37 @@
+import { Drawer as DrawerPrimitive } from "vaul-svelte";
+
+import Root from "./drawer.svelte";
+import Content from "./drawer-content.svelte";
+import Description from "./drawer-description.svelte";
+import Overlay from "./drawer-overlay.svelte";
+import Footer from "./drawer-footer.svelte";
+import Header from "./drawer-header.svelte";
+import Title from "./drawer-title.svelte";
+
+const Trigger = DrawerPrimitive.Trigger;
+const Portal = DrawerPrimitive.Portal;
+const Close = DrawerPrimitive.Close;
+
+export {
+ Root,
+ Content,
+ Description,
+ Overlay,
+ Footer,
+ Header,
+ Title,
+ Trigger,
+ Portal,
+ Close,
+ //
+ Root as Drawer,
+ Content as DrawerContent,
+ Description as DrawerDescription,
+ Overlay as DrawerOverlay,
+ Footer as DrawerFooter,
+ Header as DrawerHeader,
+ Title as DrawerTitle,
+ Trigger as DrawerTrigger,
+ Portal as DrawerPortal,
+ Close as DrawerClose
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..230a1fb
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,62 @@
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+import { cubicOut } from "svelte/easing";
+import type { TransitionConfig } from "svelte/transition";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
+
+type FlyAndScaleParams = {
+ y?: number;
+ x?: number;
+ start?: number;
+ duration?: number;
+};
+
+export const flyAndScale = (
+ node: Element,
+ params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
+): TransitionConfig => {
+ const style = getComputedStyle(node);
+ const transform = style.transform === "none" ? "" : style.transform;
+
+ const scaleConversion = (
+ valueA: number,
+ scaleA: [number, number],
+ scaleB: [number, number]
+ ) => {
+ const [minA, maxA] = scaleA;
+ const [minB, maxB] = scaleB;
+
+ const percentage = (valueA - minA) / (maxA - minA);
+ const valueB = percentage * (maxB - minB) + minB;
+
+ return valueB;
+ };
+
+ const styleToString = (
+ style: Record
+ ): string => {
+ return Object.keys(style).reduce((str, key) => {
+ if (style[key] === undefined) return str;
+ return str + `${key}:${style[key]};`;
+ }, "");
+ };
+
+ return {
+ duration: params.duration ?? 200,
+ delay: 0,
+ css: (t) => {
+ const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
+ const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
+ const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
+
+ return styleToString({
+ transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
+ opacity: t
+ });
+ },
+ easing: cubicOut
+ };
+};
\ No newline at end of file