This repository has no description
1<script module lang="ts">
2 import { tv, type VariantProps } from "tailwind-variants";
3
4 export const buttonCard = tv({
5 base: [
6 "flex h-40 w-48 cursor-pointer gap-3 rounded-(--btn-radius) [--btn-radius:var(--radius-sm)] px-4 py-3 select-none no-underline",
7 "transition-colors duration-150 ease-in-out",
8 "hover:no-underline",
9 "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
10 "disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50"
11 ],
12 variants: {
13 variant: {
14 default: [
15 "border border-border-default bg-background-default",
16 "hover:bg-background-subtle"
17 ],
18 ghost: [
19 "border border-transparent bg-transparent",
20 "hover:bg-background-inset"
21 ]
22 },
23 insetShadow: {
24 true: "",
25 false: ""
26 }
27 },
28 compoundVariants: [
29 // raised variants draw inset depth via a ::before layer.
30 {
31 variant: "default",
32 insetShadow: true,
33 class: [
34 "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[max(calc(var(--btn-radius)-2px),0px)] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150",
35 "hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)]",
36 "active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)]",
37 "disabled:active:translate-y-0"
38 ]
39 }
40 ],
41 defaultVariants: {
42 variant: "default",
43 insetShadow: false
44 }
45 });
46
47 export type ButtonCardVariants = VariantProps<typeof buttonCard>;
48</script>
49
50<script lang="ts">
51 import type { ResolvedPathname } from "$app/types";
52 import type { Component, Snippet } from "svelte";
53 import type {
54 HTMLAnchorAttributes,
55 HTMLButtonAttributes,
56 SvelteHTMLElements
57 } from "svelte/elements";
58
59 interface Props extends Omit<HTMLAnchorAttributes & HTMLButtonAttributes, "class" | "children"> {
60 variant?: ButtonCardVariants["variant"];
61 insetShadow?: boolean;
62 href?: ResolvedPathname;
63 type?: "button" | "submit" | "reset";
64 disabled?: boolean;
65 title?: string;
66 description?: string;
67 icon?: Component<SvelteHTMLElements["svg"]>;
68 class?: string;
69 children?: Snippet;
70 }
71
72 let {
73 variant = "default",
74 insetShadow = false,
75 href,
76 type = "button",
77 disabled = false,
78 title,
79 description,
80 icon,
81 class: className,
82 children,
83 ...rest
84 }: Props = $props();
85
86 const classes = $derived(buttonCard({ variant, insetShadow, class: className }));
87 const Icon = $derived(icon);
88</script>
89
90{#snippet content()}
91 <div class="flex flex-col items-start gap-1.5">
92 {#if title}
93 <div class="flex flex-row gap-2 items-center">
94 {#if Icon}
95 <Icon class="size-4 shrink-0" aria-hidden="true" />
96 {/if}
97 <span class="text-sm font-medium text-foreground-default">{title}</span>
98 </div>
99 {/if}
100 {#if description}
101 <span class="text-xs text-foreground-muted">{description}</span>
102 {/if}
103 </div>
104{/snippet}
105
106{#if href}
107 <a
108 {href}
109 class={classes}
110 aria-disabled={disabled ? "true" : undefined}
111 tabindex={disabled ? -1 : undefined}
112 {...rest}
113 >
114 {@render (children ?? content)()}
115 </a>
116{:else}
117 <button {type} {disabled} class={classes} {...rest}>
118 {@render (children ?? content)()}
119 </button>
120{/if}