# CSS Animation Patterns

> **Scope:** universal
> **Layer:** 0 (always load)
> **Keywords:** css, animation, keyframe, transition, stagger, shimmer
> **Load When:** always

**Verified against:** CSS animations/transitions (language-level, no framework). Last-verified: 2026-05-20.

---

## Keyframes Library

```css
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes slideInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideInLeft { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes slideInRight { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes scaleIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
@keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
```

## Animation Classes

| Class | Animation | Duration | Iteration |
|-------|-----------|----------|-----------|
| `.animate-fadeIn` | fadeIn | 0.3s | once |
| `.animate-fadeOut` | fadeOut | 0.3s | once |
| `.animate-slideInUp` | slideInUp | 0.5s | once |
| `.animate-slideInDown` | slideInDown | 0.5s | once |
| `.animate-slideInLeft` | slideInLeft | 0.5s | once |
| `.animate-slideInRight` | slideInRight | 0.5s | once |
| `.animate-scaleIn` | scaleIn | 0.3s | once |
| `.animate-bounce` | bounce | 1s | infinite |
| `.animate-pulse` | pulse | 2s | infinite |
| `.animate-spin` | spin | 1s | infinite |

```css
.animate-fadeIn { animation: fadeIn 0.3s ease forwards; }
.animate-slideInUp { animation: slideInUp 0.5s ease forwards; }
.animate-bounce { animation: bounce 1s ease infinite; }
```

## Stagger & Timing

```css
/* Stagger delays (0.1s - 1.0s) */
.stagger-1 { animation-delay: 0.1s; } .stagger-2 { animation-delay: 0.2s; }
.stagger-3 { animation-delay: 0.3s; } /* ... up to .stagger-10 */

/* Durations */
.duration-fast { animation-duration: 150ms; }
.duration-normal { animation-duration: 300ms; }
.duration-slow { animation-duration: 500ms; }

/* Easing */
.ease-bounce { animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
```

## Shimmer (Skeleton Loading)

```css
.shimmer {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}
```

---

## Blazor Usage

### Stagger Pattern

```razor
@for (int i = 0; i < _items.Count; i++)
{
    var index = i;
    <div class="card animate-slideInUp"
         style="@($"animation-delay: {0.05 * index}s; animation-fill-mode: backwards;")">
        @_items[index].Name
    </div>
}
```

**CRITICAL:** Use `animation-fill-mode: backwards` to avoid flash before delay.

### Conditional Enter/Exit

```razor
@if (_showMessage)
{
    <div class="@(_isEntering ? "animate-slideInUp" : "animate-fadeOut")">Msg</div>
}
@code {
    private async Task HideMessage() {
        _isEntering = false;
        await Task.Delay(300); // wait for exit animation
        _showMessage = false;
    }
}
```

### Skeleton Loading

```razor
@if (_isLoading)
{
    <div class="shimmer" style="width: 200px; height: 24px; border-radius: 4px;"></div>
}
else { <h2>@_title</h2> }
```

## Transitions (Hover/Focus)

```css
.card { transition: transform 0.2s ease, box-shadow 0.2s ease; }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }

.button { transition: background-color 0.15s ease, transform 0.15s ease; }
.button:active { transform: scale(0.98); }

.link::after { content: ''; position: absolute; width: 0; height: 2px; background: var(--primary); transition: width 0.3s ease; }
.link:hover::after { width: 100%; }
```

## Accessibility

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

## Checklist

- [ ] Keyframes in design-system.css
- [ ] `.animate-*` classes created
- [ ] `animation-fill-mode: backwards` for stagger
- [ ] `@media (prefers-reduced-motion)` implemented
- [ ] Shimmer for loading states

## JavaScript Animation Libraries

For animations beyond CSS capabilities:

| Need | Library | Standard |
|------|---------|----------|
| Simple UI transitions (enter/exit, layout, gestures) | Motion (formerly Framer Motion) | `frontend/nextjs/motion-patterns.md` |
| Complex animations (ScrollTrigger, SVG morph, timelines) | GSAP | `frontend/gsap/gsap-core.md` |

---

*MORPH-SPEC by Polymorphism Tech*
