/**
* Reusable Animated Components Library
*
* Collection of common animated components following Apple/Jon Ive design principles.
* Copy individual components as needed.
*/
import { motion, useMotionValue, useSpring, useReducedMotion } from "motion/react"
import { useRef, MouseEvent, ReactNode } from "react"
// ============================================================================
// 1. FADE UP (Entrance Animation)
// ============================================================================
interface FadeUpProps {
children: ReactNode
delay?: number
}
export function FadeUp({ children, delay = 0 }: FadeUpProps) {
const shouldReduceMotion = useReducedMotion()
return (
{children}
)
}
// ============================================================================
// 2. SCROLL REVEAL
// ============================================================================
interface ScrollRevealProps {
children: ReactNode
direction?: "up" | "down" | "left" | "right"
delay?: number
}
export function ScrollReveal({ children, direction = "up", delay = 0 }: ScrollRevealProps) {
const directions = {
up: { y: 50 },
down: { y: -50 },
left: { x: 50 },
right: { x: -50 },
}
return (
{children}
)
}
// ============================================================================
// 3. HOVER CARD (Interactive Card)
// ============================================================================
interface HoverCardProps {
children: ReactNode
className?: string
}
export function HoverCard({ children, className = "" }: HoverCardProps) {
return (
{children}
)
}
// ============================================================================
// 4. MAGNETIC BUTTON (Cursor Following)
// ============================================================================
interface MagneticButtonProps {
children: ReactNode
className?: string
onClick?: () => void
}
export function MagneticButton({ children, className = "", onClick }: MagneticButtonProps) {
const ref = useRef(null)
const x = useMotionValue(0)
const y = useMotionValue(0)
const springX = useSpring(x, { stiffness: 300, damping: 20 })
const springY = useSpring(y, { stiffness: 300, damping: 20 })
const handleMouseMove = (e: MouseEvent) => {
if (!ref.current) return
const rect = ref.current.getBoundingClientRect()
const centerX = rect.left + rect.width / 2
const centerY = rect.top + rect.height / 2
const distanceX = e.clientX - centerX
const distanceY = e.clientY - centerY
x.set(distanceX * 0.3)
y.set(distanceY * 0.3)
}
const handleMouseLeave = () => {
x.set(0)
y.set(0)
}
return (
{children}
)
}
// ============================================================================
// 5. STAGGERED LIST
// ============================================================================
interface StaggeredListProps {
items: ReactNode[]
className?: string
}
export function StaggeredList({ items, className = "" }: StaggeredListProps) {
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
}
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5 },
},
}
return (
{items.map((item, i) => (
{item}
))}
)
}
// ============================================================================
// 6. SCALE BUTTON (Simple Hover/Tap)
// ============================================================================
interface ScaleButtonProps {
children: ReactNode
className?: string
onClick?: () => void
}
export function ScaleButton({ children, className = "", onClick }: ScaleButtonProps) {
return (
{children}
)
}
// ============================================================================
// 7. ACCORDION (Expand/Collapse)
// ============================================================================
interface AccordionProps {
title: string
children: ReactNode
isOpen: boolean
onToggle: () => void
}
export function Accordion({ title, children, isOpen, onToggle }: AccordionProps) {
return (
)
}
// ============================================================================
// 8. MODAL (With Backdrop)
// ============================================================================
interface ModalProps {
isOpen: boolean
onClose: () => void
children: ReactNode
}
export function Modal({ isOpen, onClose, children }: ModalProps) {
if (!isOpen) return null
return (
<>
{/* Backdrop */}
{/* Modal */}
{children}
>
)
}
// ============================================================================
// 9. LOADING SPINNER (Spring Animation)
// ============================================================================
export function LoadingSpinner() {
return (
)
}
// ============================================================================
// 10. TOGGLE SWITCH
// ============================================================================
interface ToggleSwitchProps {
isOn: boolean
onToggle: () => void
}
export function ToggleSwitch({ isOn, onToggle }: ToggleSwitchProps) {
return (
)
}
// ============================================================================
// USAGE EXAMPLE
// ============================================================================
/*
import {
FadeUp,
ScrollReveal,
HoverCard,
MagneticButton,
ScaleButton,
Accordion,
Modal,
LoadingSpinner,
ToggleSwitch,
} from "./component-library"
export default function Example() {
return (
<>
Hello World
Feature Card
console.log("Clicked!")}>
Magnetic Button
setIsOn(!isOn)} />
>
)
}
*/