Something new is coming.Join the waitlist

Gradient Border Button

PreviousNext

An animated button/link with a cosmic gradient border effect.

Installation

npx shadcn@latest add "https://ui.srb.codes/r/gradient-border-button"

Copy and paste the following code into your project.

"use client";

import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import type * as React from "react";

import { cn } from "@/lib/utils";

const gradientBorderButtonVariants = cva(
  "group/gradient-border relative inline-flex shrink-0 items-stretch justify-center overflow-visible rounded-xl p-0.5 outline-none transition-transform focus-within:ring-[3px] focus-within:ring-ring/50 has-disabled:pointer-events-none has-disabled:opacity-50 active:scale-[0.98]",
  {
    variants: {
      size: {
        default: "",
        sm: "",
        lg: ""
      }
    },
    defaultVariants: {
      size: "default"
    }
  }
);

const gradientBorderButtonInnerVariants = cva(
  "relative z-10 inline-flex items-center justify-center gap-2 rounded-[calc(var(--radius-xl)-2px)] bg-muted font-medium text-foreground shadow-xs outline-none transition-shadow group-hover/gradient-border:shadow-md focus-visible:outline-none",
  {
    variants: {
      size: {
        default: "h-9 px-4 text-sm",
        sm: "h-8 px-3 text-xs",
        lg: "h-12 px-8 text-base"
      }
    },
    defaultVariants: {
      size: "default"
    }
  }
);

const borderTrackClassName =
  "pointer-events-none absolute inset-0 overflow-hidden rounded-xl transition-[inset] duration-300 ease-out group-hover/gradient-border:-inset-1";

function GradientBorderButton({
  className,
  size,
  asChild = false,
  children,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof gradientBorderButtonVariants> & {
    asChild?: boolean;
  }) {
  const InnerComp = asChild ? Slot : "button";

  return (
    <span
      data-slot="gradient-border-button"
      className={cn(gradientBorderButtonVariants({ size, className }))}
    >
      <span aria-hidden className={borderTrackClassName}>
        <span className="absolute inset-[-200%] animate-[spin_3s_linear_infinite] bg-[conic-gradient(from_0deg,var(--color-blue-500),var(--color-blue-600),var(--color-blue-400),var(--color-blue-600),var(--color-blue-500))] opacity-90" />
      </span>
      <span
        aria-hidden
        className={cn(
          borderTrackClassName,
          "opacity-40 mix-blend-soft-light dark:opacity-50 dark:mix-blend-overlay"
        )}
      >
        <span className="absolute inset-[-200%] animate-[spin_5s_linear_infinite_reverse] bg-[conic-gradient(from_180deg,var(--color-blue-500)_0%,transparent_35%,var(--color-blue-600)_50%,transparent_65%,var(--color-blue-500)_100%)]" />
      </span>
      <InnerComp
        className={cn(gradientBorderButtonInnerVariants({ size }))}
        {...props}
      >
        {children}
      </InnerComp>
    </span>
  );
}

export { GradientBorderButton, gradientBorderButtonVariants };

Update the import paths to match your project setup.