(null)
const [isSq, setSq] = useState(false)
const morph = () => {
const t = !isSq
P.forEach((p, i) => {
const el = svg.current!.querySelector(`#d-${p.id}`)
if (el) gsap.to(el, { attr: { d: t ? p.sq : p.tri }, duration: 1.5, ease: 'power2.inOut', delay: i * 0.15 })
}); setSq(t)
}
return (
)
}
```
See [references/geometric-puzzles.md](references/geometric-puzzles.md) for tangram, tessellations, Penrose tiles, polyominoes.
### 9. Brutalist Grid (Motion)
```tsx
'use client'
import { motion } from 'motion/react'
export function BrutalistGrid({ items }: { items: string[] }) {
return (
{items.map((item, i) => (
{item}
))}
)
}
```
## Design Philosophy (Quick Reference)
| Style | Motion Feel | Easing | Typography | Key Trait |
|-------|------------|--------|------------|-----------|
| Brutalist | Hard, instant, jarring | `none` / `steps()` | Mono, 15-30vw | Raw honesty |
| Minimalist | Smooth, subtle, slow | `power2.out` | Sans-serif light | Purposeful restraint |
| Abstract | Noise-driven, parametric | Organic/sine | Varies | Mathematical beauty |
| Neo-Brutalist | Bold but controlled | `power1.out` | Mono + color | Brutalism + restraint |
See [references/design-philosophy.md](references/design-philosophy.md) for full guide with color palettes and mixing strategies.
## Easing Reference
| Feel | GSAP | Motion |
|------|------|--------|
| Smooth | `power2.out` | `[0.16, 1, 0.3, 1]` |
| Snappy | `power4.out` | `[0.87, 0, 0.13, 1]` |
| Bouncy | `back.out(1.7)` | `{ type: 'spring', stiffness: 300, damping: 20 }` |
| Dramatic | `power4.inOut` | `[0.76, 0, 0.24, 1]` |
## Timing
- Micro-interactions: 150-300ms
- UI transitions: 300-500ms
- Page transitions: 500-800ms
- Stagger: 0.02-0.1s per item
## Accessibility
```tsx
// Motion: useReducedMotion() → conditionally disable/reduce animations
import { useReducedMotion } from 'motion/react'
const reduced = useReducedMotion() // true if prefers-reduced-motion: reduce
```
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
```
## Performance Rules
1. Only animate `transform` and `opacity`
2. Use `will-change` sparingly
3. Always cleanup: `useGSAP` handles it automatically
4. Scope GSAP selectors to container refs
5. Use `contextSafe()` for event handlers with GSAP
6. Memoize Motion variants objects
## Common Pitfalls
1. Not integrating Lenis with ScrollTrigger
2. Missing `scope` in useGSAP
3. Not using `contextSafe()` for click handlers
4. React 18 Strict Mode calling effects twice
5. Forgetting `'use client'` in Next.js App Router
6. Not calling `ScrollTrigger.refresh()` after dynamic content
## Testing Checklist
- [ ] 60fps on scroll (Chrome DevTools Performance)
- [ ] Keyboard navigation works
- [ ] Respects prefers-reduced-motion
- [ ] No layout shifts (CLS)
- [ ] Mobile touch works
- [ ] ScrollTrigger markers removed in prod
- [ ] No memory leaks on unmount
## Inspiration
Active Theory, Studio Freight, Locomotive, Resn, Aristide Benoist, Immersive Garden