Best Practices
Best practices for using FoundryKit animations effectively
Best Practices
Learn the best practices for using FoundryKit animations to create engaging, performant, and accessible user experiences.
Animation Usage
Choose Appropriate Animations
Select animations that enhance the user experience without being distracting.
import { FadeIn, SlideUp, Scale } from '@foundrykit/animation'
function AppropriateAnimations() {
return (
<div className="space-y-6">
{/* ✅ Good - Subtle entrance animation */}
<FadeIn>
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Welcome</h2>
<p>Content fades in smoothly</p>
</div>
</FadeIn>
{/* ✅ Good - Directional animation for lists */}
<SlideUp>
<div className="space-y-2">
{['Item 1', 'Item 2', 'Item 3'].map((item, index) => (
<div key={index} className="p-2 bg-gray-100 rounded">
{item}
</div>
))}
</div>
</SlideUp>
{/* ✅ Good - Interactive feedback */}
<Scale>
<button className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">
Click me
</button>
</Scale>
</div>
)
}
// ❌ Avoid - Overly complex or distracting animations
function AvoidExcessiveAnimations() {
return (
<div className="space-y-4">
{/* Too many animations can be overwhelming */}
<FadeIn>
<SlideUp>
<Scale>
<div className="p-4 bg-red-100 rounded">
Too many nested animations
</div>
</Scale>
</SlideUp>
</FadeIn>
</div>
)
}
Use Consistent Timing
Maintain consistent animation timing throughout your application.
import { FadeIn, SlideUp } from '@foundrykit/animation'
function ConsistentTiming() {
// Define consistent timing values
const timing = {
fast: 0.2,
normal: 0.4,
slow: 0.6
}
return (
<div className="space-y-4">
{/* Fast animations for immediate feedback */}
<FadeIn duration={timing.fast}>
<button className="px-4 py-2 bg-blue-600 text-white rounded">
Quick action
</button>
</FadeIn>
{/* Normal animations for content transitions */}
<SlideUp duration={timing.normal}>
<div className="p-4 bg-gray-100 rounded">
Content transition
</div>
</SlideUp>
{/* Slow animations for dramatic effects */}
<FadeIn duration={timing.slow}>
<div className="p-6 bg-white rounded-lg shadow">
Hero content
</div>
</FadeIn>
</div>
)
}
Performance Optimization
Optimize Animation Performance
Follow performance best practices for smooth animations.
import { FadeIn, Stagger } from '@foundrykit/animation'
import { useMediaQuery } from '@foundrykit/hooks'
function OptimizedAnimations() {
const isMobile = useMediaQuery('(max-width: 768px)')
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
// Reduce animation complexity on mobile and for users who prefer reduced motion
const animationProps = {
duration: isMobile || prefersReducedMotion ? 0.3 : 0.5,
staggerDelay: isMobile ? 0.05 : 0.1
}
return (
<div className="space-y-4">
<FadeIn {...animationProps}>
<div className="p-4 bg-blue-100 rounded">
Optimized animation
</div>
</FadeIn>
<Stagger {...animationProps}>
<div className="space-y-2">
{[1, 2, 3, 4, 5].map(i => (
<div key={i} className="p-2 bg-gray-100 rounded">
Item {i}
</div>
))}
</div>
</Stagger>
</div>
)
}
Limit Concurrent Animations
Avoid overwhelming users with too many simultaneous animations.
import { Stagger } from '@foundrykit/animation'
function LimitedConcurrentAnimations() {
const largeList = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`)
return (
<div className="space-y-4">
{/* Limit visible animations to prevent performance issues */}
<Stagger
animation="slideUp"
staggerDelay={0.05}
maxVisible={10 // Only animate visible items
>
{largeList.map((item, index) => (
<div key={index} className="p-2 bg-gray-100 rounded">
{item}
</div>
))}
</Stagger>
</div>
)
}
Accessibility
Respect User Preferences
Always respect user accessibility preferences.
import { FadeIn, SlideUp } from '@foundrykit/animation'
import { useMediaQuery } from '@foundrykit/hooks'
function AccessibleAnimations() {
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
if (prefersReducedMotion) {
return (
<div className="space-y-4">
{/* No animations for users who prefer reduced motion */}
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Welcome</h2>
<p>Content without animations</p>
</div>
</div>
)
}
return (
<div className="space-y-4">
<FadeIn>
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Welcome</h2>
<p>Content with animations</p>
</div>
</FadeIn>
</div>
)
}
Provide Alternative Content
Ensure content is accessible even without animations.
import { FadeIn } from '@foundrykit/animation'
function AlternativeContent() {
return (
<div className="space-y-4">
<FadeIn>
<div className="p-4 bg-green-100 rounded">
<h2 className="text-xl font-bold">Important Information</h2>
<p>This content is important and should be visible regardless of animation support.</p>
</div>
</FadeIn>
{/* Fallback for users without JavaScript */}
<noscript>
<div className="p-4 bg-yellow-100 rounded border-l-4 border-yellow-500">
<p>JavaScript is required for enhanced animations.</p>
</div>
</noscript>
</div>
)
}
State Management
Handle Animation States
Properly manage animation states and transitions.
import { FadeIn, SlideUp } from '@foundrykit/animation'
import { useState, useEffect } from 'react'
function AnimationStateManagement() {
const [isVisible, setIsVisible] = useState(false)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false)
setIsVisible(true)
}, 1000)
return () => clearTimeout(timer)
}, [])
if (isLoading) {
return (
<div className="p-4 bg-gray-100 rounded">
<div className="animate-pulse">Loading...</div>
</div>
)
}
return (
<div className="space-y-4">
{isVisible && (
<FadeIn>
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Content Loaded</h2>
<p>Animation triggered after loading</p>
</div>
</FadeIn>
)}
<SlideUp>
<div className="p-4 bg-green-100 rounded">
<p>Always visible content</p>
</div>
</SlideUp>
</div>
)
}
Error Handling
Handle animation errors gracefully.
import { FadeIn } from '@foundrykit/animation'
import { useState } from 'react'
function AnimationErrorHandling() {
const [hasError, setHasError] = useState(false)
const handleAnimationError = (error: Error) => {
console.error('Animation error:', error)
setHasError(true)
}
if (hasError) {
return (
<div className="p-4 bg-red-100 rounded border-l-4 border-red-500">
<p>Animation failed to load. Content is still accessible.</p>
</div>
)
}
return (
<FadeIn onError={handleAnimationError}>
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Animated Content</h2>
<p>This animation has error handling</p>
</div>
</FadeIn>
)
}
Code Organization
Create Reusable Animation Components
Build reusable animation components for consistency.
import { FadeIn, SlideUp, Scale } from '@foundrykit/animation'
import { ReactNode } from 'react'
interface AnimatedCardProps {
children: ReactNode
animation?: 'fade' | 'slide' | 'scale'
delay?: number
className?: string
}
function AnimatedCard({
children,
animation = 'fade',
delay = 0,
className = ''
}: AnimatedCardProps) {
const baseClasses = "p-4 bg-white rounded-lg shadow"
const combinedClasses = `${baseClasses} ${className}`
switch (animation) {
case 'slide':
return (
<SlideUp delay={delay}>
<div className={combinedClasses}>{children}</div>
</SlideUp>
)
case 'scale':
return (
<Scale delay={delay}>
<div className={combinedClasses}>{children}</div>
</Scale>
)
default:
return (
<FadeIn delay={delay}>
<div className={combinedClasses}>{children}</div>
</FadeIn>
)
}
}
function ReusableAnimations() {
return (
<div className="space-y-4">
<AnimatedCard animation="fade">
<h2 className="text-xl font-bold">Fade Animation</h2>
<p>Reusable component with fade animation</p>
</AnimatedCard>
<AnimatedCard animation="slide" delay={0.1}>
<h2 className="text-xl font-bold">Slide Animation</h2>
<p>Reusable component with slide animation</p>
</AnimatedCard>
<AnimatedCard animation="scale" delay={0.2}>
<h2 className="text-xl font-bold">Scale Animation</h2>
<p>Reusable component with scale animation</p>
</AnimatedCard>
</div>
)
}
Organize Animation Logic
Separate animation logic from component logic.
import { FadeIn, SlideUp } from '@foundrykit/animation'
import { useMediaQuery } from '@foundrykit/hooks'
// Custom hook for animation configuration
function useAnimationConfig() {
const isMobile = useMediaQuery('(max-width: 768px)')
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
return {
enabled: !prefersReducedMotion,
duration: isMobile ? 0.3 : 0.5,
staggerDelay: isMobile ? 0.05 : 0.1
}
}
function OrganizedAnimations() {
const animationConfig = useAnimationConfig()
if (!animationConfig.enabled) {
return (
<div className="space-y-4">
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Content</h2>
<p>No animations for accessibility</p>
</div>
</div>
)
}
return (
<div className="space-y-4">
<FadeIn duration={animationConfig.duration}>
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Animated Content</h2>
<p>Organized animation logic</p>
</div>
</FadeIn>
<SlideUp
duration={animationConfig.duration}
delay={animationConfig.staggerDelay}
>
<div className="p-4 bg-green-100 rounded">
<p>Consistent timing</p>
</div>
</SlideUp>
</div>
)
}
Testing
Test Animation Behavior
Ensure animations work correctly across different scenarios.
import { FadeIn } from '@foundrykit/animation'
import { render, screen, waitFor } from '@testing-library/react'
function TestableAnimation() {
return (
<FadeIn data-testid="animated-element">
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Testable Content</h2>
<p>This component is testable</p>
</div>
</FadeIn>
)
}
// Example test
describe('TestableAnimation', () => {
it('renders animated content', async () => {
render(<TestableAnimation />)
const element = screen.getByTestId('animated-element')
expect(element).toBeInTheDocument()
// Wait for animation to complete
await waitFor(() => {
expect(element).toHaveStyle({ opacity: '1' })
})
})
})
Test Accessibility
Verify animations respect accessibility preferences.
import { FadeIn } from '@foundrykit/animation'
import { useMediaQuery } from '@foundrykit/hooks'
function AccessibleTestableAnimation() {
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
return (
<div data-testid="animation-container">
{prefersReducedMotion ? (
<div className="p-4 bg-blue-100 rounded" data-testid="static-content">
<h2 className="text-xl font-bold">Static Content</h2>
<p>No animations for accessibility</p>
</div>
) : (
<FadeIn data-testid="animated-content">
<div className="p-4 bg-blue-100 rounded">
<h2 className="text-xl font-bold">Animated Content</h2>
<p>Animations enabled</p>
</div>
</FadeIn>
)}
</div>
)
}
Common Anti-Patterns
Avoid These Practices
// ❌ Don't nest multiple animations unnecessarily
function AvoidNestedAnimations() {
return (
<FadeIn>
<SlideUp>
<Scale>
<div className="p-4 bg-red-100 rounded">
Too many nested animations
</div>
</Scale>
</SlideUp>
</FadeIn>
)
}
// ❌ Don't ignore user preferences
function AvoidIgnoringPreferences() {
return (
<FadeIn>
<div className="p-4 bg-red-100 rounded">
Always animated (ignores user preferences)
</div>
</FadeIn>
)
}
// ❌ Don't use animations for critical content
function AvoidCriticalContentAnimations() {
return (
<FadeIn>
<div className="p-4 bg-red-100 rounded">
<h1 className="text-2xl font-bold">Critical Error</h1>
<p>This should be immediately visible</p>
</div>
</FadeIn>
)
}
// ❌ Don't forget error handling
function AvoidNoErrorHandling() {
return (
<FadeIn>
<div className="p-4 bg-blue-100 rounded">
No error handling for animation failures
</div>
</FadeIn>
)
}
Best Practices Checklist
Before Implementation
- Choose animations that enhance UX, not distract from it
- Respect user accessibility preferences
- Test on different devices and performance levels
- Plan for error states and fallbacks
- Consider performance implications
During Development
- Use consistent timing and easing
- Implement proper cleanup and error handling
- Test with reduced motion preferences
- Monitor performance and frame rates
- Ensure content is accessible without animations
Before Production
- Test on low-end devices
- Verify accessibility compliance
- Check for memory leaks
- Optimize animation performance
- Document animation patterns and usage
Next Steps
- Review performance guidelines for optimization techniques
- Learn about animation utilities for advanced patterns
- Explore the animation components for detailed usage examples