Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105<script module lang="ts"> import { tv, type VariantProps } from 'tailwind-variants';
export const button = tv({ base: 'inline-flex cursor-pointer select-none items-center justify-center overflow-hidden rounded no-underline transition-[background-color,border-color,color] duration-150 ease-[cubic-bezier(0.4,0,0.2,1)] hover:no-underline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50', variants: { variant: { default: 'border border-border bg-surface text-fg hover:bg-surface-hover focus-visible:outline-ring', primary: 'border border-primary-border bg-primary text-primary-fg hover:bg-primary-hover focus-visible:outline-primary', ghost: 'bg-transparent text-fg-muted hover:bg-surface-muted focus-visible:outline-ring', flat: 'bg-gray-50 text-fg hover:bg-gray-100 focus-visible:outline-ring dark:bg-gray-900 dark:hover:bg-gray-700', danger: 'border border-danger-border bg-danger text-danger-fg hover:bg-danger-hover focus-visible:outline-danger', success: 'border border-success-border bg-success text-success-fg hover:bg-success-hover focus-visible:outline-success' }, size: { sm: 'min-h-8 gap-1.5 px-2 py-1.5 text-sm', md: 'min-h-9 gap-2 px-4 py-2 text-sm', lg: 'min-h-10 gap-2.5 px-6 py-2 text-base' } }, compoundVariants: [ // raised variants draw inset depth via a ::before layer. { variant: 'default', class: "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-raised-depth)] before:transition-all before:duration-150 hover:before:shadow-[inset_0_-2px_0_0_var(--color-raised-depth-hover)] active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-raised-depth)] disabled:active:translate-y-0" }, { variant: ['primary', 'danger', 'success'], class: "relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-raised-accent-depth)] before:transition-all before:duration-150 active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-raised-accent-depth-active)] disabled:active:translate-y-0" } ], defaultVariants: { variant: 'default', size: 'md' } });
export type ButtonVariants = VariantProps<typeof button>;</script>
<script lang="ts"> import { resolve } from '$app/paths'; import type { Snippet } from 'svelte'; import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements'; import Spinner from './Spinner.svelte';
interface Props extends Omit<HTMLAnchorAttributes & HTMLButtonAttributes, 'class' | 'children'> { variant?: ButtonVariants['variant']; size?: ButtonVariants['size']; href?: string; type?: 'button' | 'submit' | 'reset'; disabled?: boolean; class?: string; loading?: boolean; spinnerClass?: string; children?: Snippet; }
let { variant = 'default', size = 'md', href, type = 'button', disabled = false, class: className, loading = false, spinnerClass, children, ...rest }: Props = $props();
const classes = $derived(button({ variant, size, class: className })); const spinnerSize = $derived(spinnerClass ?? (size === 'lg' ? 'size-5' : 'size-4'));</script>
{#if href} <a href={resolve(href as '/')} class={classes} aria-disabled={disabled || loading ? 'true' : undefined} tabindex={disabled ? -1 : undefined} {...rest} > {#if loading} <Spinner class={spinnerSize} /> {:else if children} {@render children()} {/if} </a>{:else} <button {type} disabled={disabled || loading} class={classes} aria-busy={loading} {...rest}> {#if loading} <Spinner class={spinnerSize} /> {:else if children} {@render children()} {/if} </button>{/if}