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 framer-motion clsx tailwind-merge mini-svg-data-uri
Add util file
lib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Modify the Tailwind config file
tailwind.config.ts
const svgToDataUri = require("mini-svg-data-uri");
const {
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{ts,tsx}"],
darkMode: "class",
theme: {
extend: {
// your config here
},
},
plugins: [
addVariablesForColors,
function ({ matchUtilities, theme }: any) {
matchUtilities(
{
"bg-dot-thick": (value: any) => ({
backgroundImage: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="2.5"></circle></svg>`
)}")`,
}),
},
{ values: flattenColorPalette(theme("backgroundColor")), type: "color" }
);
},
],
};
// This plugin adds each Tailwind color as a global CSS variable, e.g. var(--gray-200).
function addVariablesForColors({ addBase, theme }: any) {
let allColors = flattenColorPalette(theme("colors"));
let newVars = Object.fromEntries(
Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
);
addBase({
":root": newVars,
});
}
Copy the source code
components/ui/hero-highlight.tsx
"use client";
import { cn } from "@/lib/utils";
import { useMotionValue, motion, useMotionTemplate } from "framer-motion";
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);
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(
"relative h-[40rem] flex items-center bg-white dark:bg-black justify-center w-full group",
containerClassName
)}
onMouseMove={handleMouseMove}
>
<div className="absolute inset-0 bg-dot-thick-neutral-300 dark:bg-dot-thick-neutral-800 pointer-events-none" />
<motion.div
className="pointer-events-none bg-dot-thick-indigo-500 dark:bg-dot-thick-indigo-500 absolute inset-0 opacity-0 transition duration-300 group-hover:opacity-100"
style={{
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 pb-1 px-1 rounded-lg bg-gradient-to-r from-indigo-300 to-purple-300 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