Hover Border Gradient
A hover effect that expands to the entire container with a gradient border.
Installation
Install dependencies
npm i motion clsx tailwind-merge
Add util file
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy the source code
components/ui/hover-border-gradient.tsx
"use client";
import React, { useState, useEffect, useRef } from "react";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";
type Direction = "TOP" | "LEFT" | "BOTTOM" | "RIGHT";
export function HoverBorderGradient({
children,
containerClassName,
className,
as: Tag = "button",
duration = 1,
clockwise = true,
...props
}: React.PropsWithChildren<
{
as?: React.ElementType;
containerClassName?: string;
className?: string;
duration?: number;
clockwise?: boolean;
} & React.HTMLAttributes<HTMLElement>
>) {
const [hovered, setHovered] = useState<boolean>(false);
const [direction, setDirection] = useState<Direction>("TOP");
const rotateDirection = (currentDirection: Direction): Direction => {
const directions: Direction[] = ["TOP", "LEFT", "BOTTOM", "RIGHT"];
const currentIndex = directions.indexOf(currentDirection);
const nextIndex = clockwise
? (currentIndex - 1 + directions.length) % directions.length
: (currentIndex + 1) % directions.length;
return directions[nextIndex];
};
const movingMap: Record<Direction, string> = {
TOP: "radial-gradient(20.7% 50% at 50% 0%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
LEFT: "radial-gradient(16.6% 43.1% at 0% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
BOTTOM:
"radial-gradient(20.7% 50% at 50% 100%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
RIGHT:
"radial-gradient(16.2% 41.199999999999996% at 100% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
};
const highlight =
"radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)";
useEffect(() => {
if (!hovered) {
const interval = setInterval(() => {
setDirection((prevState) => rotateDirection(prevState));
}, duration * 1000);
return () => clearInterval(interval);
}
}, [hovered]);
return (
<Tag
onMouseEnter={(event: React.MouseEvent<HTMLDivElement>) => {
setHovered(true);
}}
onMouseLeave={() => setHovered(false)}
className={cn(
"relative flex rounded-full border content-center bg-black/20 hover:bg-black/10 transition duration-500 dark:bg-white/20 items-center flex-col flex-nowrap gap-10 h-min justify-center overflow-visible p-px decoration-clone w-fit",
containerClassName
)}
{...props}
>
<div
className={cn(
"w-auto text-white z-10 bg-black px-4 py-2 rounded-[inherit]",
className
)}
>
{children}
</div>
<motion.div
className={cn(
"flex-none inset-0 overflow-hidden absolute z-0 rounded-[inherit]"
)}
style={{
filter: "blur(2px)",
position: "absolute",
width: "100%",
height: "100%",
}}
initial={{ background: movingMap[direction] }}
animate={{
background: hovered
? [movingMap[direction], highlight]
: movingMap[direction],
}}
transition={{ ease: "linear", duration: duration ?? 1 }}
/>
<div className="bg-black absolute z-1 flex-none inset-[2px] rounded-[100px]" />
</Tag>
);
}
Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | N/A | The content to be displayed inside the hover gradient component. |
containerClassName | string? | undefined | Additional CSS class for the container. |
className | string? | undefined | Additional CSS class for the inner content. |
as | React.ElementType? | "button" | The component type that will be used as the container. |
duration | number? | 1 | Duration of the animation cycle in seconds. |
clockwise | boolean? | true | Determines the direction of the gradient rotation. |
...buttonProps | React.HTMLAttributes<HTMLElement> | N/A | Additional props to be spread over the container element. |
Build websites faster and 10x better than your competitors with Aceternity UI Pro
With the best in class components and templates, stand out from the crowd and get more attention to your website. Trusted by founders and entrepreneurs from all over the world.
Manu was such a pleasure to work with. Talented, communicative and fast. Highly recommend!
Jonathan Barshop
Founder at Barshop Studios