Spotlight Card
Spotlight Card brings controlled expression to your interface.
Make it yours.
Install the editable source and its dependencies into your React + Tailwind project.
Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/spotlight-card.jsonUse it
Scroll horizontally for long lines
"use client";
import { SpotlightCard } from "@/components/jez-ui/ui/spotlight-card";
import { Plus } from "lucide-react";
export default function Example() {
return (
<>
<SpotlightCard className="w-full max-w-sm border-0 bg-foreground p-8 text-background">
<div className="flex min-h-64 flex-col justify-between">
<Plus size={32} />
<h3 className="text-4xl leading-tight tracking-tight">
The detail
<br />
is the difference.
</h3>
<p className="text-xs opacity-70">
Follow your curiosity. And the light.
</p>
</div>
</SpotlightCard>
</>
);
}
Props & types
Scroll horizontally for long lines
Omit<
React.ComponentProps<"div">,
keyof {
children: React.ReactNode;
className?: string;
}
> & {
children: React.ReactNode;
className?: string;
}Source
View 2 source files
registry/ui/spotlight-card.tsx
Scroll horizontally for long lines
"use client";
import * as React from "react";
import { cn } from "./utils";
export function SpotlightCard({
children,
className,
...rootProps
}: Omit<
React.ComponentProps<"div">,
keyof {
children: React.ReactNode;
className?: string;
}
> & {
children: React.ReactNode;
className?: string;
}) {
const [point, setPoint] = React.useState({ x: 50, y: 50 });
return (
<div
{...rootProps}
onPointerMove={(e) => {
rootProps.onPointerMove?.(e);
if (e.defaultPrevented) return;
const r = e.currentTarget.getBoundingClientRect();
setPoint({
x: ((e.clientX - r.left) / r.width) * 100,
y: ((e.clientY - r.top) / r.height) * 100,
});
}}
className={cn(
"relative overflow-hidden rounded-xl border border-border p-8",
className,
)}
>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 opacity-20"
style={{
background: `radial-gradient(circle at ${point.x}% ${point.y}%,var(--primary),transparent 65%)`,
}}
/>
<div className="relative">{children}</div>
</div>
);
}
registry/ui/utils.ts
Scroll horizontally for long lines
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...values: ClassValue[]) {
return twMerge(clsx(values));
}
Dependencies
clsx@2.1.1 · tailwind-merge@3.5.0 · lucide-react@0.577.0
Accessibility & behaviour
Respect reduced-motion preferences. Decorative effects must not carry essential information. WebGL scenes include a static fallback and only render while visible.
Read the accessibility and performance guide →