Reusable responsive grid orchestrator that reveals cells across linear, diagonal-wave, or radial-center stagger patterns with replay and static fallback support.
Preview
01 // AST
02 // ESM
03 // CSS
04 // CLI
05 // A11Y
06 // SSR
Customize
4 props3
0.055
0.48
Installation
Copies the component source and its CSS Module into your project.
npx motionfoundry add staggered-grid-revealWrites components/ui/staggered-grid-reveal.tsx and components/styles/staggered-grid-reveal.module.css.
Usage
Updates live as you change the controls — only props that differ from the defaults are included.
example.tsx
1import { StaggeredGridReveal } from "@motionfoundry/ui";2 3export function Example() {4 return (5 <StaggeredGridReveal />6 );7}Props
Accessibility
Preserves natural DOM tab order across grid cells regardless of visual stagger pattern and renders cells immediately when prefers-reduced-motion is active.
Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.
Source
components/ui/staggered-grid-reveal.tsx
1"use client";2 3import * as React from "react";4import { motion, useReducedMotion } from "motion/react";5import styles from "../styles/staggered-grid-reveal.module.css";6 7export interface StaggeredGridRevealProps {8 /** Spatial stagger pattern */9 pattern?: "diagonal" | "linear" | "radial-center";10 /** Number of columns */11 columns?: number;12 /** Base stagger interval in seconds */13 stagger?: number;14 /** Cell transition duration in seconds */15 duration?: number;16 /** Optional custom cell labels */17 cells?: string[];18 /** Optional className */19 className?: string;20}21 22const DEFAULT_CELLS = [23 "01 // AST",24 "02 // ESM",25 "03 // CSS",26 "04 // CLI",27 "05 // A11Y",28 "06 // SSR",29];30 31export function StaggeredGridReveal({32 pattern = "diagonal",33 columns = 3,34 stagger = 0.055,35 duration = 0.48,36 cells = DEFAULT_CELLS,37 className = "",38}: StaggeredGridRevealProps) {39 const prefersReducedMotion = useReducedMotion();40 const safeCols = Math.min(Math.max(columns, 2), 4);41 const rows = Math.ceil(cells.length / safeCols);42 43 const computeDelay = (idx: number) => {44 if (prefersReducedMotion) return 0;45 const r = Math.floor(idx / safeCols);46 const c = idx % safeCols;47 if (pattern === "diagonal") {48 return (r + c) * stagger * 1.4;49 }50 if (pattern === "radial-center") {51 const centerR = (rows - 1) / 2;52 const centerC = (safeCols - 1) / 2;53 const dist = Math.hypot(r - centerR, c - centerC);54 return dist * stagger * 1.6;55 }56 return idx * stagger;57 };58 59 return (60 <div61 style={{ gridTemplateColumns: `repeat(${safeCols}, minmax(0, 1fr))` }}62 className={`${styles.grid} ${className}`.trim()}63 >64 {cells.map((label, idx) => (65 <motion.div66 key={`${label}-${idx}-${pattern}`}67 initial={prefersReducedMotion ? false : { opacity: 0, y: 18, scale: 0.92 }}68 animate={{ opacity: 1, y: 0, scale: 1 }}69 transition={{70 duration,71 delay: computeDelay(idx),72 ease: [0.16, 1, 0.3, 1],73 }}74 className={styles.cell}75 >76 <span className={styles.cellLabel}>{label}</span>77 </motion.div>78 ))}79 </div>80 );81}