feat: complete agency site build (Phases 1-7)
Full Next.js 16 + Payload CMS 3.x agency site with: - Homepage: Hero, TrustBar, Services, Configurator wizard, Process, Selected Works, Philosophy, CTA Banner - Sub-pages: /services (3 pillars + AI Layer), /work/[slug] (case studies), /about (philosophy + story) - Configurator: 3-step wizard with AI brief generation API - i18n: Full EN/FR bilingual with next-intl - Design system: Cormorant Garamond + Inter, celestial blue palette, glassmorphism nav, Framer Motion animations - Payload CMS collections: Projects, Services, Submissions, Media Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
115
src/lib/animations.ts
Normal file
115
src/lib/animations.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { Variants, Transition } from 'framer-motion'
|
||||
|
||||
// Shared easing
|
||||
const ease = [0.16, 1, 0.3, 1] as const
|
||||
|
||||
// Scroll-triggered reveal: fade in + slide up
|
||||
export const revealVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 40,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Stagger container for child elements
|
||||
export const staggerContainer: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.08,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Stagger with wider gap (for larger elements)
|
||||
export const staggerContainerWide: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Fade in only (no movement)
|
||||
export const fadeVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.5, ease },
|
||||
},
|
||||
}
|
||||
|
||||
// Scale + fade (for CTAs, buttons)
|
||||
export const scaleVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0.95,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: { duration: 0.3, ease },
|
||||
},
|
||||
}
|
||||
|
||||
// Slide from left
|
||||
export const slideLeftVariants: Variants = {
|
||||
hidden: { opacity: 0, x: -40 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: { duration: 0.6, ease },
|
||||
},
|
||||
}
|
||||
|
||||
// Slide from right
|
||||
export const slideRightVariants: Variants = {
|
||||
hidden: { opacity: 0, x: 40 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: { duration: 0.6, ease },
|
||||
},
|
||||
}
|
||||
|
||||
// Page transition
|
||||
export const pageTransition: Transition = {
|
||||
duration: 0.4,
|
||||
ease,
|
||||
}
|
||||
|
||||
export const pageVariants: Variants = {
|
||||
initial: { opacity: 0, y: 20 },
|
||||
animate: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: pageTransition,
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
y: -20,
|
||||
transition: { duration: 0.3, ease },
|
||||
},
|
||||
}
|
||||
|
||||
// Spring for interactive elements (select, toggle)
|
||||
export const springTransition: Transition = {
|
||||
type: 'spring',
|
||||
stiffness: 400,
|
||||
damping: 25,
|
||||
}
|
||||
|
||||
// Viewport settings for scroll-triggered animations
|
||||
export const viewportOnce = {
|
||||
once: true,
|
||||
amount: 0.15 as const,
|
||||
}
|
||||
5
src/lib/utils.ts
Normal file
5
src/lib/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return clsx(inputs)
|
||||
}
|
||||
Reference in New Issue
Block a user