# ✅ Checklist Kiểm Tra Micro Animations cho Website

> **Hướng dẫn sử dụng:** Review từng mục trước khi deploy. Đánh dấu `[x]` khi đã pass. Mục nào fail → ghi note và fix trước khi ship.

---

## 📋 Mục Lục

- [Phần 1: Mục Đích Animation](#-phần-1-mục-đích-animation)
- [Phần 2: Performance Check](#-phần-2-performance-check)
- [Phần 3: Accessibility](#-phần-3-accessibility)
- [Phần 4: Mobile Test](#-phần-4-mobile-test)
- [Phần 5: Over-Animation Audit](#-phần-5-over-animation-audit)

---

## 🎯 Phần 1: Mục Đích Animation

> **Nguyên tắc:** Mỗi animation phải có lý do tồn tại rõ ràng. Nếu không trả lời được các câu hỏi dưới đây → cân nhắc bỏ animation đó.

### 1.1 Self-Review Questions

Với **mỗi animation** trên trang, tự hỏi và trả lời trước khi check:

- [ ] **Animation này giải quyết vấn đề gì cho user?**
  - Ví dụ tốt: Chỉ dẫn hướng chú ý → button CTA pulse nhẹ
  - Ví dụ xấu: "Trông đẹp hơn" → không phải lý do đủ mạnh

- [ ] **User có hiểu UI state thay đổi nhờ animation này không?**
  - Toggle switch animation → user biết đang ON/OFF
  - Loading spinner → user biết hệ thống đang xử lý
  - Shake effect → user biết input bị lỗi

- [ ] **Nếu xóa animation này đi, UX có bị ảnh hưởng tiêu cực không?**
  - Nếu câu trả lời là "Không" → animation đó là decorative, không cần thiết
  - Nếu câu trả lời là "Có" → animation đó là functional, cần giữ

- [ ] **Animation này có phù hợp với brand tone và context không?**
  - App ngân hàng / y tế → animation tối giản, nghiêm túc
  - App game / creative → animation có thể expressive hơn
  - Animation vui nhộn trên trang thông báo lỗi nghiêm trọng → sai context

### 1.2 Notes

```
Animation cần review lại:
- [ ] _______________________________________________
- [ ] _______________________________________________
- [ ] _______________________________________________
```

---

## ⚡ Phần 2: Performance Check

> **Mục tiêu:** Đạt 60fps trên thiết bị trung bình. Animation không được block main thread.

### 2.1 Duration & Timing

- [ ] **Micro animations có duration từ 100ms – 500ms**
  - `< 100ms` → quá nhanh, user không nhận ra
  - `100ms – 300ms` → feedback ngay lập tức (hover, click, toggle)
  - `300ms – 500ms` → transition màn hình, modal open/close
  - `> 500ms` → chỉ dùng cho onboarding / hero animation có chủ đích

- [ ] **Không dùng `linear` cho animation tương tác với user**
  - Dùng `ease-out` cho element xuất hiện (cảm giác nhanh, responsive)
  - Dùng `ease-in` cho element biến mất
  - Dùng `ease-in-out` cho element di chuyển từ A → B
  - Tránh `linear` → cảm giác cơ học, thiếu tự nhiên

- [ ] **Delay animation không quá 100ms cho tương tác trực tiếp**
  - Hover effects: delay `0ms` hoặc `50ms` tối đa
  - Staggered list items: delay tăng dần không quá `50ms` mỗi item
  - Tổng thời gian stagger không vượt quá `600ms`

### 2.2 Composite Properties (GPU-Friendly)

- [ ] **Chỉ animate các CSS properties sau (composite layer):**

  | Property | ✅ Dùng được | Ghi chú |
  |----------|-------------|---------|
  | `transform` | ✅ | translate, scale, rotate, skew |
  | `opacity` | ✅ | fade in/out |
  | `filter` | ⚠️ | Dùng hạn chế, tốn GPU |
  | `clip-path` | ⚠️ | Test kỹ trên mobile |

- [ ] **Tránh animate các properties sau (trigger layout/paint):**

  | Property | ❌ Không animate | Thay thế bằng |
  |----------|-----------------|--------------|
  | `width` / `height` | ❌ | `transform: scaleX/scaleY` |
  | `top` / `left` / `right` / `bottom` | ❌ | `transform: translate()` |
  | `margin` / `padding` | ❌ | `transform: translate()` |
  | `background-color` | ⚠️ | Dùng `opacity` overlay trick |
  | `font-size` | ❌ | `transform: scale()` |
  | `box-shadow` | ⚠️ | Dùng `filter: drop-shadow` hoặc `opacity` trên pseudo-element |

- [ ] **Dùng `will-change` đúng cách**
  - [ ] Chỉ áp dụng cho element sắp animate, không dùng globally
  - [ ] Xóa `will-change` sau khi animation kết thúc (nếu dùng JS)
  - [ ] Không khai báo `will-change: all` → tốn bộ nhớ vô ích

  ```css
  /* ✅ Đúng */
  .button:hover {
    will-change: transform;
  }

  /* ❌ Sai */
  * {
    will-change: transform, opacity;
  }
  ```

- [ ] **Dùng `transform: translateZ(0)` hoặc `transform: translate3d(0,0,0)` để force GPU layer khi cần thiết**
  - Chỉ dùng khi animation bị jank và đã xác nhận composite property không đủ
  - Không áp dụng tràn lan

### 2.3 DevTools Verification

- [ ] **Mở Chrome DevTools → Performance tab → Record animation**
  - [ ] Không có màu đỏ (Long Tasks) trong quá trình animate
  - [ ] Main thread không bị block > 50ms
  - [ ] FPS counter giữ ổn định ≥ 60fps (hoặc ≥ 30fps với 120Hz device)

- [ ] **Kiểm tra Layers panel (DevTools → More tools → Layers)**
  - [ ] Số lượng composite layers hợp lý (không quá nhiều → tốn RAM)
  - [ ] Không có layer không cần thiết bị promote

- [ ] **Kiểm tra Paint Flashing (DevTools → Rendering → Paint Flashing)**
  - [ ] Không có vùng xanh lá nhấp nháy trong quá trình animate
  - [ ] Nếu có → xem lại property đang animate

- [ ] **Kiểm tra Layout Shift (CLS)**
  - [ ] Animation không gây Cumulative Layout Shift
  - [ ] CLS score < 0.1 sau khi thêm animation

### 2.4 Notes

```
Properties cần refactor:
- [ ] _______________________________________________
- [ ] _______________________________________________
```

---

## ♿ Phần 3: Accessibility

> **Mục tiêu:** Animation không được là rào cản với người dùng có vấn đề về tiền đình, động kinh hoặc khó chịu với chuyển động.

### 3.1 prefers-reduced-motion

- [ ] **Implement `prefers-reduced-motion` media query cho toàn bộ animation**

  ```css
  /* ✅ Pattern chuẩn – đặt ở global stylesheet */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
  ```

- [ ] **Animation thiết yếu (functional) vẫn hoạt động khi reduced motion bật**
  - Loading indicator → giữ nguyên nhưng đơn giản hóa (opacity thay vì spin)
  - Progress bar → vẫn cập nhật, bỏ smooth transition
  - Toast notification → xuất hiện ngay, không slide

- [ ] **Animation thuần decorative → tắt hoàn toàn khi reduced motion bật**
  - Background parallax
  - Hover glow effects
  - Infinite loop animations
  - Page transition effects

- [ ] **Test thực tế với reduced motion enabled**
  - macOS: System Preferences → Accessibility → Display → Reduce Motion ✓
  - Windows: Settings → Ease of Access → Display → Show animations ✗
  - iOS: Settings → Accessibility → Motion → Reduce Motion ✓
  - Android: Settings → Accessibility → Remove animations ✓

- [ ] **Implement trong JavaScript (nếu dùng JS animation library)**

  ```javascript
  // ✅ Kiểm tra trước khi chạy animation
  const prefersReducedMotion = window.matchMedia(
    '(prefers-reduced-motion: reduce)'
  ).matches;

  if (!prefersReducedMotion) {
    // Chạy animation bình thường
    runAnimation();
  } else {
    // Skip animation hoặc dùng bản đơn giản hóa
    showFinalState();
  }
  ```

### 3.2 WCAG Compliance

- [ ] **WCAG 2.1 – Success Criterion 2.2.2 (Pause, Stop, Hide) – Level A**
  - [ ] Animation chạy liên tục (> 5 giây) phải có nút Pause/Stop/Hide
  - [ ] Auto-playing content có thể tạm dừng bởi user
  - Áp dụng cho: carousel tự chạy, background video, ticker/marquee

- [ ] **WCAG 2.3.1 (Three Flashes or Below Threshold) – Level A**
  - [ ] Không có element flash/nhấp nháy quá **3 lần/giây**
  - [ ] Nếu cần flash effect → kiểm tra với **Photosensitive Epilepsy Analysis Tool (PEAT)**
  - [ ] Vùng flash không chiếm quá 25% viewport

- [ ] **WCAG 2.3.3 (Animation from Interactions) – Level AAA**
  - [ ] User có thể tắt animation triggered bởi interaction (nếu không thiết yếu)
  - [ ] Cung cấp setting "Reduce animations" trong app nếu có nhiều animation phức tạp

- [ ] **Focus indicator không bị ảnh hưởng bởi animation**
  - [ ] Focus outline/ring luôn visible khi navigate bằng keyboard
  - [ ] Animation trên focused element không làm khó nhìn thấy focus state

- [ ] **Animation không gây mất context cho screen reader users**
  - [ ] Sử dụng `aria-live` region khi animation thay đổi nội dung quan trọng
  - [ ] Dùng `aria-hidden="true"` cho animation thuần decorative

  ```html
  <!-- ✅ Ví dụ: Loading state thông báo cho screen reader -->
  <div aria-live="polite" aria-atomic="true">
    <span class="sr-only">Đang tải dữ liệu...</span>
  </div>
  ```

### 3.3 Notes

```
Accessibility issues cần fix:
- [ ] _______________________________________________
- [ ] _______________________________________________
```

---

## 📱 Phần 4: Mobile Test

> **Mục tiêu:** Animation phải mượt mà trên thiết bị tầm trung (mid-range device), không chỉ trên flagship.

### 4.1 CPU Throttling Test

- [ ] **Test với CPU throttle 4x slowdown (Chrome DevTools)**
  1. DevTools → Performance tab
  2. Click ⚙️ → CPU: **4x slowdown**
  3. Record và kiểm tra animation
  - [ ] Animation vẫn chạy ≥ 30fps với 4x throttle
  - [ ] Không có jank (giật, lag) rõ ràng
  - [ ] Animation không block user interaction

- [ ] **Test với CPU throttle 6x slowdown (low-end simulation)**
  - [ ] Functional animations vẫn hoạt động đúng (có thể kém mượt hơn)
  - [ ] Không có Long Task > 200ms do animation

- [ ] **Test trên thiết bị thật (không chỉ DevTools)**
  - [ ] Test trên Android mid-range (Snapdragon 6xx hoặc tương đương)
  - [ ] Test trên iPhone SE hoặc iPhone cũ (A13 chip trở xuống)
  - [ ] Test với Chrome trên Android (không chỉ Safari iOS)

### 4.2 FPS Monitoring

- [ ] **Bật FPS meter trong DevTools (Rendering → Frame Rendering Stats)**
  - [ ] FPS trung bình ≥ 60fps trên desktop không throttle
  - [ ] FPS trung bình ≥ 30fps trên mobile throttle 4x
  - [ ] FPS không drop xuống < 20fps bất kỳ lúc nào

- [ ] **Test trên real device với FPS counter**

  ```javascript
  // ✅ Quick FPS monitor snippet (dùng trong development)
  let fps = 0;
  let lastTime = performance.now();
  let frameCount = 0;

  function measureFPS() {
    const now = performance.now();
    frameCount++;
    if (now - lastTime >= 1000) {
      fps = frameCount;
      frameCount = 0;
      lastTime = now;
      console.log(`FPS: ${fps}`);
    }
    requestAnimationFrame(measureFPS);
  }
  measureFPS();
  ```

- [ ] **Kiểm tra dropped frames**
  - [ ] Không drop quá 5% frames trong quá trình animate bình thường
  - [ ] Không drop frames khi user scroll đồng thời với animation

### 4.3 Touch & Interaction

- [ ] **Animation không delay touch response**
  - [ ] Thời gian từ touch → visual feedback < 100ms
  - [ ] Không dùng animation delay trên interactive elements

- [ ] **Scroll performance không bị ảnh hưởng**
  - [ ] Scroll vẫn mượt khi có animation đang chạy
  - [ ] Dùng `pointer-events: none` trên animation elements nếu không cần interact

- [ ] **Passive event listeners được sử dụng cho scroll-triggered animations**

  ```javascript
  // ✅ Đúng
  window.addEventListener('scroll', handleScroll, { passive: true });

  // ❌ Sai – block scroll
  window.addEventListener('scroll', handleScroll);
  ```

- [ ] **Battery & thermal check**
  - [ ] Không có infinite animation chạy khi element không trong viewport
  - [ ] Dùng `IntersectionObserver` để pause animation khi off-screen

  ```javascript
  // ✅ Pause animation khi không nhìn thấy
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.style.animationPlayState = 'running';
      } else {
        entry.target.style.animationPlayState = 'paused';
      }
    });
  });

  document.querySelectorAll('.animated-element').forEach(el => {
    observer.observe(el);
  });
  ```

### 4.4 Notes

```
Mobile issues cần fix:
- [ ] _______________________________________________
- [ ] _______________________________________________
Thiết bị đã test: ___________________________________
```

---

## 🔢 Phần 5: Over-Animation Audit

> **Nguyên tắc vàng:** Ít animation có mục đích > Nhiều animation decorative. Animation nên invisible – user cảm nhận được UX tốt hơn nhưng không để ý đến animation.

### 5.1 Đếm Số Animation

**Hướng dẫn:** Mở trang → Dùng DevTools → Animation panel để đếm

```
📊 THỐNG KÊ ANIMATION TRÊN TRANG
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Tổng số CSS animations:        [ ___ ]
Tổng số CSS transitions:       [ ___ ]
Số JS-driven animations:       [ ___ ]
Số infinite loop animations:   [ ___ ]
Số simultaneous animations
  (cùng lúc trên màn hình):    [ ___ ]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TỔNG CỘNG:                     [ ___ ]
```

### 5.2 Rule of Thumb Checklist

- [ ] **Rule 1: Không quá 3 animations chạy đồng thời trên cùng một màn hình**
  - Đếm số animation visible tại thời điểm user nhìn vào bất kỳ scroll position nào
  - Nếu > 3 → xác định cái nào ít quan trọng nhất và loại bỏ

- [ ] **Rule 2: Mỗi component chỉ có 1 animation state chính**
  - ❌ Button có: hover scale + hover glow + hover border animate + hover text color transition
  - ✅ Button có: hover scale (chính) + opacity transition cho glow (phụ, subtle)

- [ ] **Rule 3: Infinite animations phải "invisible" hoặc rất subtle**
  - Loading spinner: OK vì user đang đợi, cần indicator
  - Background gradient animation: ❌ distraction liên tục
  - Subtle pulse trên CTA: ⚠️ Cân nhắc – chỉ dùng khi thực sự cần attention

- [ ] **Rule 4: Không animate nhiều hơn 2 properties cùng lúc trên cùng element**

  ```css
  /* ❌ Quá nhiều */
  .card:hover {
    transform: translateY(-8px) scale(1.02);
    box-shadow: 0 20px 40px rgba(0,0,0,0.2);
    border-color: var(--primary);
    background: var(--surface-elevated);
    /* 4 properties thay đổi cùng lúc → overwhelming */
  }

  /* ✅ Đủ dùng */
  .card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 20px rgba(0,0,0,0.15);
    /* 2 properties → clean, perceived */
  }
  ```

- [ ] **Rule 5: Hero section không có quá 2 animated elements**
  - Hero text animate in: 1
  - Hero CTA button pulse: 2
  - Background particles + floating icons + gradient shift: ❌ quá tải

- [ ] **Rule 6: Page load animation hoàn tất trong < 1 giây**
  - [ ] Staggered entrance animations kết thúc trước 800ms
  - [ ] User có thể interact với page trước khi animation hoàn tất (nếu > 500ms)
  - [ ] Không có animation block Above-the-fold content

- [ ] **Rule 7: Không dùng animation để che giấu performance issue**
  - [ ] Loading skeleton thay thế cho spinner khi có thể
  - [ ] Optimistic UI thay cho loading state dài
  - [ ] Animation delay không được dùng để "mua thời gian" load content

### 5.3 Animation Inventory (Điền vào để audit)

| # | Animation | Element | Loại | Duration | Có mục đích rõ? | Giữ/Xóa |
|---|-----------|---------|------|----------|-----------------|---------|
| 1 | | | CSS/JS | ms | ✅/❌ | |
| 2 | | | CSS/JS | ms | ✅/❌ | |
| 3 | | | CSS/JS | ms | ✅/❌ | |
| 4 | | | CSS/JS | ms | ✅/❌ | |
| 5 | | | CSS/JS | ms | ✅/❌ | |
| 6 | | | CSS/JS | ms | ✅/❌ | |

### 5.4 Final Verdict

- [ ] **Tất cả animation trong inventory đã có "Mục đích rõ ràng" ✅**
- [ ] **Không có animation nào trong danh sách "Xóa" còn tồn tại trên trang**
- [ ] **Team design đã review và đồng ý với số lượng animation hiện tại**
- [ ] **Checklist này đã được chạy lại sau khi fix issues**

---

## 📊 Tổng Kết & Score

```
ANIMATION QUALITY SCORE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Phần 1 – Mục đích:        ___ / 4 items
Phần 2 – Performance:     ___ / 15 items
Phần 3 – Accessibility:   ___ / 12 items
Phần 4 – Mobile:          ___ / 12 items
Phần 5 – Over-animation:  ___ / 10 items
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TỔNG:                      ___ / 53 items

ĐÁNH GIÁ:
• 48–53 ✅  Ship được – Production ready
• 40–47 ⚠️  Fix issues trước khi ship
• < 40  ❌  Cần review toàn bộ animation strategy
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

### Action Items Trước Khi Ship

```
Priority HIGH (block ship):
- [ ] _______________________________________________
- [ ] _______________________________________________

Priority MEDIUM (fix trong sprint này):
- [ ] _______________________________________________
- [ ] _______________________________________________

Priority LOW (tech debt, sprint sau):
- [ ] _______________________________________________
- [ ] _______________________________________________
```

---

**Reviewer:** _________________________ | **Date:** _________ | **Version:** _______

> 💡 **Tip:** Lưu checklist này vào repository dưới dạng `.md` file. Tạo GitHub Issue template từ file này để mỗi PR có animation đều phải đính kèm checklist đã điền.