<script module lang="ts">
import { tv, type VariantProps } from "tailwind-variants";
export const buttonCard = tv({
base: [
"flex h-40 w-48 cursor-pointer gap-sm rounded-(--btn-radius) px-md py-sm no-underline select-none [--btn-radius:var(--radius-md)]",
"hover:no-underline",
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
"disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50"
],
variants: {
variant: {
default: ["bg-background-default edge-ring", "hover:bg-background-subtle"],
ghost: ["bg-transparent", "hover:bg-background-inset"]
},
insetShadow: {
true: "",
false: ""
}
},
compoundVariants: [
{
variant: "default",
insetShadow: true,
class: [
"relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-(--btn-radius) before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:content-['']",
"transition-[translate,background-color] duration-150 ease-out-strong before:transition-shadow before:duration-150 before:ease-out-strong",
"hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)]",
"active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)]",
"disabled:active:translate-y-0 motion-reduce:active:translate-y-0"
]
}
],
defaultVariants: {
variant: "default",
insetShadow: false
}
});
export type ButtonCardVariants = VariantProps<typeof buttonCard>;
</script>
<script lang="ts">
import type { ResolvedPathname } from "$app/types";
import type { Component, Snippet } from "svelte";
import type {
HTMLAnchorAttributes,
HTMLButtonAttributes,
SvelteHTMLElements
} from "svelte/elements";
interface Props extends Omit<
HTMLAnchorAttributes & HTMLButtonAttributes,
"class" | "children"
> {
variant?: ButtonCardVariants["variant"];
insetShadow?: boolean;
href?: ResolvedPathname;
type?: "button" | "submit" | "reset";
disabled?: boolean;
title?: string;
description?: string;
icon?: Component<SvelteHTMLElements["svg"]>;
class?: string;
children?: Snippet;
}
let {
variant = "default",
insetShadow = false,
href,
type = "button",
disabled = false,
title,
description,
icon,
class: className,
children,
...rest
}: Props = $props();
const classes = $derived(buttonCard({ variant, insetShadow, class: className }));
const Icon = $derived(icon);
</script>
{#snippet content()}
<div class="flex flex-col items-start gap-1.5">
{#if title}
<div class="flex flex-row items-center gap-xs">
{#if Icon}
<Icon class="size-3.5 shrink-0" aria-hidden="true" />
{/if}
<span class="typography-paragraph-small font-medium text-foreground-default"
>{title}</span
>
</div>
{/if}
{#if description}
<span class="typography-paragraph-small text-foreground-muted">{description}</span>
{/if}
</div>
{/snippet}
{#if href}
<a
{href}
class={classes}
aria-disabled={disabled ? "true" : undefined}
tabindex={disabled ? -1 : undefined}
{...rest}
>
{@render (children ?? content)()}
</a>
{:else}
<button {type} {disabled} class={classes} {...rest}>
{@render (children ?? content)()}
</button>
{/if}