Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"./dropdown-menu": "./src/components/dropdown-menu.tsx",
"./empty": "./src/components/empty.tsx",
"./field": "./src/components/field.tsx",
"./glow-follow": "./src/components/glow-follow.ts",
"./hover-card": "./src/components/hover-card.tsx",
"./input": "./src/components/input.tsx",
"./item": "./src/components/item.tsx",
Expand Down Expand Up @@ -61,7 +62,8 @@
"./utils": "./src/utils.ts"
},
"sideEffects": [
"**/*.css"
"**/*.css",
"./src/components/glow-follow.ts"
],
"dependencies": {
"@base-ui/react": "^1.3.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function AccordionItem({
return (
<AccordionPrimitive.Item
data-slot="accordion-item"
className={cn("not-last:border-b", className)}
className={cn("not-last:border-b not-last:border-white/[0.06]", className)}
{...props}
/>
)
Expand All @@ -40,7 +40,7 @@ function AccordionTrigger({
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
"group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent px-2 py-2.5 text-left text-sm font-medium text-white/80 transition-all outline-none hover:bg-white/[0.04] hover:text-white focus-visible:border-primary/[0.45] focus-visible:ring-3 focus-visible:ring-primary/20 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-white/40",
className
)}
{...props}
Expand Down
4 changes: 2 additions & 2 deletions src/components/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function AlertDialogFooter({
<div
data-slot="alert-dialog-footer"
className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t border-white/[0.06] bg-white/[0.03] p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className
)}
{...props}
Expand All @@ -105,7 +105,7 @@ function AlertDialogMedia({
<div
data-slot="alert-dialog-media"
className={cn(
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
"mb-2 inline-flex size-10 items-center justify-center rounded-md border border-white/[0.08] bg-white/[0.05] shadow-[0_0_14px_rgba(255,255,255,0.04)] sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
className
)}
{...props}
Expand Down
12 changes: 6 additions & 6 deletions src/components/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../utils"

const alertVariants = cva(
"group/alert relative grid w-full gap-0.5 rounded-lg border-l-[3px] border border-transparent px-3 py-2.5 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
"group/alert relative grid w-full gap-0.5 rounded-xl border-l-[3px] border border-transparent px-3.5 py-3 text-left text-sm shadow-[0_10px_30px_rgba(0,0,0,0.28),0_0_0_1px_rgba(255,255,255,0.03)_inset] has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default:
"border-border border-l-border bg-card text-card-foreground",
"border-white/10 border-l-white/20 bg-white/[0.035] text-card-foreground [backdrop-filter:blur(12px)] [-webkit-backdrop-filter:blur(12px)]",
info:
"border-blue-500/20 border-l-blue-500 bg-blue-500/5 text-foreground *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-blue-400",
"border-blue-500/20 border-l-blue-500 bg-blue-500/5 text-foreground shadow-[0_10px_30px_rgba(0,0,0,0.28),0_0_18px_rgba(59,130,246,0.08)] *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-blue-400",
success:
"border-emerald-500/20 border-l-emerald-500 bg-emerald-500/5 text-foreground *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-emerald-400",
"border-emerald-500/20 border-l-emerald-500 bg-emerald-500/5 text-foreground shadow-[0_10px_30px_rgba(0,0,0,0.28),0_0_18px_rgba(16,185,129,0.08)] *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-emerald-400",
warning:
"border-amber-400/20 border-l-amber-400 bg-amber-400/5 text-foreground *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-amber-400",
"border-amber-400/20 border-l-amber-400 bg-amber-400/5 text-foreground shadow-[0_10px_30px_rgba(0,0,0,0.28),0_0_18px_rgba(245,181,23,0.10)] *:data-[slot=alert-description]:text-foreground/80 *:[svg]:text-amber-400",
destructive:
"border-destructive/20 border-l-destructive bg-destructive/5 text-destructive *:data-[slot=alert-description]:text-destructive/80 *:[svg]:text-current",
"border-destructive/20 border-l-destructive bg-destructive/5 text-destructive shadow-[0_10px_30px_rgba(0,0,0,0.28),0_0_18px_oklch(0.63_0.24_27_/_0.10)] *:data-[slot=alert-description]:text-destructive/80 *:[svg]:text-current",
},
},
defaultVariants: {
Expand Down
6 changes: 3 additions & 3 deletions src/components/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function Avatar({
data-slot="avatar"
data-size={size}
className={cn(
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
"group/avatar relative flex size-8 shrink-0 rounded-full select-none shadow-[0_0_14px_rgba(255,255,255,0.04)] after:absolute after:inset-0 after:rounded-full after:border after:border-white/[0.12] after:mix-blend-lighten data-[size=lg]:size-10 data-[size=sm]:size-6",
className
)}
{...props}
Expand Down Expand Up @@ -47,7 +47,7 @@ function AvatarFallback({
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
"flex size-full items-center justify-center rounded-full bg-white/[0.08] text-sm text-white/60 group-data-[size=sm]/avatar:text-xs",
className
)}
{...props}
Expand Down Expand Up @@ -92,7 +92,7 @@ function AvatarGroupCount({
<div
data-slot="avatar-group-count"
className={cn(
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-white/[0.08] text-sm text-white/60 ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
className
)}
{...props}
Expand Down
18 changes: 9 additions & 9 deletions src/components/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../utils";

const badgeVariants = cva(
"inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all [&>svg]:size-3",
"inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all shadow-[0_0_10px_rgba(255,255,255,0.03)] [&>svg]:size-3",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
secondary: "bg-secondary text-secondary-foreground",
destructive: "bg-destructive/10 text-destructive",
outline: "border-border text-foreground",
ghost: "hover:bg-muted hover:text-muted-foreground",
beta: "bg-amber-500/15 text-amber-300 border-amber-500/30",
stable: "bg-emerald-500/15 text-emerald-300 border-emerald-500/30",
default: "border-primary/25 bg-primary/[0.15] text-primary shadow-[0_0_12px_rgba(245,181,23,0.14)]",
secondary: "border-white/10 bg-white/10 text-white/80",
destructive: "border-destructive/25 bg-destructive/10 text-destructive shadow-[0_0_12px_oklch(0.63_0.24_27_/_0.13)]",
outline: "border-white/10 bg-white/[0.03] text-white/80",
ghost: "text-white/70 hover:bg-white/[0.06] hover:text-white",
beta: "bg-amber-500/[0.15] text-amber-300 border-amber-500/30 shadow-[0_0_12px_rgba(245,181,23,0.12)]",
stable: "bg-emerald-500/[0.15] text-emerald-300 border-emerald-500/30 shadow-[0_0_12px_rgba(16,185,129,0.12)]",
deprecated: "bg-destructive/10 text-destructive border-destructive/25",
new: "bg-blue-500/15 text-blue-300 border-blue-500/30",
new: "bg-blue-500/[0.15] text-blue-300 border-blue-500/30 shadow-[0_0_12px_rgba(59,130,246,0.12)]",
},
},
defaultVariants: { variant: "default" },
Expand Down
4 changes: 2 additions & 2 deletions src/components/button-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function ButtonGroupText({
return (
<Comp
className={cn(
"flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
"glass-surface flex items-center gap-2 rounded-lg border-white/10 px-2.5 text-sm font-medium text-white/75 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
Expand All @@ -67,7 +67,7 @@ function ButtonGroupSeparator({
data-slot="button-group-separator"
orientation={orientation}
className={cn(
"relative self-stretch bg-input data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto",
"relative self-stretch bg-white/[0.08] data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto",
className
)}
{...props}
Expand Down
24 changes: 12 additions & 12 deletions src/components/button-variants.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { cva, type VariantProps } from "class-variance-authority";

export const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all duration-200 outline-none select-none focus-visible:border-primary/50 focus-visible:ring-3 focus-visible:ring-primary/20 active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-[0_2px_16px_oklch(0.80_0.17_90_/_0.30),0_1px_3px_rgba(0,0,0,0.3)] hover:bg-primary/90 hover:shadow-[0_4px_24px_oklch(0.80_0.17_90_/_0.42),0_1px_3px_rgba(0,0,0,0.3)]",
"rounded-full border-transparent bg-gradient-kleff text-primary-foreground shadow-[0_12px_27px_rgba(196,143,0,0.22)] hover:opacity-95",
outline:
"border-white/[0.1] bg-white/[0.03] hover:bg-white/[0.07] hover:border-white/[0.15] hover:text-foreground",
"glass-surface border-white/10 text-white/80 shadow-[0_0_15px_rgba(255,255,255,0.03)] hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white",
secondary:
"bg-white/[0.06] border border-white/[0.08] text-secondary-foreground hover:bg-white/[0.1]",
"border-white/[0.08] bg-white/[0.06] text-white/90 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] hover:border-white/[0.14] hover:bg-white/[0.1] hover:text-white",
ghost:
"hover:bg-white/[0.06] hover:text-foreground",
"text-white/70 hover:bg-white/[0.06] hover:text-white",
destructive:
"bg-destructive/10 border border-destructive/20 text-destructive hover:bg-destructive/20",
"border-destructive/25 bg-destructive/10 text-destructive shadow-[0_0_16px_oklch(0.63_0.24_27_/_0.12)] hover:border-destructive/[0.35] hover:bg-destructive/20 hover:shadow-[0_0_22px_oklch(0.63_0.24_27_/_0.20)]",
link:
"text-primary underline-offset-4 hover:underline",
"text-primary underline-offset-4 hover:text-primary/[0.85] hover:drop-shadow-[0_0_8px_rgba(245,181,23,0.35)]",
},
size: {
default: "h-8 gap-1.5 px-3",
default: "h-9 gap-1.5 px-3.5",
xs: "h-6 gap-1 rounded-md px-2 text-xs [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-md px-2.5 text-[0.8rem] [&_svg:not([class*='size-'])]:size-3.5",
sm: "h-8 gap-1 rounded-md px-3 text-[0.8rem] [&_svg:not([class*='size-'])]:size-3.5",
lg: "h-10 gap-2 px-4 text-base",
icon: "size-8",
icon: "size-9",
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-7 rounded-md",
"icon-lg": "size-9",
"icon-sm": "size-8 rounded-md",
"icon-lg": "size-10",
},
},
defaultVariants: { variant: "default", size: "default" },
Expand Down
76 changes: 73 additions & 3 deletions src/components/card.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,89 @@
import * as React from "react";
import { clearGlowFollowPosition, installGlowFollow, setGlowFollowPosition } from "./glow-follow";
import { cn } from "../utils";

// Ensure global glow-follow listeners are installed when this module loads.
installGlowFollow();

type CardProps = React.ComponentProps<"div"> & {
frosted?: boolean;
focus?: boolean;
spotlight?: boolean;
size?: "default" | "sm";
"data-frosted"?: "true" | "false";
"data-focus"?: "true" | "false";
"data-spotlight"?: "true" | "false";
};

function Card({
className,
frosted = false,
focus = false,
spotlight = false,
size = "default",
"data-frosted": dataFrosted,
"data-focus": dataFocus,
"data-spotlight": dataSpotlight,
onMouseEnter,
onMouseLeave,
onMouseMove,
onPointerEnter,
onPointerLeave,
onPointerMove,
...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
}: CardProps) {
const glassState = frosted ? "true" : dataFrosted === "true" || dataFrosted === "false" ? dataFrosted : undefined;
const focusState = focus ? "true" : dataFocus === "true" || dataFocus === "false" ? dataFocus : undefined;
const spotlightState = spotlight ? "true" : dataSpotlight === "true" || dataSpotlight === "false" ? dataSpotlight : undefined;
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
onMouseMove?.(event);

setGlowFollowPosition(event.currentTarget, event.clientX, event.clientY);
};
const handleMouseEnter = (event: React.MouseEvent<HTMLDivElement>) => {
onMouseEnter?.(event);

setGlowFollowPosition(event.currentTarget, event.clientX, event.clientY);
};
const handleMouseLeave = (event: React.MouseEvent<HTMLDivElement>) => {
onMouseLeave?.(event);

clearGlowFollowPosition(event.currentTarget);
};
const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
onPointerMove?.(event);

setGlowFollowPosition(event.currentTarget, event.clientX, event.clientY);
};
const handlePointerEnter = (event: React.PointerEvent<HTMLDivElement>) => {
onPointerEnter?.(event);

setGlowFollowPosition(event.currentTarget, event.clientX, event.clientY);
};
const handlePointerLeave = (event: React.PointerEvent<HTMLDivElement>) => {
onPointerLeave?.(event);

clearGlowFollowPosition(event.currentTarget);
};

return (
<div
data-slot="card"
data-frosted={glassState}
data-focus={focusState}
data-spotlight={spotlightState}
data-size={size}
className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl border border-white/[0.07] bg-card py-4 text-sm text-card-foreground shadow-[0_1px_3px_rgba(0,0,0,0.4),0_0_0_1px_rgba(255,255,255,0.03)_inset] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
glassState ? "overview-glass-card" : "rounded-xl border border-border bg-card",
"group/card flex flex-col gap-4 py-4 text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-2xl *:[img:last-child]:rounded-b-2xl",
className
)}
onMouseEnter={spotlightState === "true" ? handleMouseEnter : onMouseEnter}
onMouseLeave={spotlightState === "true" ? handleMouseLeave : onMouseLeave}
onMouseMove={spotlightState === "true" ? handleMouseMove : onMouseMove}
onPointerEnter={spotlightState === "true" ? handlePointerEnter : onPointerEnter}
onPointerLeave={spotlightState === "true" ? handlePointerLeave : onPointerLeave}
onPointerMove={spotlightState === "true" ? handlePointerMove : onPointerMove}
{...props}
/>
);
Expand Down Expand Up @@ -80,7 +150,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-xl border-t border-white/[0.06] bg-white/[0.02] p-4 group-data-[size=sm]/card:p-3",
"flex items-center rounded-b-2xl border-t border-white/[0.06] bg-white/[0.02] p-4 group-data-[size=sm]/card:p-3",
className
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion src/components/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function ChartTooltipContent({
return (
<div
className={cn(
"grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
"glass-dropdown grid min-w-32 items-start gap-1.5 rounded-lg px-2.5 py-1.5 text-xs",
className
)}
>
Expand Down
Loading
Loading