Initial commit: CHORUS Services marketing website
Complete Next.js website with Docker containerization: - Next.js 14 with TypeScript and Tailwind CSS - Responsive design with modern UI components - Hero section, features showcase, testimonials - FAQ section with comprehensive content - Contact forms and newsletter signup - Docker production build with Nginx - Health checks and monitoring support - SEO optimization and performance tuning Ready for integration as git submodule in main CHORUS project. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
644
utils/animations.ts
Normal file
644
utils/animations.ts
Normal file
@@ -0,0 +1,644 @@
|
||||
import { Variants } from 'framer-motion';
|
||||
|
||||
/**
|
||||
* Common animation variants for Framer Motion
|
||||
* Provides consistent animations across the application
|
||||
*/
|
||||
|
||||
export const fadeInUp: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 30,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const fadeInDown: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: -30,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const fadeInLeft: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
x: -30,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const fadeInRight: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
x: 30,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const fadeIn: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const scaleIn: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const slideInUp: Variants = {
|
||||
hidden: {
|
||||
y: '100%',
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
y: '0%',
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const slideInDown: Variants = {
|
||||
hidden: {
|
||||
y: '-100%',
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
y: '0%',
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const staggerContainer: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.1,
|
||||
delayChildren: 0.1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const staggerFast: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.05,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const staggerSlow: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Hover animations
|
||||
export const hoverScale: Variants = {
|
||||
hover: {
|
||||
scale: 1.05,
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const hoverLift: Variants = {
|
||||
hover: {
|
||||
y: -5,
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const hoverGlow: Variants = {
|
||||
hover: {
|
||||
boxShadow: '0 0 30px rgba(0, 122, 255, 0.3)',
|
||||
transition: {
|
||||
duration: 0.3,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Specialized animations for CHORUS components
|
||||
export const orchestrationFlow: Variants = {
|
||||
hidden: {
|
||||
pathLength: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
pathLength: {
|
||||
duration: 2,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
opacity: {
|
||||
duration: 0.3,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const dataFlow: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0,
|
||||
},
|
||||
visible: (i: number) => ({
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
delay: i * 0.2,
|
||||
duration: 0.5,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const pulseAnimation: Variants = {
|
||||
pulse: {
|
||||
scale: [1, 1.1, 1],
|
||||
opacity: [1, 0.8, 1],
|
||||
transition: {
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const rotateAnimation: Variants = {
|
||||
rotate: {
|
||||
rotate: 360,
|
||||
transition: {
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Page transition animations
|
||||
export const pageTransition: Variants = {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
y: 20,
|
||||
},
|
||||
animate: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.4,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
exit: {
|
||||
opacity: 0,
|
||||
y: -20,
|
||||
transition: {
|
||||
duration: 0.3,
|
||||
ease: 'easeIn',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Modal animations
|
||||
export const modalBackdrop: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const modalContent: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
y: 50,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.4,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Loading animations
|
||||
export const spinnerAnimation: Variants = {
|
||||
spin: {
|
||||
rotate: 360,
|
||||
transition: {
|
||||
duration: 1,
|
||||
repeat: Infinity,
|
||||
ease: 'linear',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const dotsLoading: Variants = {
|
||||
loading: {
|
||||
scale: [1, 1.2, 1],
|
||||
opacity: [1, 0.6, 1],
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Apple-inspired easing curves
|
||||
export const appleEasing = [0.21, 1.11, 0.81, 0.99]; // Apple's signature cubic-bezier
|
||||
export const appleSpring = {
|
||||
type: 'spring',
|
||||
stiffness: 400,
|
||||
damping: 30,
|
||||
mass: 1,
|
||||
};
|
||||
|
||||
// Advanced parallax animations
|
||||
export const parallaxSlow: Variants = {
|
||||
hidden: { y: 0, opacity: 0 },
|
||||
visible: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 1,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const parallaxMedium: Variants = {
|
||||
hidden: { y: 0, opacity: 0 },
|
||||
visible: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const parallaxFast: Variants = {
|
||||
hidden: { y: 0, opacity: 0 },
|
||||
visible: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Gradient text animation
|
||||
export const gradientTextAnimation: Variants = {
|
||||
initial: {
|
||||
backgroundPosition: '0% 50%',
|
||||
},
|
||||
animate: {
|
||||
backgroundPosition: ['0% 50%', '100% 50%', '0% 50%'],
|
||||
transition: {
|
||||
duration: 5,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Advanced button hover states
|
||||
export const appleButtonHover: Variants = {
|
||||
initial: {
|
||||
scale: 1,
|
||||
boxShadow: '0 4px 14px 0 rgba(0, 122, 255, 0.25)',
|
||||
},
|
||||
hover: {
|
||||
scale: 1.05,
|
||||
boxShadow: '0 8px 25px 0 rgba(0, 122, 255, 0.4)',
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
tap: {
|
||||
scale: 0.98,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Hero entrance animations
|
||||
export const heroEntrance: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 60,
|
||||
scale: 0.95,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Staggered hero container
|
||||
export const heroStaggerContainer: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
delayChildren: 0.3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Floating elements animation
|
||||
export const floatingAnimation: Variants = {
|
||||
initial: {
|
||||
y: 0,
|
||||
rotate: 0,
|
||||
},
|
||||
animate: {
|
||||
y: [-10, 10, -10],
|
||||
rotate: [-2, 2, -2],
|
||||
transition: {
|
||||
duration: 6,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Geometric shape animations
|
||||
export const geometricShapeAnimation: Variants = {
|
||||
initial: {
|
||||
scale: 1,
|
||||
opacity: 0.2,
|
||||
rotate: 0,
|
||||
},
|
||||
animate: {
|
||||
scale: [1, 1.5, 1],
|
||||
opacity: [0.2, 0.8, 0.2],
|
||||
rotate: [0, 360],
|
||||
transition: {
|
||||
duration: 8,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Interactive hover glow effect
|
||||
export const interactiveGlow: Variants = {
|
||||
initial: {
|
||||
boxShadow: '0 0 0 rgba(0, 122, 255, 0)',
|
||||
},
|
||||
hover: {
|
||||
boxShadow: '0 0 30px rgba(0, 122, 255, 0.3)',
|
||||
transition: {
|
||||
duration: 0.3,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Advanced scroll-triggered animations
|
||||
export const scrollReveal: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 75,
|
||||
scale: 0.9,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Performance-optimized fade variants
|
||||
export const performantFade: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
transform: 'translateY(20px) translateZ(0)',
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transform: 'translateY(0px) translateZ(0)',
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: appleEasing,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Utility functions for animations
|
||||
export const createStaggeredAnimation = (
|
||||
baseAnimation: Variants,
|
||||
staggerDelay: number = 0.1
|
||||
): Variants => ({
|
||||
...baseAnimation,
|
||||
visible: {
|
||||
...baseAnimation.visible,
|
||||
transition: {
|
||||
...baseAnimation.visible?.transition,
|
||||
staggerChildren: staggerDelay,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const createDelayedAnimation = (
|
||||
baseAnimation: Variants,
|
||||
delay: number
|
||||
): Variants => ({
|
||||
...baseAnimation,
|
||||
visible: {
|
||||
...baseAnimation.visible,
|
||||
transition: {
|
||||
...baseAnimation.visible?.transition,
|
||||
delay,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const createCustomEasing = (
|
||||
baseAnimation: Variants,
|
||||
ease: string | number[]
|
||||
): Variants => ({
|
||||
...baseAnimation,
|
||||
visible: {
|
||||
...baseAnimation.visible,
|
||||
transition: {
|
||||
...baseAnimation.visible?.transition,
|
||||
ease,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Parallax utility function
|
||||
export const createParallaxAnimation = (
|
||||
speed: number,
|
||||
direction: 'up' | 'down' | 'left' | 'right' = 'up'
|
||||
) => {
|
||||
const axis = direction === 'left' || direction === 'right' ? 'x' : 'y';
|
||||
const multiplier = direction === 'down' || direction === 'right' ? 1 : -1;
|
||||
|
||||
return {
|
||||
[axis]: `${speed * multiplier * 100}px`,
|
||||
};
|
||||
};
|
||||
|
||||
// Scroll-based animation utility
|
||||
export const createScrollAnimation = (
|
||||
element: React.RefObject<HTMLElement>,
|
||||
options: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
property: string;
|
||||
from: any;
|
||||
to: any;
|
||||
}
|
||||
) => {
|
||||
// This would be used with useScroll and useTransform hooks
|
||||
return {
|
||||
scrollTrigger: {
|
||||
trigger: element.current,
|
||||
start: options.start || 0,
|
||||
end: options.end || 1000,
|
||||
scrub: true,
|
||||
},
|
||||
[options.property]: [options.from, options.to],
|
||||
};
|
||||
};
|
||||
|
||||
// Reduced motion variants for accessibility
|
||||
export const createReducedMotionVariant = (baseVariant: Variants): Variants => {
|
||||
const prefersReducedMotion = typeof window !== 'undefined' &&
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
if (prefersReducedMotion) {
|
||||
return {
|
||||
...baseVariant,
|
||||
visible: {
|
||||
...baseVariant.visible,
|
||||
transition: {
|
||||
duration: 0.01, // Nearly instant
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return baseVariant;
|
||||
};
|
||||
|
||||
// Apple-style elastic animation
|
||||
export const elasticAnimation = {
|
||||
type: 'spring',
|
||||
stiffness: 500,
|
||||
damping: 25,
|
||||
mass: 0.8,
|
||||
};
|
||||
|
||||
// Magnetic effect for interactive elements
|
||||
export const magneticEffect = {
|
||||
hover: {
|
||||
scale: 1.1,
|
||||
transition: {
|
||||
type: 'spring',
|
||||
stiffness: 400,
|
||||
damping: 10,
|
||||
},
|
||||
},
|
||||
tap: {
|
||||
scale: 0.95,
|
||||
transition: {
|
||||
type: 'spring',
|
||||
stiffness: 600,
|
||||
damping: 15,
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user