continue re-design

This commit is contained in:
Developers Digest
2025-09-05 13:06:17 -04:00
parent b96d048dbd
commit 836b085f75
270 changed files with 32269 additions and 5182 deletions
@@ -0,0 +1,148 @@
"use client";
import React from "react";
import { cn } from "@/utils/cn";
import { LucideIcon } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import AnimatedWidth from "@/components/shared/layout/animated-width";
interface CapsuleButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon?: LucideIcon | React.ComponentType<{ className?: string }>;
iconPosition?: "left" | "right";
children: React.ReactNode;
size?: "sm" | "md" | "lg";
fullWidth?: boolean;
variant?: "primary" | "secondary" | "tertiary" | "ghost";
loading?: boolean;
}
export function CapsuleButton({
icon: Icon,
iconPosition = "left",
children,
className,
size = "md",
fullWidth = false,
variant = "primary",
loading = false,
disabled,
...props
}: CapsuleButtonProps) {
const [isPressed, setIsPressed] = React.useState(false);
const sizeClasses = {
sm: "h-32 px-16 text-label-small gap-6",
md: "h-40 px-20 text-label-medium gap-8",
lg: "h-40 px-20 text-label-medium gap-8",
};
const iconSizes = {
sm: "w-14 h-14",
md: "w-16 h-16",
lg: "w-16 h-16",
};
const variants = {
primary: [
"bg-heat-100 text-white",
"hover:bg-heat-200",
"active:scale-[0.98]",
"shadow-[0_1px_2px_rgba(0,0,0,0.05)]",
"hover:shadow-[0_4px_12px_rgba(250,93,25,0.25)]",
],
secondary: [
"bg-black text-white",
"hover:bg-black/90",
"active:scale-[0.98]",
"shadow-[0_1px_2px_rgba(0,0,0,0.05)]",
"hover:shadow-[0_4px_12px_rgba(0,0,0,0.15)]",
],
tertiary: [
"bg-white text-black border border-black-alpha-8",
"hover:bg-black-alpha-4 hover:border-black-alpha-12",
"active:scale-[0.98]",
],
ghost: [
"bg-transparent text-black-alpha-60",
"hover:text-black hover:bg-black-alpha-4",
"active:scale-[0.98]",
],
};
const isDisabled = disabled || loading;
return (
<button
className={cn(
// Base styles
"inline-flex items-center justify-center rounded-full transition-all duration-200",
// Size
sizeClasses[size],
// Variant
variants[variant],
// Full width
fullWidth && "w-full",
// Disabled state
isDisabled && [
"opacity-50 cursor-not-allowed",
"hover:shadow-none hover:bg-current",
],
// Pressed state
isPressed && "scale-[0.98]",
className,
)}
disabled={isDisabled}
onMouseDown={() => !isDisabled && setIsPressed(true)}
onMouseUp={() => setIsPressed(false)}
onMouseLeave={() => setIsPressed(false)}
{...props}
>
<AnimatedWidth initial={{ width: "auto" }}>
<AnimatePresence initial={false} mode="popLayout">
{loading ? (
<motion.div
key="loading"
animate={{ opacity: 1, filter: "blur(0px)", scale: 1 }}
className="flex gap-8 items-center justify-center"
exit={{ opacity: 0, filter: "blur(2px)", scale: 0.9 }}
initial={{ opacity: 0, filter: "blur(2px)", scale: 0.95 }}
>
<span>Loading...</span>
</motion.div>
) : (
<motion.div
key="content"
animate={{ opacity: 1, filter: "blur(0px)", scale: 1 }}
className="flex gap-8 items-center justify-center"
exit={{ opacity: 0, filter: "blur(2px)", scale: 0.9 }}
initial={{ opacity: 0, filter: "blur(2px)", scale: 0.95 }}
>
{Icon && iconPosition === "left" && (
<span
className={cn(
iconSizes[size],
"flex-shrink-0 inline-flex items-center justify-center",
)}
>
<Icon className="w-full h-full" />
</span>
)}
<span>{children}</span>
{Icon && iconPosition === "right" && (
<span
className={cn(
iconSizes[size],
"flex-shrink-0 inline-flex items-center justify-center",
)}
>
<Icon className="w-full h-full" />
</span>
)}
</motion.div>
)}
</AnimatePresence>
</AnimatedWidth>
</button>
);
}
@@ -0,0 +1,48 @@
import Link from "next/link";
import { cn } from "@/utils/cn";
interface FireActionLinkProps {
href?: string;
label: string;
className?: string;
variant?: "link" | "button";
onClick?: () => void;
}
export function FireActionLink({
href,
label,
className,
variant = "link",
onClick,
}: FireActionLinkProps) {
const baseClasses =
variant === "button"
? cn(
"inline-block py-4 px-8 rounded-6",
"text-label-small text-heat-100 bg-heat-4",
"hover:bg-heat-8 transition-all",
"active:scale-[0.98]",
className,
)
: cn(
"text-label-small text-secondary hover:text-heat-100 transition-all",
"hover:underline underline-offset-4",
"active:scale-[0.98]",
className,
);
if (onClick) {
return (
<button onClick={onClick} className={baseClasses}>
{label}
</button>
);
}
return (
<Link href={href || "#"} className={baseClasses}>
{label}
</Link>
);
}
+4
View File
@@ -0,0 +1,4 @@
// Button Components
export { SlateButton } from "./slate-button";
// export { HeatButton } from "./heat-button";
export { FireActionLink } from "./fire-action-link";
+122
View File
@@ -0,0 +1,122 @@
"use client";
import React from "react";
import { cn } from "@/utils/cn";
import { LucideIcon } from "lucide-react";
interface SlateButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon?:
| LucideIcon
| React.ComponentType<{
className?: string;
isHovered?: boolean;
isOpen?: boolean;
}>
| React.ReactNode;
iconPosition?: "left" | "right";
children: React.ReactNode;
size?: "sm" | "md" | "lg";
fullWidth?: boolean;
isLoading?: boolean;
isOpen?: boolean;
}
export const SlateButton = React.forwardRef<
HTMLButtonElement,
SlateButtonProps
>(
(
{
icon: Icon,
iconPosition = "left",
children,
className,
size = "md",
fullWidth = false,
isLoading = false,
isOpen = false,
disabled,
...props
},
ref,
) => {
const [isHovered, setIsHovered] = React.useState(false);
const sizeClasses = {
sm: "h-32 px-12 text-body-small gap-6",
md: "h-40 px-16 text-body-medium gap-8",
lg: "h-48 px-24 text-body-large gap-10",
};
const iconSizes = {
sm: "w-14 h-14",
md: "w-16 h-16",
lg: "w-20 h-20",
};
return (
<button
ref={ref}
className={cn(
// Base styles
"inline-flex items-center justify-center rounded-12 transition-all",
// Colors
"bg-black-alpha-4 text-accent-black",
"hover:bg-black-alpha-6",
"active:scale-[0.98]",
// Border
// "border-0",
// Size
sizeClasses[size],
// States
disabled && "opacity-50 cursor-not-allowed",
isLoading && "cursor-wait",
// Full width
fullWidth && "w-full",
className,
)}
disabled={disabled || isLoading}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
{...props}
>
{isLoading ? (
<div className={cn("animate-spin rounded-full", iconSizes[size])} />
) : (
<>
{Icon &&
iconPosition === "left" &&
(React.isValidElement(Icon) ? (
Icon
) : (
//@ts-ignore
<Icon
className={cn(iconSizes[size], "flex-shrink-0")}
// @ts-ignore - Some icons support isHovered and isOpen
isHovered={isHovered}
isOpen={isOpen}
/>
))}
{children}
{Icon &&
iconPosition === "right" &&
(React.isValidElement(Icon) ? (
Icon
) : (
//@ts-ignore
<Icon
className={cn(iconSizes[size], "flex-shrink-0")}
// @ts-ignore - Some icons support isHovered and isOpen
isHovered={isHovered}
isOpen={isOpen}
/>
))}
</>
)}
</button>
);
},
);
SlateButton.displayName = "SlateButton";