DotGrid.tsx66 lines · main
1import { motion } from 'framer-motion'
2
3interface DotGridProps {
4 rows: number
5 columns: number
6 count: number
7}
8
9export const DotGrid = ({ rows, columns, count }: DotGridProps) => {
10 const container = {
11 hidden: { opacity: 1 },
12 visible: {
13 opacity: 1,
14 transition: {
15 staggerChildren: 0.05,
16 delayChildren: 0.05,
17 },
18 },
19 }
20
21 const item = {
22 hidden: { opacity: 0 },
23 visible: {
24 opacity: [1, 0.5, 1],
25 transition: {
26 repeat: Infinity,
27 duration: 0.5,
28 repeatDelay: 1.5,
29 ease: 'easeInOut',
30 },
31 },
32 }
33
34 return (
35 <div
36 className="relative w-full h-full"
37 style={{
38 maskImage: 'linear-gradient(to bottom, black, transparent)',
39 WebkitMaskImage: 'linear-gradient(to bottom, black, transparent)',
40 }}
41 >
42 <motion.div
43 className="grid w-full h-full justify-between items-space-between items-start"
44 style={{
45 gridTemplateColumns: `repeat(${columns}, 1px)`,
46 gridTemplateRows: `repeat(${rows}, 1fr)`,
47 rowGap: 'auto',
48 }}
49 variants={container}
50 initial="hidden"
51 animate="visible"
52 aria-label={`Grid of ${rows * columns} dots, ${count} highlighted`}
53 >
54 {Array.from({ length: rows * columns }).map((_, index) => {
55 return (
56 <motion.div
57 key={index}
58 variants={item}
59 className={`w-px h-px rounded-full bg-foreground`}
60 />
61 )
62 })}
63 </motion.div>
64 </div>
65 )
66}