{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "metal-button", "type": "registry:ui", "description": "Shadcn Button wrapped in MetalFx: animated liquid metal ring with text and icon variants", "dependencies": [ "metal-fx" ], "registryDependencies": [ "button" ], "files": [ { "path": "registry/default/ui/metal-button.tsx", "content": "\"use client\"\n\n/**\n * `metal-fx` around `Button` — liquid metal ring for controls.\n * `className` styles the button; `metalFxClassName` styles the MetalFx wrapper.\n *\n * With `normalizeHostStyles` (default), variant fills live on the MetalFx wrapper\n * and the button stays transparent so the shader ring stays visible. Pass\n * `normalizeHostStyles={false}` to keep all shadcn chrome on the button (filled\n * variants will cover most of the metal).\n */\nimport type { ComponentProps, CSSProperties } from \"react\"\nimport { forwardRef } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { MetalFx, type MetalFxProps, type MetalFxVariant } from \"metal-fx\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\n\nconst metalSurfaceVariants = cva(\"transition-colors\", {\n variants: {\n variant: {\n default: \"bg-primary! text-primary-foreground! hover:bg-primary/80!\",\n outline:\n \"bg-background! text-foreground! hover:bg-input/50! dark:bg-input/30!\",\n secondary:\n \"bg-secondary! text-secondary-foreground! hover:bg-secondary/80!\",\n ghost:\n \"bg-transparent! text-foreground! hover:bg-muted/50! dark:hover:bg-muted/50!\",\n destructive:\n \"bg-destructive/10! text-destructive! hover:bg-destructive/20! dark:bg-destructive/20! dark:hover:bg-destructive/30!\",\n link: \"bg-transparent! text-primary!\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n})\n\n/** Strip outer chrome on the host; MetalFx punches the ring around the interior. */\nconst metalHostChromeReset =\n \"border-0! bg-transparent! shadow-none! hover:bg-transparent! aria-expanded:bg-transparent!\"\n\n/** Keep a stable edge above the animated shader so bright frames cannot erase it. */\nconst metalStableEdge =\n \"relative isolate before:pointer-events-none before:absolute before:inset-0 before:z-10 before:rounded-[inherit] before:ring-1 before:ring-border/70 before:ring-inset dark:before:ring-border/80\"\n\ntype MetalSurfaceVariant = NonNullable<\n VariantProps[\"variant\"]\n>\n\ntype MetalShellProps = Pick<\n MetalFxProps,\n | \"preset\"\n | \"theme\"\n | \"strength\"\n | \"paused\"\n | \"borderRadius\"\n | \"disableGlow\"\n | \"reflectionTargets\"\n | \"shaderScale\"\n | \"ringCssPx\"\n | \"scale\"\n | \"normalizeHostStyles\"\n> & {\n metalVariant?: MetalFxVariant\n metalFxClassName?: string\n metalFxStyle?: CSSProperties\n}\n\nexport type MetalButtonProps = ComponentProps & MetalShellProps\n\nexport type MetalIconButtonProps = MetalButtonProps\n\nexport const MetalButton = forwardRef(\n function MetalButton(\n {\n metalVariant = \"button\",\n metalFxClassName,\n metalFxStyle,\n preset = \"chromatic\",\n theme = \"auto\",\n strength = 0.9,\n paused,\n borderRadius,\n disableGlow,\n reflectionTargets,\n shaderScale,\n ringCssPx,\n scale,\n normalizeHostStyles = true,\n variant = \"default\",\n className,\n ...buttonProps\n },\n ref\n ) {\n const surfaceVariant = variant as MetalSurfaceVariant\n\n return (\n \n \n \n )\n }\n)\n\nMetalButton.displayName = \"MetalButton\"\n\nexport const MetalIconButton = forwardRef(\n function MetalIconButton(\n { size = \"icon-sm\", metalVariant = \"circle\", className, ...props },\n ref\n ) {\n return (\n \n )\n }\n)\n\nMetalIconButton.displayName = \"MetalIconButton\"\n", "type": "registry:ui" } ] }