{
  "$schema": "https://shadcn-svelte.com/schema/registry-item.json",
  "name": "animated-button",
  "type": "registry:ui",
  "title": "AnimatedButton",
  "description": "Animated shadcn Button with spring-based press and hover via svelte-motion. Installs alongside the standard Button.",
  "dependencies": [
    "@humanspeak/svelte-motion",
    "tailwind-variants"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "content": "<!--\n  @component\n  Animated shadcn Button component powered by svelte-motion.\n\n  Drop-in replacement for the standard shadcn button with spring-based\n  press feedback (whileTap) and optional hover lift. Supports all standard\n  button variants and sizes, plus link mode via href.\n\n  Set `animated={false}` to disable motion and get vanilla shadcn behavior.\n-->\n<script lang=\"ts\" module>\n    import { cn, type WithElementRef } from '$UTILS$'\n    import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements'\n    import { type VariantProps, tv } from 'tailwind-variants'\n\n    export const buttonVariants = tv({\n        base: \"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        variants: {\n            variant: {\n                default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs',\n                destructive:\n                    'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs',\n                outline:\n                    'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs',\n                secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs',\n                ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',\n                link: 'text-primary underline-offset-4 hover:underline'\n            },\n            size: {\n                default: 'h-9 px-4 py-2 has-[>svg]:px-3',\n                sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',\n                lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',\n                icon: 'size-9',\n                'icon-sm': 'size-8',\n                'icon-lg': 'size-10'\n            }\n        },\n        defaultVariants: {\n            variant: 'default',\n            size: 'default'\n        }\n    })\n\n    export type ButtonVariant = VariantProps<typeof buttonVariants>['variant']\n    export type ButtonSize = VariantProps<typeof buttonVariants>['size']\n    export type ButtonProps = WithElementRef<HTMLButtonAttributes> &\n        WithElementRef<HTMLAnchorAttributes> & {\n            variant?: ButtonVariant\n            size?: ButtonSize\n            /** Set to false to disable motion animations (vanilla shadcn behavior). */\n            animated?: boolean\n        }\n</script>\n\n<script lang=\"ts\">\n    import { MotionA, MotionButton } from '@humanspeak/svelte-motion'\n\n    let {\n        class: className,\n        variant = 'default',\n        size = 'default',\n        ref = $bindable(null),\n        href = undefined,\n        type = 'button',\n        disabled,\n        animated = true,\n        style: styleProp,\n        children,\n        ...restProps\n    }: ButtonProps = $props()\n\n    // Coerce style from string|null|undefined to string|undefined for motion compatibility\n    const motionStyle = $derived(styleProp ?? undefined)\n\n    // Spring transition for snappy, natural rebound\n    const springTransition = { type: 'spring' as const, stiffness: 400, damping: 25 }\n\n    // Per-variant tap scale\n    const getTapScale = () => {\n        if (!animated) return undefined\n        if (size === 'icon' || size === 'icon-sm' || size === 'icon-lg') {\n            return { scale: 0.9 }\n        }\n        return { scale: 0.97 }\n    }\n\n    // Per-variant hover effect\n    const getHoverEffect = () => {\n        if (!animated) return undefined\n        if (size === 'icon' || size === 'icon-sm' || size === 'icon-lg') {\n            return { scale: 1.08 }\n        }\n        if (variant === 'link') {\n            return { scale: 1.03 }\n        }\n        return { y: -1 }\n    }\n</script>\n\n{#if href}\n    {#if animated}\n        <MotionA\n            bind:ref\n            data-slot=\"button\"\n            class={cn(buttonVariants({ variant, size }), className)}\n            style={motionStyle}\n            href={disabled ? undefined : href}\n            aria-disabled={disabled}\n            role={disabled ? 'link' : undefined}\n            tabindex={disabled ? -1 : undefined}\n            whileTap={getTapScale()}\n            whileHover={getHoverEffect()}\n            transition={springTransition}\n            {...restProps}\n        >\n            {@render children?.()}\n        </MotionA>\n    {:else}\n        <a\n            bind:this={ref}\n            data-slot=\"button\"\n            class={cn(buttonVariants({ variant, size }), className)}\n            style={styleProp}\n            href={disabled ? undefined : href}\n            aria-disabled={disabled}\n            role={disabled ? 'link' : undefined}\n            tabindex={disabled ? -1 : undefined}\n            {...restProps}\n        >\n            {@render children?.()}\n        </a>\n    {/if}\n{:else if animated}\n    <MotionButton\n        bind:ref\n        data-slot=\"button\"\n        class={cn(buttonVariants({ variant, size }), className)}\n        style={motionStyle}\n        {type}\n        {disabled}\n        whileTap={getTapScale()}\n        whileHover={getHoverEffect()}\n        transition={springTransition}\n        {...restProps}\n    >\n        {@render children?.()}\n    </MotionButton>\n{:else}\n    <button\n        bind:this={ref}\n        data-slot=\"button\"\n        class={cn(buttonVariants({ variant, size }), className)}\n        style={styleProp}\n        {type}\n        {disabled}\n        {...restProps}\n    >\n        {@render children?.()}\n    </button>\n{/if}\n",
      "type": "registry:file",
      "target": "animated-button/animated-button.svelte"
    },
    {
      "content": "import Root, {\n    type ButtonProps,\n    type ButtonSize,\n    type ButtonVariant,\n    buttonVariants\n} from './animated-button.svelte'\n\nexport {\n    //\n    Root as AnimatedButton,\n    buttonVariants,\n    Root,\n    type ButtonProps,\n    type ButtonSize,\n    type ButtonVariant,\n    type ButtonProps as Props\n}\n",
      "type": "registry:file",
      "target": "animated-button/index.ts"
    }
  ],
  "categories": [
    "buttons"
  ],
  "author": "Humanspeak, Inc.",
  "docs": "Requires @humanspeak/svelte-motion. Set animated={false} to disable motion."
}
