Implemented animated counters for the homepage that count up when scrolled into view, with proper accessibility support and smooth easing animations.
- Uses IntersectionObserver API to detect when counter enters viewport
- Animation triggers at 10% visibility threshold
- Only animates once per page load (tracked with
hasAnimatedstate)
- Uses
toLocaleString('en-US')for proper number formatting - Example: 1234567 displays as "1,234,567"
- Implements easeOutQuart easing function for smooth deceleration
- 2-second default duration (configurable via props)
- Uses
requestAnimationFramefor optimal performance
- Detects user's motion preference via media query
- Shows final number instantly if reduced motion is preferred
- Dynamically updates if user changes preference
- Grid layout adapts: 1 column on mobile, 3 columns on larger screens
- Counters are centered and properly spaced
- Uses Tailwind responsive classes
aria-live="polite"announces count changes to screen readersaria-atomic="true"ensures full value is announced- Respects prefers-reduced-motion for vestibular disorders
- Semantic HTML structure
- All types explicitly defined
- Proper interface for component props
- Type-safe state management
interface CounterProps {
end: number; // Final number to count to
duration?: number; // Animation duration in ms (default: 2000)
className?: string; // Additional CSS classes
prefix?: string; // Text before number (e.g., "$")
suffix?: string; // Text after number (e.g., "%")
}import { Counter } from '@/components/atoms/Counter';
<Counter end={1234567} prefix="$" />
<Counter end={5420} />
<Counter end={98} suffix="%" />components/atoms/Counter.tsx- Main counter component
app/page.tsx- Added counter showcase with platform stats
- Client Component: Marked with
'use client'for React hooks usage - IntersectionObserver: Efficient viewport detection without scroll listeners
- requestAnimationFrame: Smooth 60fps animation
- Easing Function: easeOutQuart provides natural deceleration
- Single Animation:
hasAnimatedflag prevents re-animation on scroll - Accessibility First: Motion preferences checked before animation
- Counter animates when scrolling into view
- Large numbers display with comma formatting
- Animation has smooth easing (not linear)
- With prefers-reduced-motion enabled, shows final number instantly
- Only animates once per page load
- Responsive on mobile (320px), tablet (768px), desktop (1024px+)
- Screen reader announces final value
- No TypeScript errors with strict mode
- No console errors or warnings
- IntersectionObserver: All modern browsers
- prefers-reduced-motion: All modern browsers
- requestAnimationFrame: All modern browsers
- toLocaleString: All modern browsers
- Uses IntersectionObserver (more efficient than scroll listeners)
- requestAnimationFrame for optimal rendering
- Cleanup functions prevent memory leaks
- Single animation per counter (no re-renders on scroll)