libra/docs/stories/story-9.11-animations-micro...

6.0 KiB

Story 9.11: Animations & Micro-interactions

Epic Reference

Epic 9: Design & Branding Implementation

Dependencies

  • Story 9.4 (Buttons): Button components must be styled before adding transitions
  • Story 9.5 (Forms): Form components must be complete for error shake animations
  • Story 9.6 (Cards): Card components must be styled before adding hover/lift effects
  • Story 9.7 (Navigation): Navigation must be complete for link transitions
  • Story 9.10 (Accessibility): Coordinates with prefers-reduced-motion requirements

User Story

As a user, I want subtle, professional animations, So that the interface feels polished and responsive.

Acceptance Criteria

Transitions

  • Button hover: 150ms ease
  • Card hover: 200ms ease
  • Modal open/close: 200ms
  • Page transitions (optional)

Loading States

  • Skeleton loaders for content
  • Spinner for actions
  • Progress indicators

Feedback Animations

  • Success checkmark
  • Error shake
  • Toast slide-in

Hover Effects

  • Links: Color transition
  • Cards: Lift effect
  • Buttons: Background transition

Requirements

  • All animations subtle, professional
  • Under 300ms duration
  • Respect prefers-reduced-motion

Technical Notes

Files to Create/Modify

File Action Purpose
resources/css/app.css Modify Add animation utility classes and keyframes
resources/views/components/skeleton.blade.php Create Skeleton loader component
resources/views/components/spinner.blade.php Create Loading spinner component
resources/views/components/toast.blade.php Create Toast notification with slide-in animation
resources/views/components/icons/checkmark.blade.php Create Animated success checkmark SVG

Component Integration Points

  • Buttons (<flux:button>): Add transition-colors duration-150 to existing button styles
  • Cards: Apply .card-hover class to interactive card components
  • Forms: Use .shake class on form fields when validation fails (triggered via Alpine.js)
  • Livewire Loading: Use <x-spinner /> component with wire:loading directive
  • Toast Notifications: Integrate with Livewire event dispatch for flash messages

Animation CSS

/* Base transitions */
.transition-default {
  @apply transition-all duration-150 ease-in-out;
}

.transition-slow {
  @apply transition-all duration-200 ease-in-out;
}

/* Button hover */
.btn {
  @apply transition-colors duration-150;
}

/* Card lift */
.card-hover {
  @apply transition-all duration-200;
}
.card-hover:hover {
  @apply -translate-y-0.5 shadow-md;
}

/* Skeleton loader */
.skeleton {
  @apply animate-pulse bg-charcoal/10 rounded;
}

/* Toast animation */
.toast-enter {
  @apply transform translate-x-full opacity-0;
}
.toast-enter-active {
  @apply transform translate-x-0 opacity-100 transition-all duration-200;
}

/* Success checkmark */
@keyframes checkmark {
  0% { stroke-dashoffset: 100; }
  100% { stroke-dashoffset: 0; }
}

.checkmark-animated path {
  stroke-dasharray: 100;
  animation: checkmark 0.3s ease-in-out forwards;
}

/* Error shake */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

.shake {
  animation: shake 0.3s ease-in-out;
}

/* Reduced motion support - REQUIRED for accessibility */
@media (prefers-reduced-motion: reduce) {
  .transition-default,
  .transition-slow,
  .card-hover,
  .btn {
    transition: none !important;
  }

  .skeleton {
    animation: none !important;
  }

  .checkmark-animated path,
  .shake {
    animation: none !important;
  }

  .toast-enter-active {
    transition: none !important;
  }
}
<!-- Skeleton loader component -->
@props(['lines' => 3])

<div class="space-y-3">
    @for($i = 0; $i < $lines; $i++)
        <div class="skeleton h-4 {{ $i === $lines - 1 ? 'w-2/3' : 'w-full' }}"></div>
    @endfor
</div>

<!-- Loading spinner -->
<div wire:loading class="flex items-center gap-2">
    <svg class="animate-spin h-5 w-5 text-gold" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
    </svg>
    <span>{{ __('common.loading') }}</span>
</div>

Testing Requirements

Testing Approach

  • Visual Inspection: Manual browser testing for animation smoothness and timing
  • Pest Browser Tests: Automated tests to verify animation classes are applied
  • Cross-Browser Testing: Chrome, Firefox, Safari (animations can vary)
  • Reduced Motion Testing: Verify animations disabled when preference is set

Key Test Scenarios

Scenario Expected Result
Hover over button Background color transitions smoothly (150ms)
Hover over card Card lifts slightly with shadow increase (200ms)
Form validation fails Input field shakes briefly
Content loading Skeleton pulses until content loads
Action in progress Spinner displays with wire:loading
Toast notification triggered Toast slides in from right edge
prefers-reduced-motion: reduce enabled All animations/transitions disabled

Pest Browser Test Example

// tests/Browser/AnimationsTest.php
it('applies hover transition to buttons', function () {
    visit('/login')
        ->assertPresent('button.transition-colors');
});

it('respects reduced motion preference', function () {
    visit('/')
        ->withReducedMotion()
        ->assertStyleContains('.btn', 'transition', 'none');
});

Definition of Done

  • Button transitions work
  • Card hover effects work
  • Skeleton loaders work
  • Spinners work
  • Toast animations work
  • All animations subtle
  • Reduced motion respected
  • Pest browser tests pass
  • Cross-browser tested (Chrome, Firefox, Safari)

Estimation

Complexity: Medium | Effort: 4 hours