Hero Highlight
A background effect with a text highlight component, perfect for hero sections.
With insomnia, nothing's real. Everything is far away. Everything is a copy, of a copy, of a copy.
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/hero-highlight.tsx
"use client";
import { cn } from "@/lib/utils";
import { useMotionValue, motion, useMotionTemplate } from "motion/react";
import React from "react";
export const HeroHighlight = ({
children,
className,
containerClassName,
}: {
children: React.ReactNode;
className?: string;
containerClassName?: string;
}) => {
let mouseX = useMotionValue(0);
let mouseY = useMotionValue(0);
// SVG patterns for different states and themes
const dotPatterns = {
light: {
default: `url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='16' height='16' fill='none'%3E%3Ccircle fill='%23d4d4d4' id='pattern-circle' cx='10' cy='10' r='2.5'%3E%3C/circle%3E%3C/svg%3E")`,
hover: `url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='16' height='16' fill='none'%3E%3Ccircle fill='%236366f1' id='pattern-circle' cx='10' cy='10' r='2.5'%3E%3C/circle%3E%3C/svg%3E")`,
},
dark: {
default: `url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='16' height='16' fill='none'%3E%3Ccircle fill='%23404040' id='pattern-circle' cx='10' cy='10' r='2.5'%3E%3C/circle%3E%3C/svg%3E")`,
hover: `url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='16' height='16' fill='none'%3E%3Ccircle fill='%238183f4' id='pattern-circle' cx='10' cy='10' r='2.5'%3E%3C/circle%3E%3C/svg%3E")`,
},
};
function handleMouseMove({
currentTarget,
clientX,
clientY,
}: React.MouseEvent<HTMLDivElement>) {
if (!currentTarget) return;
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
return (
<div
className={cn(
"group relative flex h-[40rem] w-full items-center justify-center bg-white dark:bg-black",
containerClassName,
)}
onMouseMove={handleMouseMove}
>
<div
className="pointer-events-none absolute inset-0 dark:hidden"
style={{
backgroundImage: dotPatterns.light.default,
}}
/>
<div
className="pointer-events-none absolute inset-0 hidden dark:block"
style={{
backgroundImage: dotPatterns.dark.default,
}}
/>
<motion.div
className="pointer-events-none absolute inset-0 opacity-0 transition duration-300 group-hover:opacity-100 dark:hidden"
style={{
backgroundImage: dotPatterns.light.hover,
WebkitMaskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
maskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
}}
/>
<motion.div
className="pointer-events-none absolute inset-0 hidden opacity-0 transition duration-300 group-hover:opacity-100 dark:block"
style={{
backgroundImage: dotPatterns.dark.hover,
WebkitMaskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
maskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
}}
/>
<div className={cn("relative z-20", className)}>{children}</div>
</div>
);
};
export const Highlight = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<motion.span
initial={{
backgroundSize: "0% 100%",
}}
animate={{
backgroundSize: "100% 100%",
}}
transition={{
duration: 2,
ease: "linear",
delay: 0.5,
}}
style={{
backgroundRepeat: "no-repeat",
backgroundPosition: "left center",
display: "inline",
}}
className={cn(
`relative inline-block rounded-lg bg-gradient-to-r from-indigo-300 to-purple-300 px-1 pb-1 dark:from-indigo-500 dark:to-purple-500`,
className,
)}
>
{children}
</motion.span>
);
};
Props
HeroHighlight
Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | N/A | The content to be displayed inside the HeroHighlight component. |
className | string (optional) | N/A | Additional CSS classes to apply to the child component for styling. |
containerClassName | string (optional) | N/A | Additional CSS classes to apply to the container component for styling. |
Highlight
Props
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | N/A | The content to be highlighted. |
className | string (optional) | N/A | Additional CSS classes to apply to the Highlight component for styling. |
Inspiration for this component is taken from Designboost's Homepage
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.
Excellent communication and professionalism from the start and
throughout. Happily and calmly accepted and entertained a few additional
out-of-scope requests as well. Good open-...
Henrik Söderlund
Former CTO at Creme Digital