Animations & Motion
Learn how to customize and use animations in your SaaS starter kit
This SaaS starter kit uses Framer Motion for smooth, performant animations. Learn how to customize, disable, or extend animations to match your brand.
Animation Philosophy
Our animation system is built on these principles:
- Purposeful: Every animation guides attention or provides feedback
- Performant: Uses transform and opacity for 60fps animations
- Accessible: Respects prefers-reduced-motion settings
- Consistent: Shared timing and easing throughout
Animation System Overview
The animation system is built on three layers:
CSS Variables
Timing and easing values stored in globals.css for consistency
Framer Motion
React animation library for declarative, physics-based motion
CSS Utilities
Pre-built animation classes for common patterns
Animation CSS Variables
Understanding Timing Variables
Animation timing values are defined in src/app/globals.css:
1:root {2 /* Animation Durations */3 --duration-fast: 150ms;4 --duration-normal: 250ms;5 --duration-slow: 350ms;6 --duration-slower: 500ms;7 8 /* Animation Easings */9 --ease-out: cubic-bezier(0.16, 1, 0.3, 1);10 --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);11 --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);12 --ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);13}Customization Tip
Using CSS Animation Utilities
Pre-built animation classes you can use anywhere:
1<!-- Fade in on load -->2<div className="animate-fade-in">Content</div>3 4<!-- Fade in with upward motion -->5<div className="animate-fade-in-up">Content</div>6 7<!-- Scale in effect -->8<div className="animate-scale-in">Content</div>9 10<!-- Staggered animations -->11<div className="animate-fade-in stagger-1">First</div>12<div className="animate-fade-in stagger-2">Second</div>13<div className="animate-fade-in stagger-3">Third</div>Hover & Interaction Utilities
Utility classes for hover effects:
1<!-- Lift on hover -->2<div className="hover-lift">Hover me</div>3 4<!-- Scale on hover -->5<div className="hover-scale">Hover me</div>6 7<!-- Glow on hover -->8<div className="hover-glow">Hover me</div>9 10<!-- Combined effects -->11<div className="hover-lift hover-glow transition-all duration-300">12 Hover me13</div>Framer Motion Components
Basic Motion Components
Use motion components for declarative animations:
1import { motion } from "framer-motion";2 3// Fade in on mount4<motion.div5 initial={{ opacity: 0 }}6 animate={{ opacity: 1 }}7 transition={{ duration: 0.5 }}8>9 Hello10</motion.div>11 12// Slide in from left13<motion.div14 initial={{ x: -100, opacity: 0 }}15 animate={{ x: 0, opacity: 1 }}16 transition={{ type: "spring", stiffness: 100 }}17>18 Slide in19</motion.div>20 21// Scale on hover22<motion.button23 whileHover={{ scale: 1.05 }}24 whileTap={{ scale: 0.95 }}25>26 Click me27</motion.button>Staggered Children
Create staggered animations for lists:
1const container = {2 hidden: { opacity: 0 },3 visible: {4 opacity: 1,5 transition: {6 staggerChildren: 0.1,7 },8 },9};10 11const item = {12 hidden: { opacity: 0, y: 20 },13 visible: { opacity: 1, y: 0 },14};15 16<motion.ul variants={container} initial="hidden" animate="visible">17 {items.map((item) => (18 <motion.li key={item.id} variants={item}>19 {item.name}20 </motion.li>21 ))}22</motion.ul>Scroll-Triggered Animations
Animate elements as they enter the viewport:
1<motion.div2 initial={{ opacity: 0, y: 50 }}3 whileInView={{ opacity: 1, y: 0 }}4 viewport={{ once: true, margin: "-100px" }}5 transition={{ duration: 0.6 }}6>7 This animates when scrolled into view8</motion.div>Accessibility: Reduced Motion
Respecting User Preferences
The starter kit automatically respects prefers-reduced-motion:
1@media (prefers-reduced-motion: reduce) {2 *,3 *::before,4 *::after {5 animation-duration: 0.01ms !important;6 animation-iteration-count: 1 !important;7 transition-duration: 0.01ms !important;8 scroll-behavior: auto !important;9 }10}Accessibility First
Testing Reduced Motion
Test your animations with reduced motion enabled:
- Open browser DevTools
- Press
Cmd/Ctrl + Shift + P - Type "Emulate CSS prefers-reduced-motion"
- Select "prefers-reduced-motion: reduce"
Common Animation Patterns
Page Transitions
Wrap page content for smooth transitions:
1import { motion } from "framer-motion";2 3export default function Page() {4 return (5 <motion.div6 initial={{ opacity: 0, y: 20 }}7 animate={{ opacity: 1, y: 0 }}8 transition={{ duration: 0.5 }}9 >10 {/* Page content */}11 </motion.div>12 );13}Card Hover Effects
Create engaging card interactions:
1<motion.div2 className="rounded-xl border bg-white p-6"3 whileHover={{ 4 y: -4, 5 boxShadow: "0 20px 40px -10px rgba(0,0,0,0.1)" 6 }}7 transition={{ duration: 0.2 }}8>9 Card content10</motion.div>Loading Skeleton
Animated loading state using existing utilities:
1import { LoadingSkeleton } from "@/components/ui";2 3// Use the built-in skeleton component4<LoadingSkeleton lines={3} />5 6// Or create custom skeleton7<div className="animate-pulse space-y-3">8 <div className="h-4 bg-primary-200 rounded w-3/4" />9 <div className="h-4 bg-primary-200 rounded" />10 <div className="h-4 bg-primary-200 rounded w-5/6" />11</div>Performance Best Practices
Use transform and opacity
These properties don't trigger layout or paint, ensuring 60fps animations.
Avoid animating layout properties
Avoid width, height, top, left changes. Use scale and translate instead.
Use will-change sparingly
Only apply will-change to elements that are actively animating.
Next Steps
Now that you understand the animation system, explore these related guides:
- Customize UI Components - Learn to style animated components
- Theme Colors - Customize colors for animations