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