@alpic-ai/ui 0.0.0-staging.dcd2ccb → 0.0.0-staging.ddb67e5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/alert.d.mts +1 -1
- package/dist/components/badge.d.mts +1 -1
- package/dist/components/button.d.mts +4 -2
- package/dist/components/button.mjs +20 -6
- package/dist/components/github-button.d.mts +13 -0
- package/dist/components/github-button.mjs +24 -0
- package/dist/components/sidebar.d.mts +1 -1
- package/dist/components/sidebar.mjs +65 -21
- package/dist/components/skeleton.d.mts +1 -1
- package/dist/components/spinner.d.mts +1 -1
- package/dist/components/status-dot.d.mts +1 -1
- package/dist/components/tabs.d.mts +1 -1
- package/dist/components/toggle-group.d.mts +1 -1
- package/dist/components/typography.d.mts +34 -0
- package/dist/components/typography.mjs +29 -0
- package/package.json +14 -14
- package/src/components/button.tsx +13 -9
- package/src/components/combobox.tsx +18 -6
- package/src/components/github-button.tsx +34 -0
- package/src/components/sidebar.tsx +64 -27
- package/src/components/typography.tsx +19 -0
- package/src/hooks/use-copy-to-clipboard.ts +6 -2
- package/src/stories/button.stories.tsx +23 -1
- package/src/stories/sidebar.stories.tsx +11 -8
- package/src/styles/tokens.css +201 -0
|
@@ -5,7 +5,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
5
5
|
|
|
6
6
|
//#region src/components/alert.d.ts
|
|
7
7
|
declare const alertVariants: (props?: ({
|
|
8
|
-
variant?: "default" | "
|
|
8
|
+
variant?: "default" | "success" | "destructive" | "warning" | null | undefined;
|
|
9
9
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
10
10
|
interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> {}
|
|
11
11
|
declare function Alert({
|
|
@@ -4,7 +4,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
4
4
|
|
|
5
5
|
//#region src/components/badge.d.ts
|
|
6
6
|
declare const badgeVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "success" | "warning" | "primary" | "secondary" | "error" | null | undefined;
|
|
8
8
|
size?: "sm" | "md" | null | undefined;
|
|
9
9
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
10
10
|
interface BadgeProps extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {}
|
|
@@ -5,13 +5,14 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
5
5
|
|
|
6
6
|
//#region src/components/button.d.ts
|
|
7
7
|
declare const buttonVariants: (props?: ({
|
|
8
|
-
variant?: "destructive" | "
|
|
9
|
-
size?: "default" | "
|
|
8
|
+
variant?: "destructive" | "primary" | "secondary" | "tertiary" | "link" | "link-muted" | "cta" | null | undefined;
|
|
9
|
+
size?: "default" | "pill" | "icon" | "icon-rounded" | null | undefined;
|
|
10
10
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
11
11
|
interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
|
|
12
12
|
asChild?: boolean;
|
|
13
13
|
loading?: boolean;
|
|
14
14
|
icon?: React.ReactNode;
|
|
15
|
+
iconTrailing?: React.ReactNode;
|
|
15
16
|
}
|
|
16
17
|
declare function Button({
|
|
17
18
|
className,
|
|
@@ -21,6 +22,7 @@ declare function Button({
|
|
|
21
22
|
asChild,
|
|
22
23
|
loading,
|
|
23
24
|
icon,
|
|
25
|
+
iconTrailing,
|
|
24
26
|
disabled,
|
|
25
27
|
children,
|
|
26
28
|
...props
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.mjs";
|
|
3
3
|
import { Loader2 } from "lucide-react";
|
|
4
|
-
import {
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
6
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
7
7
|
//#region src/components/button.tsx
|
|
8
8
|
const buttonVariants = cva([
|
|
9
9
|
"inline-flex items-center justify-center gap-1 whitespace-nowrap",
|
|
@@ -35,7 +35,14 @@ const buttonVariants = cva([
|
|
|
35
35
|
"[@media(hover:hover)]:hover:underline",
|
|
36
36
|
"focus-visible:bg-background"
|
|
37
37
|
].join(" "),
|
|
38
|
-
destructive: ["bg-destructive text-destructive-foreground", "[@media(hover:hover)]:hover:bg-destructive-hover"].join(" ")
|
|
38
|
+
destructive: ["bg-destructive text-destructive-foreground", "[@media(hover:hover)]:hover:bg-destructive-hover"].join(" "),
|
|
39
|
+
cta: [
|
|
40
|
+
"button-cta",
|
|
41
|
+
"h-9 px-4 gap-2 rounded-md",
|
|
42
|
+
"dark:bg-inverted text-foreground",
|
|
43
|
+
"transition-[transform,filter] duration-300 ease-out",
|
|
44
|
+
"active:scale-[0.99]"
|
|
45
|
+
].join(" ")
|
|
39
46
|
},
|
|
40
47
|
size: {
|
|
41
48
|
default: "type-text-sm",
|
|
@@ -49,8 +56,8 @@ const buttonVariants = cva([
|
|
|
49
56
|
size: "default"
|
|
50
57
|
}
|
|
51
58
|
});
|
|
52
|
-
function Button({ className, variant, size, type = "button", asChild = false, loading = false, icon, disabled, children, ...props }) {
|
|
53
|
-
return /* @__PURE__ */
|
|
59
|
+
function Button({ className, variant, size, type = "button", asChild = false, loading = false, icon, iconTrailing, disabled, children, ...props }) {
|
|
60
|
+
return /* @__PURE__ */ jsxs(asChild ? Slot : "button", {
|
|
54
61
|
"data-slot": "button",
|
|
55
62
|
className: cn(buttonVariants({
|
|
56
63
|
variant,
|
|
@@ -60,7 +67,14 @@ function Button({ className, variant, size, type = "button", asChild = false, lo
|
|
|
60
67
|
disabled: disabled || loading,
|
|
61
68
|
"aria-busy": loading || void 0,
|
|
62
69
|
...props,
|
|
63
|
-
children:
|
|
70
|
+
children: [
|
|
71
|
+
loading ? /* @__PURE__ */ jsx(Loader2, { className: "motion-safe:animate-spin" }) : icon,
|
|
72
|
+
asChild ? /* @__PURE__ */ jsx(Slottable, { children }) : children,
|
|
73
|
+
!loading && iconTrailing ? /* @__PURE__ */ jsx("span", {
|
|
74
|
+
"data-cta-icon-trailing": true,
|
|
75
|
+
children: iconTrailing
|
|
76
|
+
}) : null
|
|
77
|
+
]
|
|
64
78
|
});
|
|
65
79
|
}
|
|
66
80
|
//#endregion
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Button } from "./button.mjs";
|
|
2
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
import { ComponentProps } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/components/github-button.d.ts
|
|
6
|
+
type GitHubButtonProps = Omit<ComponentProps<typeof Button>, "variant" | "icon">;
|
|
7
|
+
declare function GitHubButton({
|
|
8
|
+
className,
|
|
9
|
+
children,
|
|
10
|
+
...props
|
|
11
|
+
}: GitHubButtonProps): _$react_jsx_runtime0.JSX.Element;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { GitHubButton, type GitHubButtonProps };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.mjs";
|
|
3
|
+
import { Button } from "./button.mjs";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/github-button.tsx
|
|
6
|
+
const GITHUB_ICON_PATH = "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12";
|
|
7
|
+
function GitHubIcon() {
|
|
8
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
9
|
+
fill: "currentColor",
|
|
10
|
+
viewBox: "0 0 24 24",
|
|
11
|
+
"aria-hidden": "true",
|
|
12
|
+
children: /* @__PURE__ */ jsx("path", { d: GITHUB_ICON_PATH })
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function GitHubButton({ className, children, ...props }) {
|
|
16
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
17
|
+
...props,
|
|
18
|
+
icon: /* @__PURE__ */ jsx(GitHubIcon, {}),
|
|
19
|
+
className: cn("bg-foreground text-background [@media(hover:hover)]:hover:bg-foreground/90", className),
|
|
20
|
+
children
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { GitHubButton };
|
|
@@ -108,7 +108,7 @@ declare function SidebarMenuItem({
|
|
|
108
108
|
}: React.ComponentProps<"li">): _$react_jsx_runtime0.JSX.Element;
|
|
109
109
|
declare const sidebarMenuButtonVariants: (props?: ({
|
|
110
110
|
variant?: "default" | "outline" | null | undefined;
|
|
111
|
-
size?: "
|
|
111
|
+
size?: "sm" | "lg" | "default" | null | undefined;
|
|
112
112
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
113
113
|
declare function SidebarMenuButton({
|
|
114
114
|
asChild,
|
|
@@ -6,7 +6,6 @@ import { Separator } from "./separator.mjs";
|
|
|
6
6
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./sheet.mjs";
|
|
7
7
|
import { useIsMobile } from "../hooks/use-mobile.mjs";
|
|
8
8
|
import { Skeleton } from "./skeleton.mjs";
|
|
9
|
-
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
10
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
10
|
import { cva } from "class-variance-authority";
|
|
12
11
|
import * as React from "react";
|
|
@@ -18,6 +17,18 @@ const SIDEBAR_WIDTH = "15rem";
|
|
|
18
17
|
const SIDEBAR_WIDTH_MOBILE = "16rem";
|
|
19
18
|
const SIDEBAR_WIDTH_ICON = "3.5rem";
|
|
20
19
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
20
|
+
const INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR = [
|
|
21
|
+
"a",
|
|
22
|
+
"button",
|
|
23
|
+
"input",
|
|
24
|
+
"select",
|
|
25
|
+
"textarea",
|
|
26
|
+
"[role='button']",
|
|
27
|
+
"[role='link']",
|
|
28
|
+
"[role='menuitem']",
|
|
29
|
+
"[contenteditable='true']",
|
|
30
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
31
|
+
].join(", ");
|
|
21
32
|
const SidebarContext = React.createContext(null);
|
|
22
33
|
function useSidebar() {
|
|
23
34
|
const context = React.useContext(SidebarContext);
|
|
@@ -81,10 +92,20 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
81
92
|
});
|
|
82
93
|
}
|
|
83
94
|
function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, ...props }) {
|
|
84
|
-
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
95
|
+
const { isMobile, state, openMobile, setOpenMobile, open, setOpen } = useSidebar();
|
|
96
|
+
function handleSurfaceClickCapture(event) {
|
|
97
|
+
if (event.target instanceof Element && event.target.closest(INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR)) return;
|
|
98
|
+
if (!open) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
setOpen(true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
setOpen(false);
|
|
105
|
+
}
|
|
85
106
|
if (collapsible === "none") return /* @__PURE__ */ jsx("div", {
|
|
86
107
|
"data-slot": "sidebar",
|
|
87
|
-
className: cn("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className),
|
|
108
|
+
className: cn("bg-sidebar-surface text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className),
|
|
88
109
|
...props,
|
|
89
110
|
children
|
|
90
111
|
});
|
|
@@ -96,7 +117,7 @@ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas"
|
|
|
96
117
|
"data-sidebar": "sidebar",
|
|
97
118
|
"data-slot": "sidebar",
|
|
98
119
|
"data-mobile": "true",
|
|
99
|
-
className: "bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",
|
|
120
|
+
className: "bg-sidebar-surface text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",
|
|
100
121
|
style: { "--sidebar-width": SIDEBAR_WIDTH_MOBILE },
|
|
101
122
|
side,
|
|
102
123
|
children: [/* @__PURE__ */ jsxs(SheetHeader, {
|
|
@@ -120,20 +141,20 @@ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas"
|
|
|
120
141
|
className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-out", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)")
|
|
121
142
|
}), /* @__PURE__ */ jsx("div", {
|
|
122
143
|
"data-slot": "sidebar-container",
|
|
123
|
-
className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-out md:flex", side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className),
|
|
144
|
+
className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-out md:flex", side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className),
|
|
124
145
|
...props,
|
|
125
146
|
children: /* @__PURE__ */ jsx("div", {
|
|
126
147
|
"data-sidebar": "sidebar",
|
|
127
148
|
"data-slot": "sidebar-inner",
|
|
128
|
-
className: "bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm",
|
|
149
|
+
className: "bg-sidebar-surface group-data-[variant=floating]:border-sidebar-border flex h-full w-full cursor-pointer flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm",
|
|
150
|
+
onClickCapture: handleSurfaceClickCapture,
|
|
129
151
|
children
|
|
130
152
|
})
|
|
131
153
|
})]
|
|
132
154
|
});
|
|
133
155
|
}
|
|
134
156
|
function SidebarTrigger({ className, onClick, ...props }) {
|
|
135
|
-
const {
|
|
136
|
-
const isOpen = isMobile ? openMobile : state === "expanded";
|
|
157
|
+
const { toggleSidebar } = useSidebar();
|
|
137
158
|
return /* @__PURE__ */ jsxs(Button, {
|
|
138
159
|
"data-sidebar": "trigger",
|
|
139
160
|
"data-slot": "sidebar-trigger",
|
|
@@ -145,12 +166,35 @@ function SidebarTrigger({ className, onClick, ...props }) {
|
|
|
145
166
|
toggleSidebar();
|
|
146
167
|
},
|
|
147
168
|
...props,
|
|
148
|
-
children: [
|
|
169
|
+
children: [/* @__PURE__ */ jsx(SidebarToggleIcon, { className: "size-4.5" }), /* @__PURE__ */ jsx("span", {
|
|
149
170
|
className: "sr-only",
|
|
150
171
|
children: "Toggle Sidebar"
|
|
151
172
|
})]
|
|
152
173
|
});
|
|
153
174
|
}
|
|
175
|
+
function SidebarToggleIcon({ className, ...props }) {
|
|
176
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
177
|
+
viewBox: "0 0 20 20",
|
|
178
|
+
fill: "none",
|
|
179
|
+
"aria-hidden": "true",
|
|
180
|
+
className,
|
|
181
|
+
...props,
|
|
182
|
+
children: [/* @__PURE__ */ jsx("rect", {
|
|
183
|
+
x: "2.75",
|
|
184
|
+
y: "2.75",
|
|
185
|
+
width: "14.5",
|
|
186
|
+
height: "14.5",
|
|
187
|
+
rx: "4",
|
|
188
|
+
stroke: "currentColor",
|
|
189
|
+
strokeWidth: "1.5"
|
|
190
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
191
|
+
d: "M10 4.75V15.25",
|
|
192
|
+
stroke: "currentColor",
|
|
193
|
+
strokeWidth: "1.5",
|
|
194
|
+
strokeLinecap: "round"
|
|
195
|
+
})]
|
|
196
|
+
});
|
|
197
|
+
}
|
|
154
198
|
function SidebarRail({ className, ...props }) {
|
|
155
199
|
const { toggleSidebar } = useSidebar();
|
|
156
200
|
return /* @__PURE__ */ jsx("button", {
|
|
@@ -160,7 +204,7 @@ function SidebarRail({ className, ...props }) {
|
|
|
160
204
|
tabIndex: -1,
|
|
161
205
|
onClick: toggleSidebar,
|
|
162
206
|
title: "Toggle Sidebar",
|
|
163
|
-
className: cn("
|
|
207
|
+
className: cn("absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex", "[@media(hover:hover)]:hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full", "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", className),
|
|
164
208
|
...props
|
|
165
209
|
});
|
|
166
210
|
}
|
|
@@ -182,22 +226,22 @@ function SidebarHeader({ className, icon, title, children, ...props }) {
|
|
|
182
226
|
return /* @__PURE__ */ jsxs("div", {
|
|
183
227
|
"data-slot": "sidebar-header",
|
|
184
228
|
"data-sidebar": "header",
|
|
185
|
-
className: cn("flex flex-col gap-2
|
|
229
|
+
className: cn("flex flex-col gap-2 py-2", className),
|
|
186
230
|
...props,
|
|
187
231
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
188
|
-
className: "flex h-8 items-center gap-2 px-
|
|
232
|
+
className: "flex h-8 items-center gap-2 px-3",
|
|
189
233
|
children: [
|
|
190
|
-
/* @__PURE__ */ jsxs("
|
|
191
|
-
className: "relative shrink-0",
|
|
234
|
+
/* @__PURE__ */ jsxs("span", {
|
|
235
|
+
className: "relative flex size-8 shrink-0 items-center justify-center",
|
|
192
236
|
children: [/* @__PURE__ */ jsx("span", {
|
|
193
|
-
className: "transition-opacity group-data-[collapsible=icon]:group-hover:opacity-0",
|
|
237
|
+
className: "transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-0",
|
|
194
238
|
children: icon
|
|
195
|
-
}), /* @__PURE__ */ jsx(
|
|
196
|
-
|
|
197
|
-
|
|
239
|
+
}), /* @__PURE__ */ jsx(SidebarTrigger, {
|
|
240
|
+
tabIndex: -1,
|
|
241
|
+
className: "absolute inset-0 opacity-0 transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-100"
|
|
198
242
|
})]
|
|
199
243
|
}),
|
|
200
|
-
|
|
244
|
+
/* @__PURE__ */ jsx("span", {
|
|
201
245
|
className: "text-foreground text-md min-w-0 truncate font-medium group-data-[collapsible=icon]:hidden",
|
|
202
246
|
children: title
|
|
203
247
|
}),
|
|
@@ -232,7 +276,7 @@ function SidebarGroup({ className, ...props }) {
|
|
|
232
276
|
return /* @__PURE__ */ jsx("div", {
|
|
233
277
|
"data-slot": "sidebar-group",
|
|
234
278
|
"data-sidebar": "group",
|
|
235
|
-
className: cn("relative flex w-full min-w-0 flex-col
|
|
279
|
+
className: cn("relative flex w-full min-w-0 flex-col", className),
|
|
236
280
|
...props
|
|
237
281
|
});
|
|
238
282
|
}
|
|
@@ -272,7 +316,7 @@ function SidebarMenuItem({ className, ...props }) {
|
|
|
272
316
|
return /* @__PURE__ */ jsx("li", {
|
|
273
317
|
"data-slot": "sidebar-menu-item",
|
|
274
318
|
"data-sidebar": "menu-item",
|
|
275
|
-
className: cn("group/menu-item relative", className),
|
|
319
|
+
className: cn("group/menu-item relative px-3", className),
|
|
276
320
|
...props
|
|
277
321
|
});
|
|
278
322
|
}
|
|
@@ -4,7 +4,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
4
4
|
|
|
5
5
|
//#region src/components/skeleton.d.ts
|
|
6
6
|
declare const skeletonVariants: (props?: ({
|
|
7
|
-
shape?: "
|
|
7
|
+
shape?: "circle" | "rectangle" | null | undefined;
|
|
8
8
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
9
9
|
interface SkeletonProps extends React.ComponentProps<"div">, VariantProps<typeof skeletonVariants> {}
|
|
10
10
|
declare function Skeleton({
|
|
@@ -4,7 +4,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
4
4
|
|
|
5
5
|
//#region src/components/spinner.d.ts
|
|
6
6
|
declare const spinnerVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "primary" | "secondary" | null | undefined;
|
|
8
8
|
size?: "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
9
9
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
10
10
|
interface SpinnerProps extends Omit<React.ComponentProps<"svg">, "children">, VariantProps<typeof spinnerVariants> {}
|
|
@@ -4,7 +4,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
4
4
|
|
|
5
5
|
//#region src/components/status-dot.d.ts
|
|
6
6
|
declare const statusDotVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "success" | "destructive" | "warning" | "muted" | null | undefined;
|
|
8
8
|
pulse?: boolean | null | undefined;
|
|
9
9
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
10
10
|
interface StatusDotProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof statusDotVariants> {}
|
|
@@ -6,7 +6,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
6
6
|
|
|
7
7
|
//#region src/components/tabs.d.ts
|
|
8
8
|
declare const tabsTriggerVariants: (props?: ({
|
|
9
|
-
variant?: "default" | "
|
|
9
|
+
variant?: "default" | "line" | "pill" | null | undefined;
|
|
10
10
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
11
11
|
declare function Tabs({
|
|
12
12
|
className,
|
|
@@ -7,7 +7,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
|
|
|
7
7
|
//#region src/components/toggle-group.d.ts
|
|
8
8
|
declare const toggleGroupItemVariants: (props?: ({
|
|
9
9
|
variant?: "default" | "outline" | null | undefined;
|
|
10
|
-
size?: "
|
|
10
|
+
size?: "sm" | "default" | null | undefined;
|
|
11
11
|
} & _$class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
12
12
|
type ToggleGroupContextValue = VariantProps<typeof toggleGroupItemVariants>;
|
|
13
13
|
declare function ToggleGroup({
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components/typography.d.ts
|
|
5
|
+
declare function H1({
|
|
6
|
+
children,
|
|
7
|
+
className
|
|
8
|
+
}: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
12
|
+
declare function H2({
|
|
13
|
+
children,
|
|
14
|
+
className
|
|
15
|
+
}: {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
19
|
+
declare function H3({
|
|
20
|
+
children,
|
|
21
|
+
className
|
|
22
|
+
}: {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
26
|
+
declare function H4({
|
|
27
|
+
children,
|
|
28
|
+
className
|
|
29
|
+
}: {
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { H1, H2, H3, H4 };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.mjs";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/typography.tsx
|
|
4
|
+
function H1({ children, className }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("h1", {
|
|
6
|
+
className: cn("scroll-m-20 type-display-sm font-bold", className),
|
|
7
|
+
children
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function H2({ children, className }) {
|
|
11
|
+
return /* @__PURE__ */ jsx("h2", {
|
|
12
|
+
className: cn("scroll-m-20 type-display-xs font-semibold", className),
|
|
13
|
+
children
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function H3({ children, className }) {
|
|
17
|
+
return /* @__PURE__ */ jsx("h3", {
|
|
18
|
+
className: cn("scroll-m-20 type-text-lg font-semibold", className),
|
|
19
|
+
children
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function H4({ children, className }) {
|
|
23
|
+
return /* @__PURE__ */ jsx("h4", {
|
|
24
|
+
className: cn("scroll-m-20 type-text-md font-semibold", className),
|
|
25
|
+
children
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { H1, H2, H3, H4 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpic-ai/ui",
|
|
3
|
-
"version": "0.0.0-staging.
|
|
3
|
+
"version": "0.0.0-staging.ddb67e5",
|
|
4
4
|
"description": "Alpic design system — shared UI components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"lucide-react": "^1.
|
|
27
|
-
"react": "^19.
|
|
28
|
-
"react-dom": "^19.
|
|
29
|
-
"react-hook-form": "^7.
|
|
30
|
-
"sonner": "^2.0.
|
|
31
|
-
"tailwindcss": "^4.
|
|
32
|
-
"tw-animate-css": "^1.
|
|
26
|
+
"lucide-react": "^1.8.0",
|
|
27
|
+
"react": "^19.2.5",
|
|
28
|
+
"react-dom": "^19.2.5",
|
|
29
|
+
"react-hook-form": "^7.73.1",
|
|
30
|
+
"sonner": "^2.0.7",
|
|
31
|
+
"tailwindcss": "^4.2.3",
|
|
32
|
+
"tw-animate-css": "^1.4.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -56,17 +56,17 @@
|
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@ladle/react": "^5.1.1",
|
|
59
|
-
"@tailwindcss/postcss": "^4.2.
|
|
59
|
+
"@tailwindcss/postcss": "^4.2.3",
|
|
60
60
|
"@types/react": "19.2.14",
|
|
61
61
|
"@types/react-dom": "19.2.3",
|
|
62
|
-
"lucide-react": "^1.
|
|
63
|
-
"react-hook-form": "^7.
|
|
62
|
+
"lucide-react": "^1.8.0",
|
|
63
|
+
"react-hook-form": "^7.73.1",
|
|
64
64
|
"shx": "^0.4.0",
|
|
65
65
|
"sonner": "^2.0.7",
|
|
66
|
-
"tailwindcss": "^4.2.
|
|
67
|
-
"tsdown": "^0.21.
|
|
66
|
+
"tailwindcss": "^4.2.3",
|
|
67
|
+
"tsdown": "^0.21.9",
|
|
68
68
|
"tw-animate-css": "^1.4.0",
|
|
69
|
-
"typescript": "^6.0.
|
|
69
|
+
"typescript": "^6.0.3"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "shx rm -rf dist && tsdown",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
import { Loader2 } from "lucide-react";
|
|
6
6
|
import type * as React from "react";
|
|
@@ -46,6 +46,13 @@ const buttonVariants = cva(
|
|
|
46
46
|
"bg-destructive text-destructive-foreground",
|
|
47
47
|
"[@media(hover:hover)]:hover:bg-destructive-hover",
|
|
48
48
|
].join(" "),
|
|
49
|
+
cta: [
|
|
50
|
+
"button-cta",
|
|
51
|
+
"h-9 px-4 gap-2 rounded-md",
|
|
52
|
+
"dark:bg-inverted text-foreground",
|
|
53
|
+
"transition-[transform,filter] duration-300 ease-out",
|
|
54
|
+
"active:scale-[0.99]",
|
|
55
|
+
].join(" "),
|
|
49
56
|
},
|
|
50
57
|
size: {
|
|
51
58
|
default: "type-text-sm",
|
|
@@ -65,6 +72,7 @@ interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeo
|
|
|
65
72
|
asChild?: boolean;
|
|
66
73
|
loading?: boolean;
|
|
67
74
|
icon?: React.ReactNode;
|
|
75
|
+
iconTrailing?: React.ReactNode;
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
function Button({
|
|
@@ -75,6 +83,7 @@ function Button({
|
|
|
75
83
|
asChild = false,
|
|
76
84
|
loading = false,
|
|
77
85
|
icon,
|
|
86
|
+
iconTrailing,
|
|
78
87
|
disabled,
|
|
79
88
|
children,
|
|
80
89
|
...props
|
|
@@ -90,14 +99,9 @@ function Button({
|
|
|
90
99
|
aria-busy={loading || undefined}
|
|
91
100
|
{...props}
|
|
92
101
|
>
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<>
|
|
97
|
-
{loading ? <Loader2 className="motion-safe:animate-spin" /> : icon}
|
|
98
|
-
{children}
|
|
99
|
-
</>
|
|
100
|
-
)}
|
|
102
|
+
{loading ? <Loader2 className="motion-safe:animate-spin" /> : icon}
|
|
103
|
+
{asChild ? <Slottable>{children}</Slottable> : children}
|
|
104
|
+
{!loading && iconTrailing ? <span data-cta-icon-trailing>{iconTrailing}</span> : null}
|
|
101
105
|
</Comp>
|
|
102
106
|
);
|
|
103
107
|
}
|
|
@@ -84,7 +84,9 @@ function Combobox(props: ComboboxProps) {
|
|
|
84
84
|
|
|
85
85
|
const onOpenChange = useCallback(
|
|
86
86
|
(newOpen: boolean) => {
|
|
87
|
-
if (!isOpenControlled)
|
|
87
|
+
if (!isOpenControlled) {
|
|
88
|
+
setUncontrolledOpen(newOpen);
|
|
89
|
+
}
|
|
88
90
|
controlledOnOpenChange?.(newOpen);
|
|
89
91
|
},
|
|
90
92
|
[isOpenControlled, controlledOnOpenChange],
|
|
@@ -105,7 +107,9 @@ function Combobox(props: ComboboxProps) {
|
|
|
105
107
|
|
|
106
108
|
const isSelected = useCallback(
|
|
107
109
|
(itemValue: string) => {
|
|
108
|
-
if (multiple)
|
|
110
|
+
if (multiple) {
|
|
111
|
+
return multiValues.includes(itemValue);
|
|
112
|
+
}
|
|
109
113
|
return singleValue === itemValue;
|
|
110
114
|
},
|
|
111
115
|
[multiple, singleValue, multiValues],
|
|
@@ -123,11 +127,15 @@ function Combobox(props: ComboboxProps) {
|
|
|
123
127
|
const next = multiValues.includes(itemValue)
|
|
124
128
|
? multiValues.filter((val) => val !== itemValue)
|
|
125
129
|
: [...multiValues, itemValue];
|
|
126
|
-
if (!isControlledMulti)
|
|
130
|
+
if (!isControlledMulti) {
|
|
131
|
+
setUncontrolledMultiValue(next);
|
|
132
|
+
}
|
|
127
133
|
onValueChangeMulti?.(next);
|
|
128
134
|
// Stay open in multi mode
|
|
129
135
|
} else {
|
|
130
|
-
if (!isControlledSingle)
|
|
136
|
+
if (!isControlledSingle) {
|
|
137
|
+
setUncontrolledSingleValue(itemValue);
|
|
138
|
+
}
|
|
131
139
|
onValueChangeSingle?.(itemValue);
|
|
132
140
|
onOpenChange(false);
|
|
133
141
|
}
|
|
@@ -145,9 +153,13 @@ function Combobox(props: ComboboxProps) {
|
|
|
145
153
|
|
|
146
154
|
const onDeselect = useCallback(
|
|
147
155
|
(itemValue: string) => {
|
|
148
|
-
if (!multiple)
|
|
156
|
+
if (!multiple) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
149
159
|
const next = multiValues.filter((val) => val !== itemValue);
|
|
150
|
-
if (!isControlledMulti)
|
|
160
|
+
if (!isControlledMulti) {
|
|
161
|
+
setUncontrolledMultiValue(next);
|
|
162
|
+
}
|
|
151
163
|
onValueChangeMulti?.(next);
|
|
152
164
|
},
|
|
153
165
|
[multiple, isControlledMulti, onValueChangeMulti, multiValues],
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
|
|
8
|
+
const GITHUB_ICON_PATH =
|
|
9
|
+
"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12";
|
|
10
|
+
|
|
11
|
+
function GitHubIcon() {
|
|
12
|
+
return (
|
|
13
|
+
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
14
|
+
<path d={GITHUB_ICON_PATH} />
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type GitHubButtonProps = Omit<ComponentProps<typeof Button>, "variant" | "icon">;
|
|
20
|
+
|
|
21
|
+
function GitHubButton({ className, children, ...props }: GitHubButtonProps) {
|
|
22
|
+
return (
|
|
23
|
+
<Button
|
|
24
|
+
{...props}
|
|
25
|
+
icon={<GitHubIcon />}
|
|
26
|
+
className={cn("bg-foreground text-background [@media(hover:hover)]:hover:bg-foreground/90", className)}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</Button>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type { GitHubButtonProps };
|
|
34
|
+
export { GitHubButton };
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { Slot } from "@radix-ui/react-slot";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
-
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
6
5
|
import * as React from "react";
|
|
7
6
|
|
|
8
7
|
import { useIsMobile } from "../hooks/use-mobile";
|
|
@@ -20,6 +19,19 @@ const SIDEBAR_WIDTH_MOBILE = "16rem";
|
|
|
20
19
|
const SIDEBAR_WIDTH_ICON = "3.5rem";
|
|
21
20
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
22
21
|
|
|
22
|
+
const INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR = [
|
|
23
|
+
"a",
|
|
24
|
+
"button",
|
|
25
|
+
"input",
|
|
26
|
+
"select",
|
|
27
|
+
"textarea",
|
|
28
|
+
"[role='button']",
|
|
29
|
+
"[role='link']",
|
|
30
|
+
"[role='menuitem']",
|
|
31
|
+
"[contenteditable='true']",
|
|
32
|
+
"[tabindex]:not([tabindex='-1'])",
|
|
33
|
+
].join(", ");
|
|
34
|
+
|
|
23
35
|
type SidebarContextProps = {
|
|
24
36
|
state: "expanded" | "collapsed";
|
|
25
37
|
open: boolean;
|
|
@@ -143,13 +155,31 @@ function Sidebar({
|
|
|
143
155
|
variant?: "sidebar" | "floating" | "inset";
|
|
144
156
|
collapsible?: "offcanvas" | "icon" | "none";
|
|
145
157
|
}) {
|
|
146
|
-
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
158
|
+
const { isMobile, state, openMobile, setOpenMobile, open, setOpen } = useSidebar();
|
|
159
|
+
|
|
160
|
+
function handleSurfaceClickCapture(event: React.MouseEvent<HTMLDivElement>) {
|
|
161
|
+
const clickedInteractiveElement =
|
|
162
|
+
event.target instanceof Element && event.target.closest(INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR);
|
|
163
|
+
|
|
164
|
+
if (clickedInteractiveElement) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!open) {
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
setOpen(true);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
setOpen(false);
|
|
176
|
+
}
|
|
147
177
|
|
|
148
178
|
if (collapsible === "none") {
|
|
149
179
|
return (
|
|
150
180
|
<div
|
|
151
181
|
data-slot="sidebar"
|
|
152
|
-
className={cn("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
|
182
|
+
className={cn("bg-sidebar-surface text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
|
153
183
|
{...props}
|
|
154
184
|
>
|
|
155
185
|
{children}
|
|
@@ -164,7 +194,7 @@ function Sidebar({
|
|
|
164
194
|
data-sidebar="sidebar"
|
|
165
195
|
data-slot="sidebar"
|
|
166
196
|
data-mobile="true"
|
|
167
|
-
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
197
|
+
className="bg-sidebar-surface text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
168
198
|
style={
|
|
169
199
|
{
|
|
170
200
|
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
|
@@ -213,7 +243,7 @@ function Sidebar({
|
|
|
213
243
|
// Adjust the padding for floating and inset variants.
|
|
214
244
|
variant === "floating" || variant === "inset"
|
|
215
245
|
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
|
216
|
-
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
246
|
+
: "border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
217
247
|
className,
|
|
218
248
|
)}
|
|
219
249
|
{...props}
|
|
@@ -221,7 +251,8 @@ function Sidebar({
|
|
|
221
251
|
<div
|
|
222
252
|
data-sidebar="sidebar"
|
|
223
253
|
data-slot="sidebar-inner"
|
|
224
|
-
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
|
254
|
+
className="bg-sidebar-surface group-data-[variant=floating]:border-sidebar-border flex h-full w-full cursor-pointer flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
|
255
|
+
onClickCapture={handleSurfaceClickCapture}
|
|
225
256
|
>
|
|
226
257
|
{children}
|
|
227
258
|
</div>
|
|
@@ -231,9 +262,7 @@ function Sidebar({
|
|
|
231
262
|
}
|
|
232
263
|
|
|
233
264
|
function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
|
|
234
|
-
const {
|
|
235
|
-
|
|
236
|
-
const isOpen = isMobile ? openMobile : state === "expanded";
|
|
265
|
+
const { toggleSidebar } = useSidebar();
|
|
237
266
|
|
|
238
267
|
return (
|
|
239
268
|
<Button
|
|
@@ -248,12 +277,21 @@ function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<t
|
|
|
248
277
|
}}
|
|
249
278
|
{...props}
|
|
250
279
|
>
|
|
251
|
-
|
|
280
|
+
<SidebarToggleIcon className="size-4.5" />
|
|
252
281
|
<span className="sr-only">Toggle Sidebar</span>
|
|
253
282
|
</Button>
|
|
254
283
|
);
|
|
255
284
|
}
|
|
256
285
|
|
|
286
|
+
function SidebarToggleIcon({ className, ...props }: React.ComponentProps<"svg">) {
|
|
287
|
+
return (
|
|
288
|
+
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className} {...props}>
|
|
289
|
+
<rect x="2.75" y="2.75" width="14.5" height="14.5" rx="4" stroke="currentColor" strokeWidth="1.5" />
|
|
290
|
+
<path d="M10 4.75V15.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
291
|
+
</svg>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
257
295
|
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
|
258
296
|
const { toggleSidebar } = useSidebar();
|
|
259
297
|
|
|
@@ -266,9 +304,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
|
|
266
304
|
onClick={toggleSidebar}
|
|
267
305
|
title="Toggle Sidebar"
|
|
268
306
|
className={cn(
|
|
269
|
-
"
|
|
270
|
-
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
|
271
|
-
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
307
|
+
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
|
|
272
308
|
"[@media(hover:hover)]:hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
|
273
309
|
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
274
310
|
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
|
@@ -316,21 +352,22 @@ function SidebarHeader({ className, icon, title, children, ...props }: SidebarHe
|
|
|
316
352
|
<div
|
|
317
353
|
data-slot="sidebar-header"
|
|
318
354
|
data-sidebar="header"
|
|
319
|
-
className={cn("flex flex-col gap-2
|
|
355
|
+
className={cn("flex flex-col gap-2 py-2", className)}
|
|
320
356
|
{...props}
|
|
321
357
|
>
|
|
322
|
-
<div className="flex h-8 items-center gap-2 px-
|
|
323
|
-
<
|
|
324
|
-
<span className="transition-opacity group-data-[collapsible=icon]:group-hover:opacity-0">
|
|
325
|
-
|
|
326
|
-
<SidebarTrigger className="!size-4 !p-0" />
|
|
327
|
-
</div>
|
|
328
|
-
</div>
|
|
329
|
-
{title && (
|
|
330
|
-
<span className="text-foreground text-md min-w-0 truncate font-medium group-data-[collapsible=icon]:hidden">
|
|
331
|
-
{title}
|
|
358
|
+
<div className="flex h-8 items-center gap-2 px-3">
|
|
359
|
+
<span className="relative flex size-8 shrink-0 items-center justify-center">
|
|
360
|
+
<span className="transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-0">
|
|
361
|
+
{icon}
|
|
332
362
|
</span>
|
|
333
|
-
|
|
363
|
+
<SidebarTrigger
|
|
364
|
+
tabIndex={-1}
|
|
365
|
+
className="absolute inset-0 opacity-0 transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-100"
|
|
366
|
+
/>
|
|
367
|
+
</span>
|
|
368
|
+
<span className="text-foreground text-md min-w-0 truncate font-medium group-data-[collapsible=icon]:hidden">
|
|
369
|
+
{title}
|
|
370
|
+
</span>
|
|
334
371
|
<SidebarTrigger className="ml-auto shrink-0 group-data-[collapsible=icon]:hidden" />
|
|
335
372
|
</div>
|
|
336
373
|
{children}
|
|
@@ -376,7 +413,7 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
376
413
|
<div
|
|
377
414
|
data-slot="sidebar-group"
|
|
378
415
|
data-sidebar="group"
|
|
379
|
-
className={cn("relative flex w-full min-w-0 flex-col
|
|
416
|
+
className={cn("relative flex w-full min-w-0 flex-col", className)}
|
|
380
417
|
{...props}
|
|
381
418
|
/>
|
|
382
419
|
);
|
|
@@ -453,7 +490,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
|
453
490
|
<li
|
|
454
491
|
data-slot="sidebar-menu-item"
|
|
455
492
|
data-sidebar="menu-item"
|
|
456
|
-
className={cn("group/menu-item relative", className)}
|
|
493
|
+
className={cn("group/menu-item relative px-3", className)}
|
|
457
494
|
{...props}
|
|
458
495
|
/>
|
|
459
496
|
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
|
|
5
|
+
export function H1({ children, className }: { children: ReactNode; className?: string }) {
|
|
6
|
+
return <h1 className={cn("scroll-m-20 type-display-sm font-bold", className)}>{children}</h1>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function H2({ children, className }: { children: ReactNode; className?: string }) {
|
|
10
|
+
return <h2 className={cn("scroll-m-20 type-display-xs font-semibold", className)}>{children}</h2>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function H3({ children, className }: { children: ReactNode; className?: string }) {
|
|
14
|
+
return <h3 className={cn("scroll-m-20 type-text-lg font-semibold", className)}>{children}</h3>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function H4({ children, className }: { children: ReactNode; className?: string }) {
|
|
18
|
+
return <h4 className={cn("scroll-m-20 type-text-md font-semibold", className)}>{children}</h4>;
|
|
19
|
+
}
|
|
@@ -8,7 +8,9 @@ export function useCopyToClipboard({ resetDelay = 2000 }: { resetDelay?: number
|
|
|
8
8
|
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
return () => {
|
|
11
|
-
if (timeoutRef.current)
|
|
11
|
+
if (timeoutRef.current) {
|
|
12
|
+
clearTimeout(timeoutRef.current);
|
|
13
|
+
}
|
|
12
14
|
};
|
|
13
15
|
}, []);
|
|
14
16
|
|
|
@@ -16,7 +18,9 @@ export function useCopyToClipboard({ resetDelay = 2000 }: { resetDelay?: number
|
|
|
16
18
|
(text: string) => {
|
|
17
19
|
navigator.clipboard.writeText(text).then(() => {
|
|
18
20
|
setIsCopied(true);
|
|
19
|
-
if (timeoutRef.current)
|
|
21
|
+
if (timeoutRef.current) {
|
|
22
|
+
clearTimeout(timeoutRef.current);
|
|
23
|
+
}
|
|
20
24
|
timeoutRef.current = setTimeout(() => setIsCopied(false), resetDelay);
|
|
21
25
|
});
|
|
22
26
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plus } from "lucide-react";
|
|
1
|
+
import { ArrowRight, Plus, Sparkles } from "lucide-react";
|
|
2
2
|
|
|
3
3
|
import { Button } from "../components/button";
|
|
4
4
|
|
|
@@ -323,6 +323,28 @@ export const AllVariants = () => {
|
|
|
323
323
|
</Button>
|
|
324
324
|
</div>
|
|
325
325
|
|
|
326
|
+
{/* ── CTA (animated gradient ring) ────────────────────────────────── */}
|
|
327
|
+
<span className={SECTION_HEADER}>CTA — animated</span>
|
|
328
|
+
<p className="type-text-xs text-muted-foreground -mt-2 max-w-md">
|
|
329
|
+
Hover to rotate the conic gradient around the border and ignite the soft halo.
|
|
330
|
+
</p>
|
|
331
|
+
|
|
332
|
+
<div className="flex items-center gap-6">
|
|
333
|
+
<Button variant="cta" iconTrailing={<ArrowRight />}>
|
|
334
|
+
Get started
|
|
335
|
+
</Button>
|
|
336
|
+
<Button variant="cta" icon={<Sparkles />} iconTrailing={<ArrowRight />}>
|
|
337
|
+
Launch server
|
|
338
|
+
</Button>
|
|
339
|
+
<Button variant="cta">Deploy now</Button>
|
|
340
|
+
<Button variant="cta" iconTrailing={<ArrowRight />} disabled>
|
|
341
|
+
Disabled
|
|
342
|
+
</Button>
|
|
343
|
+
<Button variant="cta" iconTrailing={<ArrowRight />} loading>
|
|
344
|
+
Deploying
|
|
345
|
+
</Button>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
326
348
|
{/* ── asChild ─────────────────────────────────────────────────────── */}
|
|
327
349
|
<span className={SECTION_HEADER}>asChild</span>
|
|
328
350
|
|
|
@@ -62,7 +62,7 @@ export const AllVariants: Story = () => (
|
|
|
62
62
|
<Sidebar collapsible="icon">
|
|
63
63
|
<TeamHeader />
|
|
64
64
|
<SidebarContent>
|
|
65
|
-
<SidebarGroup
|
|
65
|
+
<SidebarGroup>
|
|
66
66
|
<SidebarGroupContent>
|
|
67
67
|
<SidebarMenu>
|
|
68
68
|
<SidebarMenuItem>
|
|
@@ -107,7 +107,8 @@ export const AllVariants: Story = () => (
|
|
|
107
107
|
<UserFooter />
|
|
108
108
|
</Sidebar>
|
|
109
109
|
<SidebarInset>
|
|
110
|
-
<div className="p-4">
|
|
110
|
+
<div className="flex items-center gap-2 p-4">
|
|
111
|
+
<SidebarTrigger />
|
|
111
112
|
<span className="type-text-sm text-muted-foreground">Sub-menus expand inline under their parent</span>
|
|
112
113
|
</div>
|
|
113
114
|
</SidebarInset>
|
|
@@ -118,7 +119,7 @@ export const AllVariants: Story = () => (
|
|
|
118
119
|
<Sidebar collapsible="icon">
|
|
119
120
|
<TeamHeader />
|
|
120
121
|
<SidebarContent>
|
|
121
|
-
<SidebarGroup
|
|
122
|
+
<SidebarGroup>
|
|
122
123
|
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
|
|
123
124
|
<SidebarGroupContent>
|
|
124
125
|
<SidebarMenu>
|
|
@@ -146,7 +147,7 @@ export const AllVariants: Story = () => (
|
|
|
146
147
|
</SidebarGroupContent>
|
|
147
148
|
</SidebarGroup>
|
|
148
149
|
<SidebarSeparator />
|
|
149
|
-
<SidebarGroup
|
|
150
|
+
<SidebarGroup>
|
|
150
151
|
<SidebarGroupLabel>Account</SidebarGroupLabel>
|
|
151
152
|
<SidebarGroupContent>
|
|
152
153
|
<SidebarMenu>
|
|
@@ -169,7 +170,8 @@ export const AllVariants: Story = () => (
|
|
|
169
170
|
<UserFooter />
|
|
170
171
|
</Sidebar>
|
|
171
172
|
<SidebarInset>
|
|
172
|
-
<div className="p-4">
|
|
173
|
+
<div className="flex items-center gap-2 p-4">
|
|
174
|
+
<SidebarTrigger />
|
|
173
175
|
<span className="type-text-sm text-muted-foreground">Groups with labels, badges, and separator</span>
|
|
174
176
|
</div>
|
|
175
177
|
</SidebarInset>
|
|
@@ -180,7 +182,7 @@ export const AllVariants: Story = () => (
|
|
|
180
182
|
<Sidebar collapsible="icon">
|
|
181
183
|
<TeamHeader />
|
|
182
184
|
<SidebarContent>
|
|
183
|
-
<SidebarGroup
|
|
185
|
+
<SidebarGroup>
|
|
184
186
|
<SidebarGroupContent>
|
|
185
187
|
<SidebarMenu>
|
|
186
188
|
{["skeleton-1", "skeleton-2", "skeleton-3", "skeleton-4", "skeleton-5"].map((key) => (
|
|
@@ -195,7 +197,8 @@ export const AllVariants: Story = () => (
|
|
|
195
197
|
<UserFooter />
|
|
196
198
|
</Sidebar>
|
|
197
199
|
<SidebarInset>
|
|
198
|
-
<div className="p-4">
|
|
200
|
+
<div className="flex items-center gap-2 p-4">
|
|
201
|
+
<SidebarTrigger />
|
|
199
202
|
<span className="type-text-sm text-muted-foreground">Loading state with skeleton placeholders</span>
|
|
200
203
|
</div>
|
|
201
204
|
</SidebarInset>
|
|
@@ -214,7 +217,7 @@ export const AllVariants: Story = () => (
|
|
|
214
217
|
</div>
|
|
215
218
|
</SidebarHeader>
|
|
216
219
|
<SidebarContent>
|
|
217
|
-
<SidebarGroup
|
|
220
|
+
<SidebarGroup>
|
|
218
221
|
<SidebarGroupContent>
|
|
219
222
|
<SidebarMenu>
|
|
220
223
|
<SidebarMenuItem>
|
package/src/styles/tokens.css
CHANGED
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
|
|
40
40
|
--color-ring: #f22b79; /* Figma: focus-ring */
|
|
41
41
|
|
|
42
|
+
/* cta — decorative gradient accent, used with --color-primary in the CTA button ring */
|
|
43
|
+
--color-cta-accent: #6eece7; /* Figma: CTA border accent (cyan) */
|
|
44
|
+
|
|
42
45
|
/* sidebar */
|
|
43
46
|
--color-sidebar: #f8fafa; /* Figma: bg-secondary-subtle */
|
|
44
47
|
--color-sidebar-foreground: #3a4848; /* Figma: fg-secondary */
|
|
@@ -106,6 +109,8 @@
|
|
|
106
109
|
/* animations */
|
|
107
110
|
--animate-accordion-down: accordion-down 200ms ease-out;
|
|
108
111
|
--animate-accordion-up: accordion-up 200ms ease-out;
|
|
112
|
+
--animate-beacon-ring-core: beacon-ring-core 2.2s ease-in-out infinite;
|
|
113
|
+
--animate-beacon-ring-pulse: beacon-ring-pulse 2.2s ease-in-out infinite;
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
@keyframes accordion-down {
|
|
@@ -126,6 +131,186 @@
|
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
|
|
134
|
+
@keyframes beacon-ring-core {
|
|
135
|
+
0%,
|
|
136
|
+
100% {
|
|
137
|
+
opacity: 0.55;
|
|
138
|
+
transform: scale(0.98);
|
|
139
|
+
}
|
|
140
|
+
50% {
|
|
141
|
+
opacity: 0.85;
|
|
142
|
+
transform: scale(1.02);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@keyframes beacon-ring-pulse {
|
|
147
|
+
0%,
|
|
148
|
+
100% {
|
|
149
|
+
opacity: 0.88;
|
|
150
|
+
transform: scale(1);
|
|
151
|
+
filter: drop-shadow(0 0 0 rgba(59, 130, 246, 0));
|
|
152
|
+
}
|
|
153
|
+
50% {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
transform: scale(1.02);
|
|
156
|
+
filter: drop-shadow(0 0 8px rgba(59, 130, 246, 0.24));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ─── CTA button — animated conic gradient ring ───────────────────────────── */
|
|
161
|
+
|
|
162
|
+
@property --cta-angle {
|
|
163
|
+
syntax: "<angle>";
|
|
164
|
+
inherits: false;
|
|
165
|
+
initial-value: 135deg;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@keyframes cta-rotate {
|
|
169
|
+
to {
|
|
170
|
+
--cta-angle: 495deg;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.button-cta {
|
|
175
|
+
position: relative;
|
|
176
|
+
isolation: isolate;
|
|
177
|
+
/* light mode: whisper-of-gradient surface tint + soft rose drop-shadow */
|
|
178
|
+
background-image: linear-gradient(
|
|
179
|
+
135deg,
|
|
180
|
+
color-mix(in oklab, var(--color-primary) 5%, transparent) 0%,
|
|
181
|
+
color-mix(in oklab, var(--color-cta-accent) 5%, transparent) 100%
|
|
182
|
+
);
|
|
183
|
+
box-shadow:
|
|
184
|
+
0 6px 24px -10px color-mix(in oklab, var(--color-primary) 38%, transparent),
|
|
185
|
+
0 2px 6px -4px color-mix(in oklab, var(--color-cta-accent) 30%, transparent);
|
|
186
|
+
transition:
|
|
187
|
+
box-shadow 400ms ease,
|
|
188
|
+
transform 300ms ease,
|
|
189
|
+
filter 300ms ease;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@media (hover: hover) {
|
|
193
|
+
.button-cta:hover {
|
|
194
|
+
box-shadow:
|
|
195
|
+
0 10px 30px -8px color-mix(in oklab, var(--color-primary) 52%, transparent),
|
|
196
|
+
0 3px 10px -3px color-mix(in oklab, var(--color-cta-accent) 40%, transparent);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* dark mode: solid inverted surface, no tint or shadow — let the halo do the work */
|
|
201
|
+
.dark .button-cta {
|
|
202
|
+
background-image: none;
|
|
203
|
+
box-shadow: none;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.button-cta::before,
|
|
207
|
+
.button-cta::after {
|
|
208
|
+
content: "";
|
|
209
|
+
position: absolute;
|
|
210
|
+
inset: 0;
|
|
211
|
+
border-radius: inherit;
|
|
212
|
+
pointer-events: none;
|
|
213
|
+
/* Always "running" in browser terms, but paused at rest — freezes at current
|
|
214
|
+
angle on unhover instead of snapping back. */
|
|
215
|
+
animation: cta-rotate 3.2s linear infinite;
|
|
216
|
+
animation-play-state: paused;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Gradient ring (masked so only the border shows) */
|
|
220
|
+
.button-cta::before {
|
|
221
|
+
padding: 1.5px;
|
|
222
|
+
background: conic-gradient(
|
|
223
|
+
from var(--cta-angle),
|
|
224
|
+
var(--color-cta-accent) 0deg,
|
|
225
|
+
var(--color-primary) 150deg,
|
|
226
|
+
var(--color-cta-accent) 300deg,
|
|
227
|
+
var(--color-cta-accent) 360deg
|
|
228
|
+
);
|
|
229
|
+
-webkit-mask:
|
|
230
|
+
linear-gradient(#000 0 0) content-box,
|
|
231
|
+
linear-gradient(#000 0 0);
|
|
232
|
+
-webkit-mask-composite: xor;
|
|
233
|
+
mask-composite: exclude;
|
|
234
|
+
transition: filter 400ms ease;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Blurred glow halo behind the button — subtle in light, bolder in dark */
|
|
238
|
+
.button-cta::after {
|
|
239
|
+
z-index: -1;
|
|
240
|
+
background: conic-gradient(
|
|
241
|
+
from var(--cta-angle),
|
|
242
|
+
var(--color-cta-accent) 0deg,
|
|
243
|
+
var(--color-primary) 150deg,
|
|
244
|
+
var(--color-cta-accent) 300deg,
|
|
245
|
+
var(--color-cta-accent) 360deg
|
|
246
|
+
);
|
|
247
|
+
filter: blur(12px);
|
|
248
|
+
opacity: 0.05;
|
|
249
|
+
transition: opacity 400ms ease;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.dark .button-cta::after {
|
|
253
|
+
opacity: 0.14;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@media (hover: hover) {
|
|
257
|
+
.button-cta:hover::before,
|
|
258
|
+
.button-cta:hover::after {
|
|
259
|
+
animation-play-state: running;
|
|
260
|
+
}
|
|
261
|
+
.button-cta:hover::before {
|
|
262
|
+
filter: saturate(1.15) brightness(1.05);
|
|
263
|
+
}
|
|
264
|
+
.button-cta:hover::after {
|
|
265
|
+
opacity: 0.18;
|
|
266
|
+
}
|
|
267
|
+
.dark .button-cta:hover::after {
|
|
268
|
+
opacity: 0.32;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.button-cta:focus-visible::before,
|
|
273
|
+
.button-cta:focus-visible::after {
|
|
274
|
+
animation-play-state: running;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.button-cta:disabled::before,
|
|
278
|
+
.button-cta:disabled::after,
|
|
279
|
+
[aria-busy="true"].button-cta::before,
|
|
280
|
+
[aria-busy="true"].button-cta::after {
|
|
281
|
+
animation-play-state: paused;
|
|
282
|
+
}
|
|
283
|
+
.button-cta:disabled::after {
|
|
284
|
+
opacity: 0;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@media (prefers-reduced-motion: reduce) {
|
|
288
|
+
.button-cta::before,
|
|
289
|
+
.button-cta::after {
|
|
290
|
+
animation: none;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Icon slide on hover (applied to [data-cta-icon-trailing]) */
|
|
295
|
+
.button-cta [data-cta-icon-trailing] {
|
|
296
|
+
transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@media (hover: hover) {
|
|
300
|
+
.button-cta:hover [data-cta-icon-trailing] {
|
|
301
|
+
transform: translateX(2px);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@media (prefers-reduced-motion: reduce) {
|
|
306
|
+
.button-cta [data-cta-icon-trailing] {
|
|
307
|
+
transition: none;
|
|
308
|
+
}
|
|
309
|
+
.button-cta:hover [data-cta-icon-trailing] {
|
|
310
|
+
transform: none;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
129
314
|
/* ─── Dark mode ───────────────────────────────────────────────────────────── */
|
|
130
315
|
|
|
131
316
|
.dark {
|
|
@@ -163,6 +348,9 @@
|
|
|
163
348
|
|
|
164
349
|
--color-ring: #f22b79; /* Figma: focus-ring */
|
|
165
350
|
|
|
351
|
+
/* cta — decorative gradient accent, used with --color-primary in the CTA button ring */
|
|
352
|
+
--color-cta-accent: #6eece7; /* Figma: CTA border accent (cyan) */
|
|
353
|
+
|
|
166
354
|
/* sidebar */
|
|
167
355
|
--color-sidebar: #0c1c1c; /* Figma: bg-secondary */
|
|
168
356
|
--color-sidebar-foreground: #90a4a4; /* Figma: fg-secondary */
|
|
@@ -221,6 +409,14 @@
|
|
|
221
409
|
|
|
222
410
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
223
411
|
|
|
412
|
+
:root {
|
|
413
|
+
--gradient-sidebar: linear-gradient(0deg, #c9e2e280 0%, #ffffff 70%); /* Figma: bg-nav-gradiant-light */
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.dark {
|
|
417
|
+
--gradient-sidebar: linear-gradient(0deg, #213535 0%, #121e1e 70%); /* Figma: bg-nav-gradiant-dark */
|
|
418
|
+
}
|
|
419
|
+
|
|
224
420
|
@layer base {
|
|
225
421
|
* {
|
|
226
422
|
@apply border-border shadow-shadow;
|
|
@@ -274,6 +470,11 @@
|
|
|
274
470
|
|
|
275
471
|
/* ─── Type preset utilities ───────────────────────────────────────────────── */
|
|
276
472
|
|
|
473
|
+
@utility bg-sidebar-surface {
|
|
474
|
+
background-color: var(--color-background);
|
|
475
|
+
background-image: var(--gradient-sidebar);
|
|
476
|
+
}
|
|
477
|
+
|
|
277
478
|
@utility type-display-2xl {
|
|
278
479
|
font-family: var(--font-display);
|
|
279
480
|
font-size: var(--font-size-display-2xl);
|