Files
Astro-Component/src/components/Button.astro
T

135 lines
6.8 KiB
Plaintext

---
import type { HTMLAttributes } from "astro/types";
import { cn } from "../utils/cn";
import { Icon } from "astro-icon/components";
type Variant = "solid" | "outline" | "ghost" | "soft" | "link";
type Color =
| "primary"
| "secondary"
| "tertiary"
| "accent"
| "info"
| "success"
| "warning"
| "danger"
| "neutral";
type Size = "sm" | "md" | "lg" | "icon";
interface Props extends HTMLAttributes<"button"> {
variant?: Variant;
color?: Color;
size?: Size;
pill?: boolean;
icon?: string;
}
const {
variant = "solid",
color = "primary",
size = "md",
pill = false,
icon = "",
class: className,
...rest
}: Props = Astro.props;
const base =
"inline-flex items-center justify-center gap-2 font-medium transition-colors " +
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 " +
"disabled:opacity-50 disabled:pointer-events-none";
const sizeClass: Record<Size, string> = {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-11 px-5 text-base",
icon: "h-10 w-10 p-0",
};
const radius = pill ? "rounded-full" : "rounded-md";
const styles: Record<Color, Record<Variant, string>> = {
primary: {
solid: "bg-primary text-primary-foreground hover:bg-primary/90 border border-transparent focus-visible:ring-primary/40",
outline:
"border border-primary text-primary hover:bg-primary/10 focus-visible:ring-primary/40",
ghost: "text-primary hover:bg-primary/10 border border-transparent focus-visible:ring-primary/30",
soft: "bg-primary/15 text-primary hover:bg-primary/20 border border-transparent focus-visible:ring-primary/30",
link: "text-primary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-primary/40",
},
secondary: {
solid: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border border-transparent focus-visible:ring-secondary/40",
outline:
"border border-secondary text-secondary hover:bg-secondary/10 focus-visible:ring-secondary/40",
ghost: "text-secondary hover:bg-secondary/10 border border-transparent focus-visible:ring-secondary/30",
soft: "bg-secondary/15 text-secondary hover:bg-secondary/20 border border-transparent focus-visible:ring-secondary/30",
link: "text-secondary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-secondary/40",
},
tertiary: {
solid: "bg-tertiary text-tertiary-foreground hover:bg-tertiary/90 border border-transparent focus-visible:ring-tertiary/40",
outline:
"border border-tertiary text-tertiary hover:bg-tertiary/10 focus-visible:ring-tertiary/40",
ghost: "text-tertiary hover:bg-tertiary/10 border border-transparent focus-visible:ring-tertiary/30",
soft: "bg-tertiary/15 text-tertiary hover:bg-tertiary/20 border border-transparent focus-visible:ring-tertiary/30",
link: "text-tertiary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-tertiary/40",
},
accent: {
solid: "bg-accent text-accent-foreground hover:bg-accent/90 border border-transparent focus-visible:ring-accent/40",
outline:
"border border-accent text-accent hover:bg-accent/10 focus-visible:ring-accent/40",
ghost: "text-accent hover:bg-accent/10 border border-transparent focus-visible:ring-accent/30",
soft: "bg-accent/15 text-accent hover:bg-accent/20 border border-transparent focus-visible:ring-accent/30",
link: "text-accent underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-accent/40",
},
info: {
solid: "bg-info text-info-foreground hover:bg-info/90 border border-transparent focus-visible:ring-info/40",
outline:
"border border-info text-info hover:bg-info/10 focus-visible:ring-info/40",
ghost: "text-info hover:bg-info/10 border border-transparent focus-visible:ring-info/30",
soft: "bg-info/15 text-info hover:bg-info/20 border border-transparent focus-visible:ring-info/30",
link: "text-info underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-info/40",
},
success: {
solid: "bg-success text-success-foreground hover:bg-success/90 border border-transparent focus-visible:ring-success/40",
outline:
"border border-success text-success hover:bg-success/10 focus-visible:ring-success/40",
ghost: "text-success hover:bg-success/10 border border-transparent focus-visible:ring-success/30",
soft: "bg-success/15 text-success hover:bg-success/20 border border-transparent focus-visible:ring-success/30",
link: "text-success underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-success/40",
},
warning: {
solid: "bg-warning text-warning-foreground hover:bg-warning/90 border border-transparent focus-visible:ring-warning/40",
outline:
"border border-warning text-warning hover:bg-warning/10 focus-visible:ring-warning/40",
ghost: "text-warning hover:bg-warning/10 border border-transparent focus-visible:ring-warning/30",
soft: "bg-warning/14 text-warning hover:bg-warning/20 border border-transparent focus-visible:ring-warning/30",
link: "text-warning underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-warning/40",
},
danger: {
solid: "bg-danger text-danger-foreground hover:bg-danger/90 border border-transparent focus-visible:ring-danger/40",
outline:
"border border-danger text-danger hover:bg-danger/10 focus-visible:ring-danger/40",
ghost: "text-danger hover:bg-danger/10 border border-transparent focus-visible:ring-danger/30",
soft: "bg-danger/15 text-danger hover:bg-danger/20 border border-transparent focus-visible:ring-danger/30",
link: "text-danger underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-danger/40",
},
neutral: {
solid: "bg-neutral text-neutral-foreground hover:bg-neutral/90 border border-transparent focus-visible:ring-neutral/40",
outline:
"border border-neutral text-neutral hover:bg-neutral/10 focus-visible:ring-neutral/40",
ghost: "text-neutral hover:bg-neutral/10 border border-transparent focus-visible:ring-neutral/30",
soft: "bg-neutral/15 text-neutral hover:bg-neutral/20 border border-transparent focus-visible:ring-neutral/30",
link: "text-neutral underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-neutral/40",
},
};
const variantColor = styles[color][variant];
const sizeKls = variant === "link" ? "" : sizeClass[size];
const iconSize = size === "sm" ? "size-4" : size === "lg" ? "size-5" : "size-5";
---
<button class={cn(base, radius, sizeKls, variantColor, className)} {...rest}>
{icon && <Icon name={icon} class={cn(iconSize, "shrink-0")} />}
<slot />
</button>