# 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

```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;
  }
}
```

```blade
<!-- 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

```php
// 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
