Motion & Animation

Motion in Fidus provides feedback, guides attention, and creates smooth transitions. All animations are purposeful, subtle, and respect user preferences for reduced motion.

Motion Principles

1. Purposeful

Every animation serves a purpose: feedback, guidance, or context.

2. Subtle

Animations are quick and understated, not distracting.

3. Consistent

Similar interactions have similar animations across the app.

4. Respectful

Honor prefers-reduced-motion for accessibility.

Timing Functions

We use CSS custom properties for consistent timing curves:

TokenValueUsage
--easing-acceleratecubic-bezier(0.4, 0, 1, 1)Elements exiting - starts slow, ends fast
--easing-deceleratecubic-bezier(0, 0, 0.2, 1)Elements entering - starts fast, ends slow
--easing-standardcubic-bezier(0.4, 0, 0.2, 1)Smooth transitions - balanced start and end

Interactive Demos

Click "Animate" to see each easing curve in action:

--easing-accelerate

cubic-bezier(0.4, 0, 1, 1)

Elements exiting the screen - starts slow, ends fast

--easing-decelerate

cubic-bezier(0, 0, 0.2, 1)

Elements entering the screen - starts fast, ends slow

--easing-standard

cubic-bezier(0.4, 0, 0.2, 1)

Elements moving within the screen - smooth start and end

Duration Scale

TokenValueUsage
--duration-fast150msHover states, button presses
--duration-normal250msCard dismissals, modals
--duration-slow350msPage transitions, large movements

Common Animations

Fade In (Appearing)

Used when OpportunityCards or widgets appear on screen:

New Opportunity

This OpportunityCard appears with a fade-in and slide-up animation.

Animation: opacity 0→1, translateY 8px→0, 300ms ease-out

💡Just appeared with smooth animation

Hover Lift

Subtle elevation on interactive cards:

Hover Over Me

Hover over this card to see it lift up slightly.

Animation: translateY -2px, shadow sm→md, 150ms ease

💡Card has hover lift animation

Expand/Collapse

Used in DetailCard and collapsible sections:

Swipe Gestures

OpportunityCards support swipe-to-dismiss on mobile. Try swiping the card below on a touch device, or use Chrome DevTools Device Emulation (F12 → Toggle device toolbar).

Budget Alert

You've spent 85% of your monthly food budget (€425 of €500).

Consider adjusting your spending or reviewing your budget allocation.

💡Monthly spending update

Swipe Behavior

1. Touch Start

Card begins tracking finger position in real-time

2. Swiping (Distance < 50px)

Card follows finger with no transition delay - immediate feedback

3a. Release (Distance < 50px)

Card springs back to original position with 150ms ease-out transition

3b. Release (Distance > 50px)

Dismiss animation triggers - card slides out completely (250ms ease-in) and onDismiss callback fires

Loading States

Loading states provide feedback during asynchronous operations. Fidus provides Spinner and Skeleton components for different loading scenarios.

Spinner

Used for short operations where exact progress cannot be tracked:

Sizes

Loading small
Loading medium
Loading large

With Context

Loading budget data
Loading budget data...

Skeleton Loader

Used for content loading to show the structure before data arrives:

Text Lines

Loading...
Loading...
Loading...

Card Skeleton (List Item)

Loading...
Loading...
Loading...

Rectangular Block

Loading...

Implementation Examples

Tailwind CSS Classes

// Hover state
<button className="transition-colors duration-150 hover:bg-primary/90">

// Card appearing
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">

// Card dismissing (custom)
<div className="transition-all duration-250 opacity-0 translate-x-full">

// Respect reduced motion
<div className="transition-all duration-250 motion-reduce:transition-none">

Custom Transitions

// In Tailwind config
module.exports = {
  theme: {
    extend: {
      transitionDuration: {
        '150': '150ms',
        '250': '250ms',
        '400': '400ms',
      },
      transitionTimingFunction: {
        'ease-in': 'cubic-bezier(0.4, 0, 1, 1)',
        'ease-out': 'cubic-bezier(0, 0, 0.2, 1)',
        'ease-in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
      },
    },
  },
}

Usage Guidelines

When to use motion

  • •Providing feedback on user interactions (button presses, form submissions)
  • •Guiding user attention to important changes (new opportunity card appears)
  • •Creating smooth transitions between states (modal opening, panel expanding)
  • •Indicating loading or processing states
  • •Enhancing perceived performance (skeleton loaders)

Best practices

  • •Keep animations subtle - they should enhance, not distract
  • •Match animation direction to element behavior (entering from top exits to top)
  • •Use consistent durations from the scale (150ms/250ms/400ms)
  • •Test on lower-end devices - animations may feel different
  • •Avoid animating multiple elements simultaneously
  • •Prefer GPU-accelerated properties (transform, opacity)
  • •Always respect user preferences for reduced motion

Accessibility

  • •Always respect prefers-reduced-motion media query
  • •Use motion-reduce: utilities in Tailwind for reduced motion variants
  • •Provide instant state changes (no animation) when reduced motion is preferred
  • •Never convey information through motion alone - include text or icons
  • •Avoid infinite looping animations (except loading indicators)

Do's and Don'ts

✓ Do

  • •Use 150ms for instant feedback (hover, press)
  • •Use 250ms for transitions (dismiss, modal)
  • •Use 400ms for large movements (page transition)
  • •Always respect prefers-reduced-motion
  • •Animate transform and opacity for performance
  • •Keep animations purposeful and subtle

✗ Don't

  • •Use auto-playing animations on page load
  • •Create long duration animations (>500ms)
  • •Use complex 3D transforms
  • •Animate many elements at once
  • •Create purely decorative animations
  • •Ignore user's reduced motion preference

Accessibility: Reduced Motion

Always respect the prefers-reduced-motion media query. Users who enable this setting should see instant state changes without animations.

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Tailwind Implementation

<div className="transition-all duration-250 motion-reduce:transition-none">

Performance

GPU-Accelerated Properties

Prefer these properties for smooth 60fps animations:

  • •transform - translate, rotate, scale
  • •opacity - fade in/out effects

Avoid Animating

These properties cause layout recalculation (expensive):

  • ✗width, height - Use transform: scale() instead
  • ✗top, left, right, bottom - Use transform: translate() instead
  • ✗margin, padding - Use transform or opacity

Related Components

Resources

Key Takeaways

  • ✅ Use 150ms for instant feedback (hover, press)
  • ✅ Use 250ms for transitions (dismiss, modal)
  • ✅ Use 400ms for large movements (page transition)
  • ✅ Always respect prefers-reduced-motion
  • ✅ Animate transform and opacity for performance
  • ✅ Keep animations purposeful and subtle