Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167<script module lang="ts"> import { tv, type VariantProps } from "tailwind-variants";
export const button = tv({ base: [ "inline-flex cursor-pointer items-center justify-center overflow-hidden rounded-l-(--btn-radius-l) rounded-r-(--btn-radius-r) no-underline select-none [--btn-radius-l:var(--btn-radius)] [--btn-radius-r:var(--btn-radius)] [--btn-radius:var(--radius-sm)]", "transition-colors duration-150 ease-in-out", "hover:no-underline", "focus-visible:outline-2 focus-visible:outline-offset-2", "disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50" ], variants: { variant: { default: [ "border border-border-default bg-background-default text-foreground-default", "hover:bg-background-subtle", "focus-visible:outline-border-focus" ], primary: [ "border border-border-success bg-background-success-emphasis text-foreground-on-emphasis", "hover:bg-background-success-emphasis-hover", "focus-visible:outline-background-success-emphasis" ], ghost: [ "border border-transparent bg-transparent text-foreground-muted", "hover:bg-background-inset", "focus-visible:outline-border-focus" ], // danger / success / warning are outline buttons in Figma: default surface + // border, colored text only (bound to the fg-* tokens), no accent fill. danger: [ "border border-border-default bg-background-default text-foreground-danger", "hover:bg-background-subtle", "focus-visible:outline-foreground-danger" ], success: [ "border border-border-default bg-background-default text-foreground-success", "hover:bg-background-subtle", "focus-visible:outline-foreground-success" ], warning: [ "border border-border-default bg-background-default text-foreground-warning", "hover:bg-background-subtle", "focus-visible:outline-foreground-warning" ] }, size: { sm: "min-h-8 gap-1.5 px-2 py-1.5 text-sm", md: "min-h-9 gap-2 px-3 py-1.5 text-sm", lg: "min-h-10 gap-2.5 px-4 py-2 text-base" }, insetShadow: { true: "", false: "" } }, compoundVariants: [ // raised variants draw inset depth via a ::before layer. { variant: "default", insetShadow: true, class: [ "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150 before:content-['']", "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" ] }, { variant: "primary", insetShadow: true, class: [ "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-l-[max(calc(var(--btn-radius-l)-2px),0px)] before:rounded-r-[max(calc(var(--btn-radius-r)-2px),0px)] before:shadow-[inset_0_-2px_0_0_var(--color-alpha-dark-25)] before:transition-all before:duration-150 before:content-['']", "active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-alpha-dark-50)]", "disabled:active:translate-y-0" ] } ], defaultVariants: { variant: "default", size: "md", insetShadow: false } });
export type ButtonVariants = VariantProps<typeof button>;</script>
<script lang="ts"> import type { ResolvedPathname } from "$app/types"; import type { Component, Snippet } from "svelte"; import type { HTMLAnchorAttributes, HTMLButtonAttributes, SvelteHTMLElements } from "svelte/elements"; import Spinner from "./Spinner.svelte";
interface Props extends Omit<HTMLAnchorAttributes & HTMLButtonAttributes, "class" | "children"> { variant?: ButtonVariants["variant"]; size?: ButtonVariants["size"]; insetShadow?: boolean; href?: ResolvedPathname; type?: "button" | "submit" | "reset"; disabled?: boolean; icon?: Component<SvelteHTMLElements["svg"]>; iconSide?: string; class?: string; loading?: boolean; spinnerClass?: string; children?: Snippet; }
let { variant = "default", size = "md", insetShadow = false, href, type = "button", disabled = false, icon, iconSide = "left", class: className, loading = false, spinnerClass, children, ...rest }: Props = $props();
const classes = $derived(button({ variant, size, insetShadow, class: className })); const spinnerSize = $derived(spinnerClass ?? (size === "lg" ? "size-5" : "size-4")); const iconSize = $derived(size === "lg" ? "size-5" : "size-4"); const Icon = $derived(icon);</script>
{#snippet content()} {#if loading} <Spinner class={spinnerSize} /> {:else} {#if Icon && iconSide == "left"} <Icon class="{iconSize} shrink-0" aria-hidden="true" /> {/if} {#if children} {@render children()} {/if} {#if Icon && iconSide == "right"} <Icon class="{iconSize} shrink-0" aria-hidden="true" /> {/if} {/if}{/snippet}
{#if href} <a {href} class={classes} aria-disabled={disabled || loading ? "true" : undefined} tabindex={disabled ? -1 : undefined} {...rest} > {@render content()} </a>{:else} <button {type} disabled={disabled || loading} class={classes} aria-busy={loading} {...rest}> {@render content()} </button>{/if}