@turtleclub/ui 0.3.0-beta.21 → 0.3.0-beta.23

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.
@@ -14,10 +14,10 @@ const WidgetItem = React.forwardRef<HTMLDivElement, WidgetItemProps>(
14
14
  <Card
15
15
  ref={ref}
16
16
  variant="border"
17
- gradientBorder="white"
17
+ gradient="white"
18
18
  className={cn(
19
- "cursor-pointer transition-all duration-200 hover:shadow-md w-full p-4 bg-background",
20
- selected && "ring-2 ring-primary ring-offset-2",
19
+ "bg-background w-full cursor-pointer p-4 transition-all duration-200 hover:shadow-md",
20
+ selected && "ring-primary ring-2 ring-offset-2",
21
21
  className
22
22
  )}
23
23
  onClick={onSelect}
@@ -39,11 +39,7 @@ interface WidgetItemTopProps extends React.ComponentProps<"div"> {
39
39
  const WidgetItemTop = React.forwardRef<HTMLDivElement, WidgetItemTopProps>(
40
40
  ({ className, children, ...props }, ref) => {
41
41
  return (
42
- <div
43
- ref={ref}
44
- className={cn("flex items-start justify-between gap-4", className)}
45
- {...props}
46
- >
42
+ <div ref={ref} className={cn("flex items-start justify-between gap-4", className)} {...props}>
47
43
  {children}
48
44
  </div>
49
45
  );
@@ -60,11 +56,7 @@ interface WidgetItemBottomProps extends React.ComponentProps<"div"> {
60
56
  const WidgetItemBottom = React.forwardRef<HTMLDivElement, WidgetItemBottomProps>(
61
57
  ({ className, children, ...props }, ref) => {
62
58
  return (
63
- <div
64
- ref={ref}
65
- className={cn("flex items-end justify-between gap-4", className)}
66
- {...props}
67
- >
59
+ <div ref={ref} className={cn("flex items-end justify-between gap-4", className)} {...props}>
68
60
  {children}
69
61
  </div>
70
62
  );
@@ -83,7 +75,7 @@ const WidgetItemLeft = React.forwardRef<HTMLDivElement, WidgetItemLeftProps>(
83
75
  return (
84
76
  <div
85
77
  ref={ref}
86
- className={cn("flex flex-col min-w-0 flex-1 items-start justify-start gap-1", className)}
78
+ className={cn("flex min-w-0 flex-1 flex-col items-start justify-start gap-1", className)}
87
79
  {...props}
88
80
  >
89
81
  {children}
@@ -104,7 +96,7 @@ const WidgetItemRight = React.forwardRef<HTMLDivElement, WidgetItemRightProps>(
104
96
  return (
105
97
  <div
106
98
  ref={ref}
107
- className={cn("flex flex-col gap-1 justify-between items-end", className)}
99
+ className={cn("flex flex-col items-end justify-between gap-1", className)}
108
100
  {...props}
109
101
  >
110
102
  {children}
@@ -116,4 +108,10 @@ const WidgetItemRight = React.forwardRef<HTMLDivElement, WidgetItemRightProps>(
116
108
  WidgetItemRight.displayName = "WidgetItemRight";
117
109
 
118
110
  export { WidgetItem, WidgetItemTop, WidgetItemBottom, WidgetItemLeft, WidgetItemRight };
119
- export type { WidgetItemProps, WidgetItemTopProps, WidgetItemBottomProps, WidgetItemLeftProps, WidgetItemRightProps };
111
+ export type {
112
+ WidgetItemProps,
113
+ WidgetItemTopProps,
114
+ WidgetItemBottomProps,
115
+ WidgetItemLeftProps,
116
+ WidgetItemRightProps,
117
+ };
@@ -2,12 +2,12 @@ import * as React from "react";
2
2
  import { Slot } from "@radix-ui/react-slot";
3
3
  import { cva, type VariantProps } from "class-variance-authority";
4
4
 
5
- import { cn } from "@/lib/utils";
6
5
  import { DotIcon } from "../icons/dot";
6
+ import { cn } from "@/lib/utils";
7
7
 
8
8
  const buttonVariants = cva(
9
9
  [
10
- "border border-transparent bg-clip-padding",
10
+ "border border-transparent",
11
11
  "inline-flex items-center justify-center gap-2 text-xs font-medium whitespace-nowrap transition-all",
12
12
  "group-disabled:opacity-80 disabled:pointer-events-none",
13
13
  "shrink-0 rounded-full outline-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -15,8 +15,10 @@ const buttonVariants = cva(
15
15
  {
16
16
  variants: {
17
17
  variant: {
18
- default: "bg-secondary hover:bg-background text-foreground disabled:text-muted-foreground",
19
- green: "bg-secondary hover:bg-background text-primary disabled:text-muted-foreground",
18
+ default:
19
+ "bg-secondary hover:bg-background text-foreground disabled:text-muted-foreground active:bg-background/90",
20
+ green:
21
+ "bg-secondary hover:bg-background text-primary disabled:text-muted-foreground active:bg-background/90",
20
22
  menu: "bg-background hover:text-primary justify-between disabled:grayscale-100",
21
23
  },
22
24
  size: {
@@ -25,10 +27,17 @@ const buttonVariants = cva(
25
27
  lg: "h-10 px-6 has-[>svg]:px-4",
26
28
  icon: "size-9",
27
29
  },
30
+ border: {
31
+ none: "",
32
+ interactive: "hover:border-gradient-white active:border-gradient-primary",
33
+ "gradient-white": "border-gradient-white",
34
+ "gradient-primary": "border-gradient-primary",
35
+ },
28
36
  },
29
37
  defaultVariants: {
30
38
  variant: "default",
31
39
  size: "default",
40
+ border: "interactive",
32
41
  },
33
42
  }
34
43
  );
@@ -37,50 +46,35 @@ function Button({
37
46
  className,
38
47
  variant,
39
48
  size,
40
- fullWidth = false,
41
- asChild = false,
49
+ border,
42
50
  isActive = false,
51
+ asChild = false,
43
52
  children,
44
53
  ...props
45
54
  }: React.ComponentProps<"button"> &
46
55
  VariantProps<typeof buttonVariants> & {
47
56
  asChild?: boolean;
48
- fullWidth?: boolean;
49
57
  isActive?: boolean;
50
58
  }) {
51
59
  const Comp = asChild ? Slot : "button";
52
60
 
53
- const noGradient = variant === "menu" || fullWidth;
54
61
  return (
55
62
  <Comp
56
63
  data-slot="button"
57
64
  {...props}
58
- className={cn(
59
- "group active:from-primary active:via-primary/20 active:to-primary/30 relative rounded-full bg-gradient-to-b disabled:pointer-events-none disabled:opacity-50",
60
- noGradient
61
- ? "active:from-primary active:via-primary/20 active:to-primary/30"
62
- : "group from-foreground/40 via-foreground/10 to-foreground/20 active:from-primary active:via-primary/20 active:to-primary/30 relative bg-gradient-to-b",
63
- fullWidth && "w-full",
64
- isActive &&
65
- "shadow-primary from-primary via-primary/20 to-primary/30 bg-gradient-to-b shadow-xs"
66
- )}
65
+ className={buttonVariants({
66
+ variant,
67
+ size,
68
+ border,
69
+ className: cn(isActive ? "border-gradient-primary" : "", className),
70
+ })}
67
71
  >
68
- <div
69
- className={cn(
70
- buttonVariants({
71
- variant,
72
- size,
73
- className: cn("w-full", className),
74
- })
75
- )}
76
- >
77
- {children}
78
- {isActive && (
79
- <div className="relative size-[18px] shrink-0">
80
- <DotIcon className="text-primary absolute top-[-15px] left-[-15px] size-12" />
81
- </div>
82
- )}
83
- </div>
72
+ {children}
73
+ {isActive && (
74
+ <div className="relative size-[18px] shrink-0">
75
+ <DotIcon className="text-primary absolute top-[-15px] left-[-15px] size-12" />
76
+ </div>
77
+ )}
84
78
  </Comp>
85
79
  );
86
80
  }
@@ -8,15 +8,12 @@ const cardVariants = cva("min-h-28 max-w-md p-2.5 transition-all", {
8
8
  variant: {
9
9
  default: "bg-background",
10
10
  border: "bg-background border-border border",
11
- shadow: "bg-muted shadow-[0px_2px_2px_0px] shadow-black",
11
+ shadow: "bg-muted shadow-background shadow-[0px_4px_4px_0px]",
12
12
  },
13
- gradientBorder: {
14
- none: " ",
15
- white:
16
- "group from-foreground/40 via-foreground/10 to-foreground/20 relative bg-gradient-to-b",
17
- green:
18
- "relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:bg-gradient-to-br before:from-[#73F36C]/60 before:via-transparent before:to-[#73F36C]/20 before:[mask-composite:exclude] before:p-px before:content-[''] before:[mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]",
19
- auto: "light:before:from-black/20 light:before:to-black/5 relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:bg-gradient-to-br before:from-white/40 before:via-transparent before:to-white/10 before:[mask-composite:exclude] before:p-px before:content-[''] before:[mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)] dark:before:from-white/40 dark:before:to-white/10",
13
+ gradient: {
14
+ none: "",
15
+ primary: "border-gradient-primary",
16
+ white: "border-gradient-white",
20
17
  },
21
18
  rounded: {
22
19
  default: "rounded-turtle",
@@ -32,7 +29,7 @@ const cardVariants = cva("min-h-28 max-w-md p-2.5 transition-all", {
32
29
  defaultVariants: {
33
30
  variant: "default",
34
31
  rounded: "default",
35
- gradientBorder: "none",
32
+ gradient: "none",
36
33
  },
37
34
  });
38
35
 
@@ -40,35 +37,16 @@ function Card({
40
37
  className,
41
38
  variant,
42
39
  rounded,
43
- gradientBorder = "none",
40
+ gradient,
44
41
  ...props
45
42
  }: React.ComponentProps<"div"> & VariantProps<typeof cardVariants>) {
46
- // Auto-apply gradient border for container variant if not specified
47
-
48
- const hasGradientBorder = gradientBorder !== "none";
49
-
50
- // Get the gradient border classes
51
- const gradientClasses = cardVariants({ gradientBorder });
52
-
53
- // Get the main card classes without gradient
54
- const cardClasses = cn(cardVariants({ variant, rounded, gradientBorder: "none", className }));
55
-
56
- // If we have a gradient border, wrap the card in a gradient container
57
- if (hasGradientBorder) {
58
- // Get the rounded class for the wrapper
59
- const roundedClass = cardVariants({ rounded });
60
- return (
61
- <div className={cn(gradientClasses, roundedClass, "h-full p-0")} data-slot="card-wrapper">
62
- <div
63
- data-slot="card"
64
- className={cn(cardClasses, hasGradientBorder ? "bg-muted" : "", "h-full")}
65
- {...props}
66
- />
67
- </div>
68
- );
69
- }
70
-
71
- return <div data-slot="card" className={cardClasses} {...props} />;
43
+ return (
44
+ <div
45
+ data-slot="card"
46
+ className={cardVariants({ variant, gradient, rounded, className })}
47
+ {...props}
48
+ />
49
+ );
72
50
  }
73
51
 
74
52
  function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -10,15 +10,13 @@ export function Sidebar({
10
10
  className?: string;
11
11
  }) {
12
12
  return (
13
- <div className="from-foreground/40 via-foreground/10 to-foreground/20 rounded-turtle min-h-[calc(100svh-22px)] w-fit bg-gradient-to-br">
14
- <div
15
- className={cn(
16
- "bg-background rounded-turtle flex h-full min-h-[calc(100svh-20px)] w-72 flex-col gap-4 border border-transparent bg-clip-padding px-8 py-10",
17
- className
18
- )}
19
- >
20
- {children}
21
- </div>
13
+ <div
14
+ className={cn(
15
+ "border-gradient-white bg-background rounded-turtle flex h-full min-h-[calc(100svh-20px)] w-72 flex-col gap-4 px-8 py-10",
16
+ className
17
+ )}
18
+ >
19
+ {children}
22
20
  </div>
23
21
  );
24
22
  }
@@ -51,7 +49,7 @@ export function SidebarItem({
51
49
  isActive?: boolean;
52
50
  }) {
53
51
  return (
54
- <Button fullWidth variant="menu" {...props}>
52
+ <Button variant="menu" className="w-full" {...props}>
55
53
  {children}
56
54
  </Button>
57
55
  );
@@ -31,12 +31,12 @@ function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimiti
31
31
  function TooltipContent({
32
32
  className,
33
33
  sideOffset = 2,
34
- withGradient,
34
+ gradient,
35
35
  children,
36
36
  container = document.querySelector(".turtle-widget-root") ?? document.body,
37
37
  ...props
38
38
  }: React.ComponentProps<typeof TooltipPrimitive.Content> & {
39
- withGradient?: boolean;
39
+ gradient?: "primary" | "white";
40
40
  container?: HTMLElement;
41
41
  }) {
42
42
  return (
@@ -45,24 +45,14 @@ function TooltipContent({
45
45
  data-slot="tooltip-content"
46
46
  sideOffset={sideOffset}
47
47
  className={cn(
48
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-tooltip-content-transform-origin) transition-all",
49
- withGradient
50
- ? "group from-foreground/40 via-foreground/10 to-foreground/20 relative rounded-lg bg-gradient-to-br"
51
- : ""
48
+ "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-neutral-alpha-10 border-border origin-(--radix-tooltip-content-transform-origin) rounded-xl border p-2 transition-all",
49
+ gradient === "primary" && "border-gradient-primary",
50
+ gradient === "white" && "border-gradient-white",
51
+ className
52
52
  )}
53
53
  {...props}
54
54
  >
55
- <div
56
- className={cn(
57
- "text-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-lg px-5 py-2.5 text-xs text-balance",
58
- withGradient
59
- ? "bg-secondary border border-transparent bg-clip-padding"
60
- : "bg-neutral-alpha-2 border-border border backdrop-blur-lg",
61
- className
62
- )}
63
- >
64
- {children}
65
- </div>
55
+ {children}
66
56
  </TooltipPrimitive.Content>
67
57
  </TooltipPrimitive.Portal>
68
58
  );
@@ -44,34 +44,67 @@
44
44
  --color-secondary-foreground: var(--secondary-foreground);
45
45
  --color-muted: var(--muted);
46
46
  --color-muted-foreground: var(--muted-foreground);
47
- --color-neutral-alpha-2: var(--color-neutral-alpha-2);
48
- --color-neutral-alpha-5: var(--color-neutral-alpha-5);
49
- --color-neutral-alpha-10: var(--color-neutral-alpha-10);
50
47
  --color-accent: var(--accent);
51
48
  --color-accent-foreground: var(--accent-foreground);
52
49
  --color-destructive: var(--destructive);
53
50
  --color-destructive-foreground: var(--destructive-foreground);
51
+
54
52
  --color-border: var(--border);
55
53
  --color-input: var(--input);
56
54
  --color-ring: var(--ring);
55
+
56
+ --color-neutral-alpha-2: var(--color-neutral-alpha-2);
57
+ --color-neutral-alpha-5: var(--color-neutral-alpha-5);
58
+ --color-neutral-alpha-10: var(--color-neutral-alpha-10);
59
+ --color-neutral-alpha-20: var(--color-neutral-alpha-20);
60
+ --color-neutral-alpha-30: var(--color-neutral-alpha-30);
61
+ --color-neutral-alpha-50: var(--color-neutral-alpha-50);
62
+
63
+ --color-primary-alpha-10: var(--color-brand-green-alpha-10);
64
+ --color-primary-alpha-20: var(--color-brand-green-alpha-20);
65
+ --color-primary-alpha-30: var(--color-brand-green-alpha-30);
66
+ --color-primary-alpha-50: var(--color-brand-green-alpha-50);
67
+
57
68
  --color-chart-1: var(--chart-1);
58
69
  --color-chart-2: var(--chart-2);
59
70
  --color-chart-3: var(--chart-3);
60
71
  --color-chart-4: var(--chart-4);
61
72
  --color-chart-5: var(--chart-5);
73
+
74
+ --border-gradient-white: linear-gradient(
75
+ 165deg,
76
+ var(--color-neutral-alpha-50) 0%,
77
+ var(--color-neutral-alpha-5) 20%,
78
+ var(--color-neutral-alpha-2) 75%,
79
+ var(--color-neutral-alpha-20) 100%
80
+ )
81
+ border-box;
82
+ --border-gradient-primary: linear-gradient(
83
+ 165deg,
84
+ var(--color-primary-alpha-50) 0%,
85
+ var(--color-primary-alpha-20) 20%,
86
+ var(--color-primary-alpha-10) 75%,
87
+ var(--color-primary-alpha-30) 100%
88
+ )
89
+ border-box;
62
90
  }
63
91
 
64
- /* ===== BASE STYLES ===== */
65
- @layer base {
66
- /* HTML and body setup */
67
- html,
68
- body {
69
- @apply size-full font-sans antialiased;
70
- }
92
+ @utility border-gradient-* {
93
+ @apply relative border border-transparent;
71
94
 
72
- /* Body styling with semantic variables */
73
- body {
74
- @apply bg-background text-foreground;
95
+ &::before {
96
+ content: "";
97
+ @apply pointer-events-none absolute -inset-px border border-transparent;
98
+ border-radius: inherit; /* match rounded-* from parent */
99
+ background: --value(--border-gradient-*); /* gradient from utility */
100
+ -webkit-mask:
101
+ linear-gradient(#fff 0 0) padding-box,
102
+ linear-gradient(#fff 0 0) border-box;
103
+ -webkit-mask-composite: xor;
104
+ mask:
105
+ linear-gradient(#fff 0 0) padding-box,
106
+ linear-gradient(#fff 0 0) border-box;
107
+ mask-composite: exclude;
75
108
  }
76
109
  }
77
110
 
@@ -35,6 +35,16 @@
35
35
  --color-neutral-base-900: #141514; /* Darkest - same as brand black */
36
36
 
37
37
  /* ===== TRANSPARENT VARIANTS ===== */
38
+ --color-brand-green-alpha-10: #73f36c1a; /* 10% */
39
+ --color-brand-green-alpha-20: #73f36c33; /* 20% */
40
+ --color-brand-green-alpha-30: #73f36c4d; /* 30% */
41
+ --color-brand-green-alpha-40: #73f36c66; /* 40% */
42
+ --color-brand-green-alpha-50: #73f36c80; /* 50% */
43
+ --color-brand-green-alpha-60: #73f36c99; /* 60% */
44
+ --color-brand-green-alpha-70: #73f36cb3; /* 70% */
45
+ --color-brand-green-alpha-80: #73f36ccc; /* 80% */
46
+ --color-brand-green-alpha-90: #73f36ce6; /* 90% */
47
+
38
48
  /* Alpha variants for overlays and subtle backgrounds */
39
49
  --color-neutral-alpha-2: #f9f9f905; /* 2% opacity white */
40
50
  --color-neutral-alpha-5: #f9f9f90d; /* 5% opacity white */