Spotlight Card
A container for interactive cards with a hover spotlight effect, ideal for showcasing features or highlights.
spotlight-card-preview.jsx
import SpotlightCard, { SpotlightCardsContainer } from "@/components/interactions/spotlight-card/spotlight-card";
const SpotlightCardPreview = () => {
return (
<SpotlightCardsContainer
style={spotlightCardsContainerStyles}
>
{cards.map((card) => (
<SpotlightCard
spotlightColor="rgb(132, 250, 175)"
style={spotlightCardStyles}
wrapperProps={{
style: spotlightCardWrapperStyles,
}}
>
<span style={cardIcon}>{card.icon}</span>
<h2 style={cardHeading}>{card.title}</h2>
<p>{card.description}</p>
</SpotlightCard>
))}
</SpotlightCardsContainer>
);
};
const spotlightCardsContainerStyles = {
margin: "32px",
display: "flex",
gap: "16px",
flexWrap: "wrap",
justifyContent: "center",
};
const spotlightCardStyles = {
width: "320px",
borderRadius: "8px",
};
const spotlightCardWrapperStyles = {
background: "var(--layer-secondary, #111111)",
padding: "16px",
};
const cardHeading = {
fontSize: "1.25em",
};
const cardIcon = {
display: "inline-block",
marginBottom: "16px",
};
const cards = [
{
icon: "⚡",
title: "Fast Performance",
description: "Experience lightning-fast load times and smooth interactions across all devices.",
},
{
icon: "🔒",
title: "Secure by Default",
description: "Built with modern security practices to keep your data safe and protected.",
},
{
icon: "🎨",
title: "Customizable UI",
description: "Easily adapt components to match your brand and design system.",
},
{
icon: "📱",
title: "Responsive Design",
description: "Optimized layouts that look great on mobile, tablet, and desktop screens.",
},
{
icon: "🧑💻",
title: "Developer Friendly",
description: "Simple APIs and flexible props make development fast and enjoyable.",
},
{
icon: "📈",
title: "Scalable Architecture",
description: "Designed to grow with your application from small projects to large systems.",
},
];
export default SpotlightCardPreview; Installation
Run the command from your project root directory (the folder that contains package.json).
Terminal / Console
npx mosaicui-cli@latest interactions/spotlight-card 1. Copy the component file
Create a new file called spotlight-card.jsx
in your reusable components folder (for example
/src/components/) and paste the following
code into it.
spotlight-card.jsx
import { memo, useMemo } from "react";
import styles from "./spotlight-card.module.css";
export const SpotlightCardsContainer = memo((props) => {
const {
children,
className,
tagName = "div",
...restProps
} = props;
const Component = tagName || "div";
const handleMouseMove = (e) => {
const { clientX, clientY } = e;
const cards = e.currentTarget.querySelectorAll(`.${styles["spotlight-card"]}`);
cards.forEach((card) => {
const { x, y } = card.getBoundingClientRect();
card.style.setProperty("--mx", `${clientX - x}px`);
card.style.setProperty("--my", `${clientY - y}px`);
});
};
return (
<Component
{...restProps}
className={[
styles["spotlight-card-container"],
className,
].join(" ")}
onMouseMove={handleMouseMove}
>
{children}
</Component>
);
});
const SpotlightCard = (props) => {
const {
children,
spotlightColor = "rgb(127, 127, 127)",
spotlightSize = 100,
spotlightBorderWidth = 1,
tagName = "div",
className,
style,
wrapperTagName = "div",
wrapperProps = {},
...restProps
} = props;
const Component = tagName || "div";
const Wrapper = wrapperTagName || "div";
const {
className: wrapperClassName = "",
...restWrapperProps
} = wrapperProps;
const _spotlightBorderWidth = useMemo(() => (
Math.max(0, spotlightBorderWidth)
), [spotlightBorderWidth]);
const _spotlightColor = useMemo(() => {
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 1;
const ctx = canvas.getContext("2d");
ctx.fillStyle = spotlightColor;
ctx.fillRect(0, 0, 1, 1);
const [r, g, b] = Array.from(ctx.getImageData(0, 0, 1, 1).data);
canvas.remove();
return (alpha) => {
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
};
}, [spotlightColor]);
return (
<Component
{...restProps}
className={[
styles["spotlight-card"],
className,
].join(" ")}
style={{
...style,
"--spotlight-size": `${spotlightSize}px`,
"--spotlight-before-color": _spotlightColor(0.8),
"--spotlight-after-color": _spotlightColor(0.2),
"--spotlight-border-width": `${_spotlightBorderWidth}px`,
}}
>
<Wrapper
{...restWrapperProps}
className={[
styles["wrapper"],
wrapperClassName,
].join(" ")}
>
{children}
</Wrapper>
</Component>
);
};
export default memo(SpotlightCard); 2. Copy the CSS module file
In the same folder, create a file called spotlight-card.module.css and paste the following
CSS.
spotlight-card.module.css
.spotlight-card-container {
.spotlight-card {
position: relative;
.wrapper {
position: relative;
top: var(--spotlight-border-width) !important;
left: var(--spotlight-border-width) !important;
width: calc(100% - calc(var(--spotlight-border-width) * 2)) !important;
height: calc(100% - calc(var(--spotlight-border-width) * 2)) !important;
border-radius: inherit;
z-index: 2;
}
&:before,
&:after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
border-radius: inherit;
transition: all 250ms ease-in-out;
}
&:before {
background: radial-gradient(
var(--spotlight-size) circle at var(--mx) var(--my),
var(--spotlight-before-color),
rgba(0, 0, 0, 0)
);
z-index: 1;
}
&:after {
background: radial-gradient(
var(--spotlight-size) circle at var(--mx) var(--my),
var(--spotlight-after-color),
rgba(0, 0, 0, 0)
);
z-index: 3;
}
&:hover:after {
opacity: 1;
}
}
&:hover {
.spotlight-card:before {
opacity: 1;
}
}
} Examples
Discover how supported props transform components to match your needs.
Spotlight Size
spotlight-card
import SpotlightCard, { SpotlightCardsContainer } from "@/components/interactions/spotlight-card/spotlight-card";
const SpotlightCardSpotlightSizePreview = () => {
return (
<SpotlightCardsContainer
style={spotlightCardsContainerStyles}
>
{cards.map((card) => (
<SpotlightCard
spotlightColor="rgb(132, 250, 175)"
spotlightSize={200}
style={spotlightCardStyles}
wrapperProps={{
style: spotlightCardWrapperStyles,
}}
>
<span style={cardIcon}>{card.icon}</span>
<h2 style={cardHeading}>{card.title}</h2>
<p>{card.description}</p>
</SpotlightCard>
))}
</SpotlightCardsContainer>
);
};
const spotlightCardsContainerStyles = {
margin: "32px",
display: "flex",
gap: "16px",
flexWrap: "wrap",
justifyContent: "center",
};
const spotlightCardStyles = {
width: "320px",
borderRadius: "8px",
};
const spotlightCardWrapperStyles = {
background: "var(--layer-secondary, #111111)",
padding: "16px",
};
const cardHeading = {
fontSize: "1.25em",
};
const cardIcon = {
display: "inline-block",
marginBottom: "16px",
};
const cards = [
{
icon: "⚡",
title: "Fast Performance",
description: "Experience lightning-fast load times and smooth interactions across all devices.",
},
{
icon: "🔒",
title: "Secure by Default",
description: "Built with modern security practices to keep your data safe and protected.",
},
{
icon: "🎨",
title: "Customizable UI",
description: "Easily adapt components to match your brand and design system.",
},
{
icon: "📱",
title: "Responsive Design",
description: "Optimized layouts that look great on mobile, tablet, and desktop screens.",
},
{
icon: "🧑💻",
title: "Developer Friendly",
description: "Simple APIs and flexible props make development fast and enjoyable.",
},
{
icon: "📈",
title: "Scalable Architecture",
description: "Designed to grow with your application from small projects to large systems.",
},
];
export default SpotlightCardSpotlightSizePreview; Spotlight Border Width
spotlight-card
import SpotlightCard, { SpotlightCardsContainer } from "@/components/interactions/spotlight-card/spotlight-card";
const SpotlightCardSpotlightBorderWidthPreview = () => {
return (
<SpotlightCardsContainer
style={spotlightCardsContainerStyles}
>
{cards.map((card) => (
<SpotlightCard
spotlightColor="rgb(132, 250, 175)"
spotlightBorderWidth={4}
style={spotlightCardStyles}
wrapperProps={{
style: spotlightCardWrapperStyles,
}}
>
<span style={cardIcon}>{card.icon}</span>
<h2 style={cardHeading}>{card.title}</h2>
<p>{card.description}</p>
</SpotlightCard>
))}
</SpotlightCardsContainer>
);
};
const spotlightCardsContainerStyles = {
margin: "32px",
display: "flex",
gap: "16px",
flexWrap: "wrap",
justifyContent: "center",
};
const spotlightCardStyles = {
width: "320px",
borderRadius: "8px",
};
const spotlightCardWrapperStyles = {
background: "var(--layer-secondary, #111111)",
padding: "16px",
};
const cardHeading = {
fontSize: "1.25em",
};
const cardIcon = {
display: "inline-block",
marginBottom: "16px",
};
const cards = [
{
icon: "⚡",
title: "Fast Performance",
description: "Experience lightning-fast load times and smooth interactions across all devices.",
},
{
icon: "🔒",
title: "Secure by Default",
description: "Built with modern security practices to keep your data safe and protected.",
},
{
icon: "🎨",
title: "Customizable UI",
description: "Easily adapt components to match your brand and design system.",
},
{
icon: "📱",
title: "Responsive Design",
description: "Optimized layouts that look great on mobile, tablet, and desktop screens.",
},
{
icon: "🧑💻",
title: "Developer Friendly",
description: "Simple APIs and flexible props make development fast and enjoyable.",
},
{
icon: "📈",
title: "Scalable Architecture",
description: "Designed to grow with your application from small projects to large systems.",
},
];
export default SpotlightCardSpotlightBorderWidthPreview; Props
Configure the component with the following props:
SpotlightCardsContainer Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| children | React.ReactNode | Yes | — | One or more SpotlightCard components to be rendered inside the container. |
| tagName | string | No | "div" | HTML tag used to render the container element. |
| className | string | No | — | Additional CSS classes applied to the container. |
| style | React.CSSProperties | No | — | Inline styles applied to the container element. |
SpotlightCard Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| children | React.ReactNode | Yes | — | Content to be rendered inside the card. |
| spotlightColor | string | No | "rgb(127, 127, 127)" | Spotlight color. Accepts RGB or HEX; alpha channel is used to control spotlight intensity. |
| spotlightSize | number | No | 100 | Size of the spotlight effect in pixels. |
| spotlightBorderWidth | number | No | 1 | Width of the card border in pixels; adopts spotlight color on hover proximity. |
| tagName | string | No | `“div” | HTML tag used to render the card container. |
| className | string | No | — | Additional CSS classes applied to the card. |
| style | React.CSSProperties | No | — | Inline styles applied to the card. |
| wrapperTagName | string | No | "div" | HTML tag used for the inner wrapper around children. |
| wrapperProps | Record<string, any> | No | {} | Props passed to the wrapper element (e.g., className, style, etc.). |