# LESSON-13｜CSS 進階：Transition、動畫與樣式整理

> 對象：完全沒有前端概念的入門者
>
> 時間：3 小時
>
> **本堂專屬焦點：**用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋

## 1. 課程目標

完成一個 390px 不溢出的可操作示範：按鈕與服務卡片在 hover、移出、Tab focus 時有清楚但短暫的變化；animation 與 transition 能分辨；reduced motion 下移除非必要位移與動畫，focus 仍清楚。

## 2. 任務情境

服務型網站的 HTML 已經能顯示內容，但按鈕和卡片的狀態切換太突然；本堂只處理 CSS 狀態變化、簡短 animation 與使用者的減少動態偏好。

## 3. 先備知識與檔案結構

本堂先備：服務型網站的 HTML 已經能顯示內容，但按鈕和卡片的狀態切換太突然；本堂只處理 CSS 狀態變化、簡短 animation 與使用者的減少動態偏好。 需要的檔案是 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion。若尚未理解這些位置，先開啟 README 的最小實驗，再開始階段練習。

### 檔案地圖

- `index.html`：首頁入口：呈現商家第一印象與主要 CTA。
- `style.css`：CSS 樣式檔：放置本堂「用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋」相關的版面規則。
- `main.js`：JavaScript 腳本：放置本堂「用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋」相關的互動邏輯。
- `examples/minimal.html`：HTML 頁面：承載本堂「用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋」所需的內容與入口。
- `.primary-button`：主要按鈕 class：標示訪客最重要的行動入口。
- `.service-card`：服務卡片 class：控制每張服務資訊卡的共用樣式。
- `@keyframes attention`：定位或條件語法：用來指定本堂要觀察的元素、狀態或 viewport（@keyframes attention）。
- `prefers-reduced-motion`：動態偏好條件：尊重使用者減少動畫的系統設定。

## 授課補強｜180 分鐘教學節奏

**本堂專屬焦點：**用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋

固定的是八段時間，變化的是本堂檔案、教師操作、學生產出與驗收證據。

| 時間 | 教學活動 | 教師交付 | 學生產出 |
| --- | --- | --- | --- |
| 0–10 分鐘 | 情境導入｜服務型網站的 HTML 已經能顯示內容，但按鈕和卡片的狀態切換太突然；本堂只處理 CSS 狀態變化、簡短 animation 與使用者的減少動態偏好。 | 展示 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion 目前的狀態，先請學生預測「完成一個 390px 不溢出的可操作示範：按鈕與服務卡片在 hover、移出、Tab focus 時有清楚但短暫的變化；animation 與 transition 能分辨；reduced motion 下移除非必要位移與動畫，focus 仍清楚。」完成後會出現什麼畫面或資料結果。 | 說出本堂問題與完成目標。；完成後：知道要從 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion 找到第一個可觀察結果。；驗收：以HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸中的第一項作為導入前後的對照證據。 |
| 10–35 分鐘 | 概念建立｜class、selector、:hover、:focus-visible、transition-property、duration、timing function、delay、transform、opacity、animation 與 prefers-reduced-motion | 用 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion 中的最小片段逐詞解釋 class、selector、:hover、:focus-visible、transition-property、duration、timing function、delay、transform、opacity、animation 與 prefers-reduced-motion，每介紹一個詞就回到瀏覽器或 DevTools 指出可觀察結果。 | 完成名詞對照表，並先回答一題預測題。；完成後：能用自己的話解釋 class、selector、:hover、:focus-visible、transition-property、duration、timing function、delay、transform、opacity、animation 與 prefers-reduced-motion。；驗收：口頭回答一題預測題，並指出HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸中對應的Elements、Styles、Computed、Tab 與 Rendering證據。 |
| 35–55 分鐘 | 教師示範｜先在 index.html 找到按鈕與 service-card，再用 Styles／Computed 比較初始狀態、hover、鍵盤 focus、transition 與 animation；最後用 Rendering 切換 reduced motion。 | 從最小可執行範例開始，逐次修改 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion；每次只改一個檢查點，先預測再重新整理或操作。 | 跟著完成一次修改，記下修改前後差異。；完成後：看到 HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸 中至少一項可觀察結果。；驗收：保留一次修改前後畫面，並記下HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸中至少兩項觀察結果。 |
| 55–95 分鐘 | 學生跟做｜完成一個 390px 不溢出的可操作示範：按鈕與服務卡片在 hover、移出、Tab focus 時有清楚但短暫的變化；animation 與 transition 能分辨；reduced motion 下移除非必要位移與動畫，focus 仍清楚。 | 將示範拆成 10–15 分鐘一個小檢查點，巡迴確認學生真的修改到 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion，卡住時先要求說出症狀與證據。 | 在自己的專案完成：完成一個 390px 不溢出的可操作示範：按鈕與服務卡片在 hover、移出、Tab focus 時有清楚但短暫的變化；animation 與 transition 能分辨；reduced motion 下移除非必要位移與動畫，focus 仍清楚。；完成後：核心成果能在自己的專案重現，而不是只在講師畫面成功。；驗收：提交核心成果、修改檔案清單，以及HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸中可重現的至少兩項證據。 |
| 95–110 分鐘 | 觀察與除錯｜故意把 transition 寫在 .primary-button:hover，讓移入會動、移出瞬間跳回；學生要從 Styles 找到位置，將 transition 移到一般 selector。 | 故意重現「故意把 transition 寫在 .primary-button:hover，讓移入會動、移出瞬間跳回；學生要從 Styles 找到位置，將 transition 移到一般 selector。」，示範從症狀、證據、假設、單一修正到重新驗證的除錯紀錄。 | 重現一個錯誤，再只修改一處並重新驗證。；完成後：能寫出症狀、原因、修正與修正後結果。；驗收：留下錯誤狀態與修正後狀態的對照，證明HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸中的錯誤已消失。 |
| 110–155 分鐘 | 情境變體與獨立挑戰｜把服務卡片文字換成自己的商家，保留 .primary-button、.service-card、:focus-visible 與 reduced-motion 的責任；只能改內容和少量色彩，不新增框架或 JavaScript 驗證器。 | 先圈出 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion 中不變的技術結構，再示範如何完成「把服務卡片文字換成自己的商家，保留 .primary-button、.service-card、:focus-visible 與 reduced-motion 的責任；只能改內容和少量色彩，不新增框架或 JavaScript 驗證器。」，最後請學生獨立完成同一變體。 | 完成變體：把服務卡片文字換成自己的商家，保留 .primary-button、.service-card、:focus-visible 與 reduced-motion 的責任；只能改內容和少量色彩，不新增框架或 JavaScript 驗證器。；完成後：能說明哪些結構保留、哪些內容替換，且沒有引入超出本堂範圍的技術。；驗收：將變體結果與原始結果並列，附上檔案或操作位置與一次重新驗證紀錄。 |
| 155–175 分鐘 | 驗收、展示與討論｜HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸 | 依 HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸 逐項抽查學生的Elements、Styles、Computed、Tab 與 Rendering與檔案，請學生先展示證據，再討論不同解法。 | 展示：HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸；完成後：能回答「如果驗收失敗，下一個要查什麼？」。；驗收：留下可重現的驗收包：結果截圖或網址、檔案位置、檢查步驟與實際觀察。 |
| 175–180 分鐘 | 重點回顧與下堂銜接｜下一堂會用 JavaScript 函式，在穩定的 HTML/CSS 上加入可測試的行為。 | 用「概念、操作、證據」三問回看 index.html、style.css、main.js、examples/minimal.html、.primary-button、.service-card、@keyframes attention、prefers-reduced-motion 的成果，再說明它如何接到「下一堂會用 JavaScript 函式，在穩定的 HTML/CSS 上加入可測試的行為。」。 | 寫下三句回顧：我理解了什麼、我做出了什麼、我用什麼證據確認；再預測下一堂會修改哪個檔案。；完成後：能用一句話說明本堂成果如何銜接：下一堂會用 JavaScript 函式，在穩定的 HTML/CSS 上加入可測試的行為。；驗收：講師抽問一位學生重述下一堂會用 JavaScript 函式，在穩定的 HTML/CSS 上加入可測試的行為。，並收回一項本堂驗收證據。 |

## 4. 觀念拆解

本堂第一次出現的詞，先從「白話 → 正式定義 → 何時使用 → 最小例子 → 可觀察證據 → 混淆概念」建立完整理解。

### class（class attribute）

- **白話：**給一個或多個 HTML 元素貼上的可重用名稱。
- **正式定義：**HTML 元素上的 class attribute 會成為 CSS selector 的匹配依據；同一個 class 可以被多個元素共用。
- **使用時機：**要讓按鈕與服務卡片各自套用一組可重用樣式時。
- **最小範例：**`<button class="primary-button">查看方案</button>`。
- **觀察證據：**Elements 能看到 class 名稱；Styles 能顯示 `.primary-button` 是否命中。
- **容易混淆：**和 id 混淆；class 可以共用，id 通常只識別一個元素。

### selector（CSS selector）

- **白話：**告訴 CSS 要把規則套用到誰。
- **正式定義：**CSS 規則中用來匹配文件元素的模式；本堂先使用 class selector 與 pseudo-class selector。
- **使用時機：**要讓 `.primary-button` 或 `.service-card` 套用規則時。
- **最小範例：**`.service-card { padding: 1rem; }` 會選取所有 service-card 元素。
- **觀察證據：**Elements 的 Styles 顯示規則命中；沒有命中時會看不到預期宣告。
- **容易混淆：**和 property 混淆；selector 決定對誰作用，property 決定改什麼。

### 狀態（state）

- **白話：**同一個元素在不同情況下的樣子，例如平常、滑入或取得焦點。
- **正式定義：**由使用者互動或文件條件造成、可被 CSS selector 表達的元素呈現狀態。
- **使用時機：**要比較按鈕尚未操作、滑鼠移入、移出與鍵盤 focus 時。
- **最小範例：**同一顆按鈕可以有一般狀態與 `.primary-button:hover` 狀態。
- **觀察證據：**先在畫面操作，再在 Styles 看到目前匹配的規則。
- **容易混淆：**和元素本身混淆；元素沒有換掉，只是目前狀態改變。

### :hover（hover pseudo-class）

- **白話：**滑鼠游標移到元素上時的狀態。
- **正式定義：**CSS pseudo-class，用來匹配使用者指標目前懸停的元素。
- **使用時機：**要提供滑鼠移入時的顏色、陰影或輕微位移時。
- **最小範例：**`.primary-button:hover { transform: translateY(-2px); }`。
- **觀察證據：**滑鼠移入與移出按鈕時，畫面在兩個狀態間變化。
- **容易混淆：**和 `:focus-visible` 混淆；hover 依賴指標，鍵盤使用者不一定會觸發它。

### :focus-visible（focus-visible pseudo-class）

- **白話：**鍵盤 Tab 走到元素時，顯示目前操作位置的狀態。
- **正式定義：**CSS pseudo-class，用來匹配瀏覽器判斷應顯示可見鍵盤焦點指示的元素。
- **使用時機：**按鈕、連結或表單需要讓鍵盤使用者知道目前位置時。
- **最小範例：**`.primary-button:focus-visible { outline: 3px solid #f3b63f; }`。
- **觀察證據：**不用滑鼠，按 Tab 走到按鈕時仍看得到清楚的 outline。
- **容易混淆：**和 `:hover` 混淆；focus-visible 是鍵盤操作的驗收入口，不是滑鼠裝飾。

### transition（CSS transition）

- **白話：**讓狀態改變在一小段時間內平順地從 A 走到 B。
- **正式定義：**當可動畫的 CSS property 值因狀態改變而不同時，瀏覽器在兩個值之間計算中間畫面的效果。
- **使用時機：**按鈕 hover、focus 或卡片 focus-within 需要短暫視覺回饋時。
- **最小範例：**`transition: transform 200ms ease;` 要寫在一般 selector，而不是只寫在 hover。
- **觀察證據：**Computed 能看到 transition；移入與移出都能觀察到變化。
- **容易混淆：**和 animation 混淆；transition 需要狀態前後值，animation 可依 keyframes 自己播放。

### transition-property（transition-property）

- **白話：**指定哪些 CSS property 要平順改變。
- **正式定義：**transition shorthand 中描述要套用過渡效果的 property 清單。
- **使用時機：**只想讓 transform、background-color 或 box-shadow 改變，而不想讓所有屬性都動時。
- **最小範例：**`transition-property: transform, box-shadow;`。
- **觀察證據：**Computed 或 Styles 顯示實際 transition 的 property，而不是 `all`。
- **容易混淆：**和 property 一般概念混淆；它是 transition 要追蹤的 property，不是要被選取的 selector。

### duration（transition-duration）

- **白話：**指定過渡要花多久時間。
- **正式定義：**transition-duration 設定 CSS property 從起點到終點完成變化所需的時間。
- **使用時機：**要讓互動短暫、可感覺但不拖慢操作時。
- **最小範例：**`transition-duration: 200ms;`。
- **觀察證據：**Computed 能看到 200ms；移動滑鼠離開時不會延遲很久。
- **容易混淆：**和 delay 混淆；duration 是變化本身多久，delay 是多久後才開始。

### timing function（transition-timing-function）

- **白話：**決定變化速度是平均、先快後慢，或其他節奏。
- **正式定義：**transition-timing-function 定義時間進度如何映射到 property 值進度的函式。
- **使用時機：**要讓短動畫的速度感自然，而且能在 Computed 讀出使用的節奏時。
- **最小範例：**`transition-timing-function: ease;`。
- **觀察證據：**Computed 顯示 `ease`，且 200ms 內的變化不是突然跳到終點。
- **容易混淆：**和 duration 混淆；timing function 改變速度曲線，duration 改變總時間。

### delay（transition-delay）

- **白話：**指定過渡開始前要等多久。
- **正式定義：**transition-delay 設定 property 值變更後，瀏覽器等待多久才開始過渡。
- **使用時機：**只有在確實需要錯開時才使用；本堂示範讀懂它，不把互動故意拖慢。
- **最小範例：**`transition-delay: 0ms;` 表示不額外等待。
- **觀察證據：**Computed 能看到 delay；按 Tab 後 focus outline 不會被不必要地延遲。
- **容易混淆：**和 duration 混淆；delay 在開始前等待，duration 是開始後持續多久。

### transform（CSS transform）

- **白話：**改變元素的幾何呈現，例如上下移動，但不直接改文件流位置。
- **正式定義：**以矩陣或函式對元素進行平移、旋轉、縮放等視覺幾何轉換的 CSS property。
- **使用時機：**要做小幅度、非必要的 hover 或 focus 回饋時。
- **最小範例：**`transform: translateY(-2px);`。
- **觀察證據：**畫面看到卡片或按鈕微微上移；reduced motion 時這個位移被移除。
- **容易混淆：**和改變文件排列的版面規則混淆；transform 是視覺轉換，本堂只用它做小幅度回饋。

### animation／@keyframes（CSS animation and keyframes）

- **白話：**讓元素依一組時間點自行播放一段動畫。
- **正式定義：**animation 使用 @keyframes 定義的起點、終點或中間影格，依時間與播放設定改變 property。
- **使用時機：**要和需要狀態前後值的 transition 做小型比較時。
- **最小範例：**`@keyframes attention { from { opacity: .4; } to { opacity: 1; } }`。
- **觀察證據：**Styles 能看到 animation，推薦標籤只在載入時播放一次；reduced motion 時不播放。
- **容易混淆：**和 transition 混淆；animation 不需要 hover 才能依 keyframes 播放。

### prefers-reduced-motion（prefers-reduced-motion media feature）

- **白話：**讀取使用者希望減少畫面動態的系統偏好。
- **正式定義：**CSS media feature，讓作者依使用者代理程式提供的減少動態偏好，移除、縮短或替換非必要 motion。
- **使用時機：**頁面有 transform、transition 或 animation，又要讓使用者選擇較穩定的畫面時。
- **最小範例：**`@media (prefers-reduced-motion: reduce) { .service-card__tag { animation: none; } }`。
- **觀察證據：**Rendering 切換 reduce 後 animation 不播放、位移被移除，Tab focus outline 仍存在。
- **容易混淆：**和完全禁止所有視覺變化混淆；顏色或 focus 仍可保留，只處理非必要動態。

## 5. Live Lab

### 最小可執行實驗

- **入口：**`examples/minimal.html`
- **操作：**開啟 examples/minimal.html，先移入和移出卡片與按鈕，再只用 Tab 操作；最後在 DevTools Rendering 切換 prefers-reduced-motion。
- **預期結果：**正常偏好下看到按鈕／卡片的短暫位移與陰影、推薦標籤的短 animation；reduce 下位移與 animation 被移除，Tab focus 仍清楚，390px 沒有水平捲軸。
- **重設方式：**重新整理 examples/minimal.html；切換偏好後重新整理，再回到預設偏好做一次對照。
- **第一個檢查位置：**先在 Elements 找到 .primary-button 與 .service-card，再用 Styles 確認 selector 命中，不要先改 CSS。

### Step 1：先把 HTML class 對到 CSS selector

- **教學目的：**先建立元素、class 與 selector 的最小對應，避免把 CSS 貼到不存在的元素上。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**index.html 的 #action 與 .service-card
- **修改前：**#action 是一般 button，兩張 article 已有 service-card；尚未把主要按鈕標成 primary-button。
- **本次只修改：**只替 #action 加上 class="primary-button"，保留 id、type 與按鈕文字。
- **程式碼或操作：**

  ```text
  <button id="action" class="primary-button" type="button">查看推薦方案</button>
  ```
- **預期結果：**Elements 中按鈕的 class 變成 primary-button；畫面結構不變，現在可以開始讓 CSS 精準命中它。
- **學生可能的錯誤答案：**把 class 寫成 primary_button，或把 primary-button 加到外層 section，導致 .primary-button 找不到按鈕。
- **講師追問：**如果 Styles 沒有命中 .primary-button，你會先查 HTML 的哪一個屬性？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**Elements 搜尋 .primary-button 與 .service-card，確認按鈕為 1 個、服務卡片為 2 個。
- **卡住時的單一檢查：**只在 Elements 點擊按鈕，逐字比對 class 是否為 primary-button。

### Step 2：先在一般 selector 建立 transition

- **教學目的：**先只處理狀態前後值的平順變化，不把 transition 寫進 hover 或 focus 狀態。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**style.css 的 .primary-button 與 .service-card
- **修改前：**按鈕與卡片已有顏色、邊框與 focus outline，但狀態改變會瞬間跳轉。
- **本次只修改：**在一般 selector 寫入指定 property、duration 與 timing function；hover、focus-visible、focus-within 只寫狀態改變後的值。
- **程式碼或操作：**

  ```text
  .primary-button {
    transition: transform 200ms ease, background-color 200ms ease, box-shadow 200ms ease;
  }
  
  .service-card {
    transition: transform 200ms ease, box-shadow 200ms ease;
  }
  ```
- **預期結果：**滑鼠移入與移出按鈕、按 Tab 到按鈕、移入卡片時，變化都短暫且可辨識；此步先不期待 keyframes animation。
- **學生可能的錯誤答案：**只在 .primary-button:hover 寫 transition，或寫 transition: all 2s，造成移出瞬間跳回或不必要的延遲。
- **講師追問：**transition 應該放在一般狀態還是 hover 狀態？如果放錯，移出時會發生什麼？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**Styles 命中一般 .primary-button 與 .service-card；Computed 可讀到 transition-property、duration、timing-function；Tab focus 仍有 outline。
- **卡住時的單一檢查：**只在 Styles 搜尋 .primary-button 的一般規則，確認 transition 是否位於狀態規則之外。

### Step 3：拆解 transition 的四個組成

- **教學目的：**把 property、duration、timing function 與 delay 分開讀，避免只背一整串 shorthand。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**style.css 的 .primary-button transition 宣告
- **修改前：**一般 selector 已有 transition shorthand，但尚未把每個組成和 Computed 對照。
- **本次只修改：**逐一指出 transition-property、transition-duration、transition-timing-function 與 transition-delay 在 shorthand 中的責任。
- **程式碼或操作：**

  ```text
  transition-property: transform, background-color, box-shadow;
  transition-duration: 200ms;
  transition-timing-function: ease;
  transition-delay: 0ms;
  ```
- **預期結果：**學生能指出三個 property、總時間、速度曲線與等待時間，並在 Computed 讀到目前值。
- **學生可能的錯誤答案：**把 duration 當成 delay，或把 `all` 當成最精準的 property 清單。
- **講師追問：**如果 delay 是 500ms、duration 是 200ms，按鈕總共何時開始、何時結束？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**Computed 看到 transform／background-color／box-shadow、200ms、ease 與 0s；不需要把 Console 當成 CSS 證據。
- **卡住時的單一檢查：**只看 .primary-button 的 transition 那一行，先圈出 property、時間、速度與等待四個部分。

### Step 4：用 @keyframes 比較 animation

- **教學目的：**讓學生看見 animation 可以依 keyframes 自己播放，不需要 hover 才有前後狀態。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**style.css 的 @keyframes attention 與 .service-card__tag
- **修改前：**transition 已經完成，但推薦標籤還沒有自己的 animation。
- **本次只修改：**建立 attention 的 from／to 影格，並只讓推薦標籤播放一次短動畫。
- **程式碼或操作：**

  ```text
  @keyframes attention {
    from { opacity: .4; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .service-card__tag { animation: attention 900ms ease both; }
  ```
- **預期結果：**重新整理時推薦標籤播放一次短 animation；hover／focus 的按鈕與卡片仍由 transition 負責。
- **學生可能的錯誤答案：**把 animation 寫到 hover 裡，或用 animation 取代所有狀態 transition。
- **講師追問：**哪一個效果需要 hover 才觸發？哪一個效果在載入時就能依 keyframes 播放？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**Styles 看到 @keyframes 與 animation；重新整理後觀察標籤，並比較按鈕 transition 的 Computed。
- **卡住時的單一檢查：**只搜尋 @keyframes attention，確認影格名稱和 animation 宣告使用同一個名稱。

### Step 5：用 reduced motion 提供替代呈現

- **教學目的：**理解 reduced motion 不是把所有樣式刪光，而是移除、縮短或替換非必要動態。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**style.css 的 @media (prefers-reduced-motion: reduce)
- **修改前：**正常偏好下有按鈕／卡片 transform 與推薦標籤 animation，尚未提供使用者偏好的替代規則。
- **本次只修改：**在 reduce 條件中縮短按鈕背景色 transition、移除卡片 transition、停止 keyframes，並取消 hover／focus 的位移；保留 focus-visible outline。
- **程式碼或操作：**

  ```text
  @media (prefers-reduced-motion: reduce) {
    .primary-button { transition: background-color .01ms linear; }
    .service-card { transition: none; }
    .service-card__tag { animation: none; }
    .primary-button:hover,
    .primary-button:focus-visible,
    .service-card:hover,
    .service-card:focus-within { transform: none; box-shadow: none; }
  }
  ```
- **預期結果：**正常與 reduce 的差異可被看見：reduce 不再有非必要位移或載入 animation，但按 Tab 仍看得到清楚 focus ring。
- **學生可能的錯誤答案：**用全域 !important 把所有 transition、outline 和 animation 一起關掉，讓鍵盤使用者失去位置提示。
- **講師追問：**切換 reduce 後，哪個動態被移除？哪個 focus 證據必須留下？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**DevTools Rendering 切換 prefers-reduced-motion；比較正常／reduce 的 Computed、畫面位移與 Tab focus。
- **卡住時的單一檢查：**只先確認 Rendering 的 reduce 設定是否真的開啟，再重新整理一次。

### Step 6：修正 transition 寫錯位置的真實缺口

- **教學目的：**從可重現的移入／移出差異，理解 transition 必須放在共同存在的狀態基底。
- **講師白話解釋：**先請學生說出預測，再說明這一步只處理一個責任。
- **精確檔案與區塊：**checkpoint/files/style.css 的 .primary-button:hover
- **修改前：**Checkpoint 故意把 transition 寫在 .primary-button:hover；移入有動畫，移出會瞬間跳回。
- **本次只修改：**把 transition 移到 .primary-button 一般 selector，讓 hover／focus-visible 只留下 transform、background-color 與 box-shadow 的終點值。
- **程式碼或操作：**

  ```text
  .primary-button {
    transition: transform 200ms ease, background-color 200ms ease, box-shadow 200ms ease;
  }
  
  .primary-button:hover,
  .primary-button:focus-visible {
    transform: translateY(-2px);
  }
  ```
- **預期結果：**滑入和移出都平順；Computed 在一般按鈕狀態也能看到 transition 設定。
- **學生可能的錯誤答案：**只把 200ms 改成 100ms，或只補 hover 的 transform，沒有移動 transition 的宣告位置。
- **講師追問：**移出時狀態規則不再匹配，瀏覽器要從哪裡讀回 transition？
- **Elements／Styles／Computed／Tab／Rendering 驗收：**記錄修正前移入／移出、Styles 的宣告位置與修正後 Computed 的 property／duration／timing-function。
- **卡住時的單一檢查：**只查看 .primary-button:hover 區塊，確認裡面沒有 transition 宣告。

### Live Lab 與階段任務對照

- Step 1 對應 **starter-1**：index.html｜#action；學生停點：Elements 中的按鈕有 primary-button，兩張卡片都有 service-card；此時尚未期待動畫。；證據：Elements 搜尋 .primary-button 與 .service-card，數量和 HTML 結構一致。
- Step 2 對應 **starter-2**：style.css｜.primary-button、.service-card 與 @keyframes attention；學生停點：按鈕和卡片的 hover、focus-within 有平順變化；推薦標籤只播放一次短 animation。；證據：hover 進入與移出、Tab focus、Styles 命中；Computed 看到 transform、background-color、box-shadow 的 transition。
- Step 3 對應 **starter-2**：style.css｜.primary-button、.service-card 與 @keyframes attention；學生停點：按鈕和卡片的 hover、focus-within 有平順變化；推薦標籤只播放一次短 animation。；證據：hover 進入與移出、Tab focus、Styles 命中；Computed 看到 transform、background-color、box-shadow 的 transition。
- Step 4 對應 **starter-2**：style.css｜.primary-button、.service-card 與 @keyframes attention；學生停點：按鈕和卡片的 hover、focus-within 有平順變化；推薦標籤只播放一次短 animation。；證據：hover 進入與移出、Tab focus、Styles 命中；Computed 看到 transform、background-color、box-shadow 的 transition。
- Step 5 對應 **starter-3**：style.css｜@media (prefers-reduced-motion: reduce)；學生停點：切換 reduce 後 transform 位移不再出現、animation 不播放或被縮短，outline 仍清楚。；證據：Rendering 的 prefers-reduced-motion、Computed 的 transition 與 Tab focus 對照正常／reduce 兩種狀態。
- Step 6 對應 **checkpoint-1**：style.css｜.primary-button:hover；學生停點：移入和移出都平順；一般 selector 的 Computed 可讀到 transition，狀態 selector 只保留變化後的值。；證據：先觀察移入／移出差異，再在 Styles 找到 transition 的實際位置，最後用 Computed 檢查 property、duration、timing function。

## 6. 觀察與除錯

教師先示範故意錯誤，再要求學生依序寫下「症狀 → 證據 → 假設 → 單一修正 → 重新驗證」。本堂常見錯誤是：故意把 transition 寫在 .primary-button:hover，讓移入會動、移出瞬間跳回；學生要從 Styles 找到位置，將 transition 移到一般 selector。

## 7. 學生任務

### 教師完整示範

示範 先在 index.html 找到按鈕與 service-card，再用 Styles／Computed 比較初始狀態、hover、鍵盤 focus、transition 與 animation；最後用 Rendering 切換 reduced motion。；每次只修改一個檔案或區塊，先讓學生預測，再立即回到畫面、Elements、Styles、Computed、Tab、Rendering。

### 學生跟做

把教師示範拆成三到四個 10–15 分鐘微任務：先完成入口，再完成核心修改，接著重現錯誤，最後留下驗收證據。學生必須在自己的資料夾重現，不以講師畫面成功為完成。

### 情境變體

把服務卡片文字換成自己的商家，保留 .primary-button、.service-card、:focus-visible 與 reduced-motion 的責任；只能改內容和少量色彩，不新增框架或 JavaScript 驗證器。

### 獨立挑戰

不看 Solution，替另一個服務型商家完成「用 class、狀態與 transition 做出可驗收的按鈕與服務卡片回饋」，並留下 HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸 的驗收證據。

### 驗收證據

HTML class 與 CSS selector 對應、hover 進出、Tab focus、Computed 的 transition-property／duration／timing-function、正常與 reduced motion 對照、390px 無水平捲軸。學生需提交修改檔案清單、操作步驟、預期結果、實際觀察與一項可重現證據。

### 離場前回顧

請學生回答：我理解了什麼？我做出了什麼？我用什麼證據確認？下一堂會接著處理什麼？

下堂銜接：下一堂會用 JavaScript 函式，在穩定的 HTML/CSS 上加入可測試的行為。

### 官方來源補充

- [MDN：transition property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/transition)
- [MDN：Using CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Transitions/Using)
- [MDN：CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations)
- [MDN：prefers-reduced-motion](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/%40media/prefers-reduced-motion)
