LESSON-10 · 自主延伸

內容驅動響應式與圖片

把「內容驅動響應式與圖片」拆成 4 個可獨立完成、可重設、可驗證的自主學習 module。

1. 現在要做什麼

成果:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

需要的檔案:files/starter/index.html(整合起點,保留跨 module 的最小缺口。)

完成後:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

0 / 8 個操作已記錄

2. 先備自我檢查

逐項展開;答不出來時先走補救連結,不必硬做。

已完成本章基礎練習並能找到 Starter。

自我檢查:我能否指出本章 Starter 入口與唯一 TODO?

不足時補救:回到本章 README,先完成基礎練習與入口導覽。

能開啟本機 HTML 並讀取至少一種瀏覽器證據。

自我檢查:我能否開啟 Elements、Console 或 Network 並找到本頁?

不足時補救:先開 DevTools,重新整理一次,確認目前文件與第一筆 request。

3. 完整學習目標與 Learning Map

每一個目標只對應一個 module;成功狀態必須能指出證據。

能在導覽內容第一次擁擠或換行的寬度設定 breakpoint,而不是依特定手機型號命名。

為什麼重要
裝置尺寸會變、文字也可能翻譯變長;由內容失效點決定斷點,版面才對真實內容有韌性。
完成條件
先保持導覽 nowrap,執行後切換 compact-navigation 使 flex-wrap 由 nowrap 變 wrap,並記錄真正開始擁擠的 viewport 寬度。
成功狀態
runtime 的 flex-wrap 由 nowrap 變 wrap;後續 viewport probe 能量得第一個 item rect 重疊/換行點與 media query match。
證據位置
現有 runtime before=nowrap、after=wrap、property=flex-wrap;完整證據需 firstWrapWidth、itemRects 與 mediaMatches

能用 clamp(min, preferred, max) 建立會隨 viewport 流動但不超過上下界的標題尺度。

為什麼重要
只有 vw 會在極窄頁過小、極寬頁過大;clamp 同時保留流動性與可讀邊界。
完成條件
標題初始 --heading-size:2rem,執行後改為 clamp(2rem,4vw,3rem),並在 390/800/1440px 讀 computed font-size。
成功狀態
現有 runtime 真正把控制 font-size 的 token 改為 clamp 表達式;完整 viewport facts 顯示三個 computed 值皆介於 32px 與 48px。
證據位置
現有 runtime before=2rem、after=clamp(2rem,4vw,3rem)、property=--heading-size;完整證據需 values[390,800,1440] 皆為32–48px

能用 srcset 提供不同 intrinsic widths,並用 sizes 告訴瀏覽器圖片在各 breakpoint 的預期顯示槽寬。

為什麼重要
只有 srcset 沒有正確 sizes,瀏覽器可能高估顯示寬度而下載過大圖片,浪費行動網路與解碼時間。
完成條件
fixture 保留兩個本地候選,執行後將 sizes 由 100vw 改為 `(max-width:480px) 100vw, 50vw`。
成功狀態
runtime 的 sizes attribute 真正更新;完整 image probe 在不同 viewport 回報 currentSrc、naturalWidth 與選中候選。
證據位置
現有 runtime before=100vw、after=(max-width:480px) 100vw, 50vw、attribute=sizes;完整證據需 currentSrc/naturalWidth/candidateByWidth

能在 390px 寬度與 200% 文字尺度下保留全部內容、操作與清楚焦點,不產生雙向捲動。

為什麼重要
低視力使用者會放大內容;固定高度、不可換行列與隱藏 overflow 可能裁掉文字或按鈕。
完成條件
fixture 初始 font-size:100%,執行後切為 200%,再於 390px 量測文件與元件 scrollWidth/clientWidth。
成功狀態
runtime 的 font-size 由 16px 變 32px;完整 overflow probe 證明 documentOverflow=false,文字與按鈕仍可見可聚焦。
證據位置
現有 runtime before=16px、after=32px、property=font-size;完整證據需 scrollWidth<=clientWidth、controlsVisible=true、focusVisible=true

4. 4 個知識模組與 Micro Labs

依序展開;每次只改一項,操作後確認該模組自己的 DOM、progress 與 evidence。

1內容驅動 breakpoint

完整解釋

白話:不要問『iPhone 是幾 px』,要問『我的導覽從幾 px 開始擠不下』。

正式說法:A content-driven breakpoint is a media-query threshold selected from observed layout failure rather than device taxonomy;the responsive rule changes layout at that measured condition。

何時使用:導覽列、卡片列、表單欄位在容器縮小時開始碰撞、截斷或不可讀時。

修改前後:Before:用 device names 選 375/768px。After:拖曳 viewport,記錄內容第一個失效寬度並在其附近設 query。

常見混淆:breakpoint 不是每個常見裝置各一條 media query;一個版面只需要處理實際內容狀態轉移。

名詞與最小範例

內容驅動 breakpoint Content-driven responsive breakpoint

白話
不要問『iPhone 是幾 px』,要問『我的導覽從幾 px 開始擠不下』。
正式定義
A content-driven breakpoint is a media-query threshold selected from observed layout failure rather than device taxonomy;the responsive rule changes layout at that measured condition。
何時使用
導覽列、卡片列、表單欄位在容器縮小時開始碰撞、截斷或不可讀時。
最小範例
.site-nav{display:flex;flex-wrap:nowrap}@media(max-width:44rem){.site-nav{flex-wrap:wrap}}
驗收證據
現有 runtime before=nowrap、after=wrap、property=flex-wrap;完整證據需 firstWrapWidth、itemRects 與 mediaMatches
常混淆
breakpoint 不是每個常見裝置各一條 media query;一個版面只需要處理實際內容狀態轉移。

Micro Lab|內容驅動 breakpoint

情境:品牌導覽加入『企業永續顧問服務』後,在平板橫向就開始重疊,但原本只為手機設 breakpoint。

起始狀態:導覽為 display:flex、flex-wrap:nowrap,四個連結保留真實長文字。

  1. 執行實驗替導覽切換 compact-navigation class,讀取 computed flex-wrap;再以 viewport probe 找內容失效寬度。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:內容驅動 breakpoint

尚未執行;DOM data-state=starter。

預期:runtime 的 flex-wrap 由 nowrap 變 wrap;後續 viewport probe 能量得第一個 item rect 重疊/換行點與 media query match。

證據:現有 runtime before=nowrap、after=wrap、property=flex-wrap;完整證據需 firstWrapWidth、itemRects 與 mediaMatches

第一個檢查:先拖曳預覽並記下第一個連結換行或互相擠壓的寬度。

重設:重新載入 module,移除 compact-navigation class,viewport 回到初始寬度。

卡住時

  • 症狀:某些語言版本仍在 breakpoint 前溢位。
    可能原因:測試只用短版中文,沒有包含最長真實導覽文字。
    先檢查:替換成最長語系內容,再重新量 firstWrapWidth。
    修正:依最壞內容狀態調整 breakpoint,而不是新增裝置專屬 query。

理解檢查

選 breakpoint 最可靠的依據是什麼?

尚未作答。

查看解釋

內容與版面關係才是要處理的狀態,不是裝置品牌。

本模組官方來源:MDN|Using media queries

2clamp()

完整解釋

白話:clamp 像有最小與最大護欄,中間可以跟著畫面寬度平滑變化。

正式說法:clamp(MIN, VAL, MAX) resolves to max(MIN,min(VAL,MAX)); a relative preferred value such as vw provides fluid interpolation bounded by accessible minimum and design maximum。

何時使用:頁面主標題、區塊間距或容器 padding 需要跨寬度平滑縮放但不能失控時。

修改前後:Before:每個 breakpoint 跳一個固定 font-size。After:preferred 值流動,min/max 保護極端寬度。

常見混淆:clamp 的三個參數不是起點、中點、終點;第二個值仍會被 min/max 比較。

名詞與最小範例

clamp() Bounded fluid values with clamp

白話
clamp 像有最小與最大護欄,中間可以跟著畫面寬度平滑變化。
正式定義
clamp(MIN, VAL, MAX) resolves to max(MIN,min(VAL,MAX)); a relative preferred value such as vw provides fluid interpolation bounded by accessible minimum and design maximum。
何時使用
頁面主標題、區塊間距或容器 padding 需要跨寬度平滑縮放但不能失控時。
最小範例
.hero-title{font-size:clamp(2rem,4vw,3rem)}
驗收證據
現有 runtime before=2rem、after=clamp(2rem,4vw,3rem)、property=--heading-size;完整證據需 values[390,800,1440] 皆為32–48px
常混淆
clamp 的三個參數不是起點、中點、終點;第二個值仍會被 min/max 比較。

Micro Lab|clamp()

情境:首頁標題在手機太小、超寬桌機又過大,目前需要三條 media query 才勉強控制。

起始狀態:標題使用固定 --heading-size:2rem。

  1. 執行實驗切換 fluid-heading class,讀取實際供 font-size 使用的 --heading-size;再由 viewport probe 計算三個寬度。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:clamp()

讓服務跟著需求成長

尚未執行;DOM data-state=starter。

預期:現有 runtime 真正把控制 font-size 的 token 改為 clamp 表達式;完整 viewport facts 顯示三個 computed 值皆介於 32px 與 48px。

證據:現有 runtime before=2rem、after=clamp(2rem,4vw,3rem)、property=--heading-size;完整證據需 values[390,800,1440] 皆為32–48px

第一個檢查:先讀 root font-size,再計算 2rem 與 3rem 的 px 上下界。

重設:重新載入 module,移除 fluid-heading class。

卡住時

  • 症狀:三個 viewport 的標題都固定 32px。
    可能原因:preferred 4vw 在測試寬度都小於 min,或 class/token 沒命中。
    先檢查:讀 --heading-size 與每個寬度的 computed font-size,計算 4vw。
    修正:確認 fluid-heading 生效,並選擇會在目標區間穿越 min/max 的 preferred value。

理解檢查

`clamp(2rem,4vw,3rem)` 的最大結果是什麼?

尚未作答。

查看解釋

clamp 的第三個參數是上界;preferred 再大也不超過它。

本模組官方來源:MDN|clamp()

3srcset 與 sizes

完整解釋

白話:srcset 是可選圖片清單,sizes 是告訴瀏覽器『這個位置大約會顯示多寬』。

正式說法:Width-descriptor srcset supplies candidate intrinsic widths;sizes defines the source size used with device pixel ratio to select a candidate before layout completes。

何時使用:同一內容圖片在手機佔滿寬、桌機只佔半欄,需要避免所有裝置都下載最大檔時。

修改前後:Before:所有 viewport 使用單一最大圖或 sizes=100vw。After:候選寬度與真實 layout slot 規則一致。

常見混淆:sizes 不是圖片檔案本身的尺寸,也不會直接設定 CSS width;它是候選選擇提示。

名詞與最小範例

srcset 與 sizes Responsive image source candidates and slot sizes

白話
srcset 是可選圖片清單,sizes 是告訴瀏覽器『這個位置大約會顯示多寬』。
正式定義
Width-descriptor srcset supplies candidate intrinsic widths;sizes defines the source size used with device pixel ratio to select a candidate before layout completes。
何時使用
同一內容圖片在手機佔滿寬、桌機只佔半欄,需要避免所有裝置都下載最大檔時。
最小範例
<img src="service-640.svg" srcset="service-320.svg 320w, service-640.svg 640w" sizes="(max-width:480px) 100vw, 50vw" alt="到府服務人員整理空間">
驗收證據
現有 runtime before=100vw、after=(max-width:480px) 100vw, 50vw、attribute=sizes;完整證據需 currentSrc/naturalWidth/candidateByWidth
常混淆
sizes 不是圖片檔案本身的尺寸,也不會直接設定 CSS width;它是候選選擇提示。

Micro Lab|srcset 與 sizes

情境:桌機圖片實際只佔半欄,但瀏覽器仍依 100vw 選擇 1280px 候選。

起始狀態:img 有兩個 width descriptor candidates,sizes 暫為 100vw。

  1. 執行實驗將圖片 sizes attribute 由 100vw 改為 `(max-width:480px) 100vw, 50vw`;完整 probe 再於兩個寬度讀 currentSrc。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:srcset 與 sizes

到府服務人員整理空間
響應式服務圖片
尚未執行;DOM data-state=starter。

預期:runtime 的 sizes attribute 真正更新;完整 image probe 在不同 viewport 回報 currentSrc、naturalWidth 與選中候選。

證據:現有 runtime before=100vw、after=(max-width:480px) 100vw, 50vw、attribute=sizes;完整證據需 currentSrc/naturalWidth/candidateByWidth

第一個檢查:先看 CSS layout 的真實 slot width,再檢查 sizes 是否描述相同規則。

重設:重新載入 module,sizes 回到 100vw;候選皆為教材本地資產。

卡住時

  • 症狀:不同 viewport 都選到最大候選。
    可能原因:sizes 仍是 100vw、候選 descriptor 錯誤,或裝置 DPR 使較大候選合理。
    先檢查:同時記錄 viewport、CSS slot width、DPR、sizes 與 currentSrc。
    修正:讓 sizes 對應 layout,修正 w descriptor;不要只憑檔名判斷是否過大。

理解檢查

`sizes` 在 width-descriptor srcset 中主要描述什麼?

尚未作答。

查看解釋

瀏覽器以 source size 和 DPR 評估需要哪個 intrinsic width 候選。

本模組官方來源:MDN|Responsive images

4200% zoom、橫向 reflow

完整解釋

白話:把字放大兩倍時,頁面應重新排成更高的單欄,不該要求使用者同時左右上下找內容。

正式說法:Reflow requires content to adapt without loss of information/functionality and without two-dimensional scrolling at equivalent narrow CSS viewport conditions, except essential two-dimensional layouts。

何時使用:驗收響應式導覽、卡片、表單、CTA 與任何固定高度容器時。

修改前後:Before:用 fixed height 與 overflow:hidden 維持卡片外觀。After:高度由內容撐開、controls 可換行,200% 仍完整。

常見混淆:瀏覽器 page zoom 與只改一個元素 font-size 不完全相同;本 microLab 先模擬文字尺度,完整驗收仍要用瀏覽器 zoom。

名詞與最小範例

200% zoom、橫向 reflow Two-hundred-percent zoom and horizontal reflow

白話
把字放大兩倍時,頁面應重新排成更高的單欄,不該要求使用者同時左右上下找內容。
正式定義
Reflow requires content to adapt without loss of information/functionality and without two-dimensional scrolling at equivalent narrow CSS viewport conditions, except essential two-dimensional layouts。
何時使用
驗收響應式導覽、卡片、表單、CTA 與任何固定高度容器時。
最小範例
.zoom-test{font-size:200%}.actions{display:flex;flex-wrap:wrap}.card{height:auto;overflow:visible}
驗收證據
現有 runtime before=16px、after=32px、property=font-size;完整證據需 scrollWidth<=clientWidth、controlsVisible=true、focusVisible=true
常混淆
瀏覽器 page zoom 與只改一個元素 font-size 不完全相同;本 microLab 先模擬文字尺度,完整驗收仍要用瀏覽器 zoom。

Micro Lab|200% zoom、橫向 reflow

情境:聯絡卡片在 200% zoom 時,固定高度裁掉錯誤訊息,兩個按鈕又推出 viewport。

起始狀態:預覽根字級 16px;actions 已允許 wrap,卡片高度為 auto。

  1. 執行實驗替預覽根節點切換 zoom-200 class,讀取 computed font-size;再以 390px overflow probe 量測 reflow。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:200% zoom、橫向 reflow

聯絡顧問

請留下需求,我們會在下一個工作日回覆。

尚未執行;DOM data-state=starter。

預期:runtime 的 font-size 由 16px 變 32px;完整 overflow probe 證明 documentOverflow=false,文字與按鈕仍可見可聚焦。

證據:現有 runtime before=16px、after=32px、property=font-size;完整證據需 scrollWidth<=clientWidth、controlsVisible=true、focusVisible=true

第一個檢查:先確認 viewport meta 與 390px 寬度,再比較 document scrollWidth/clientWidth。

重設:重新載入 module,移除 zoom-200 class,viewport 回到390px測試起點。

卡住時

  • 症狀:scrollWidth 等於 clientWidth,但部分文字仍不見。
    可能原因:overflow:hidden 或固定高度裁切內容,單看水平捲軸不足。
    先檢查:檢查卡片 scrollHeight/clientHeight、overflow 與被裁切節點 rect。
    修正:移除固定高度/裁切,讓內容自然 reflow,再保留必要 focus outline。

理解檢查

200% reflow 驗收不能只看哪一項?

尚未作答。

查看解釋

隱藏 overflow 也能讓捲軸消失;仍需驗內容、功能與焦點沒有被裁切。

本模組官方來源:WCAG|Reflow

5. 內容驅動響應式與圖片|整合 Lab

情境:在同一份章節作品中整合 4 個 module,排除單項通過但組合失敗的問題。

交付:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

尚未執行整合步驟。
  1. 1觀察整合 Starter 並找出第一個未通過 module。

    檔案:files/starter/index.html 定位:#observe

    操作:逐項讀取 outcomes,執行一次現況操作。

    // OBSERVE:不修改程式,先記錄第一個失敗 moduleId。

    預期:能指出第一個未通過 module。

    證據:第一個 moduleId 與缺少的 evidence。

    原因:先觀察可避免未重現就直接改答案。

    卡住先查:確認開啟的是 files/starter/index.html。

  2. 2依 module 順序修改,一次只加入一個責任。

    檔案:files/starter/index.html 定位:#modify

    操作:執行實驗替導覽切換 compact-navigation class,讀取 computed flex-wrap;再以 viewport probe 找內容失效寬度。 → 執行實驗切換 fluid-heading class,讀取實際供 font-size 使用的 --heading-size;再由 viewport probe 計算三個寬度。 → 執行實驗將圖片 sizes attribute 由 100vw 改為 `(max-width:480px) 100vw, 50vw`;完整 probe 再於兩個寬度讀 currentSrc。 → 執行實驗替預覽根節點切換 zoom-200 class,讀取 computed font-size;再以 390px overflow probe 量測 reflow。

    // 10-01-breakpoint: 執行實驗替導覽切換 compact-navigation class,讀取 computed flex-wrap;再以 viewport probe 找內容失效寬度。
    // 10-02-clamp: 執行實驗切換 fluid-heading class,讀取實際供 font-size 使用的 --heading-size;再由 viewport probe 計算三個寬度。
    // 10-03-srcset-sizes: 執行實驗將圖片 sizes attribute 由 100vw 改為 `(max-width:480px) 100vw, 50vw`;完整 probe 再於兩個寬度讀 currentSrc。
    // 10-04-200-zoom-reflow: 執行實驗替預覽根節點切換 zoom-200 class,讀取 computed font-size;再以 390px overflow probe 量測 reflow。

    預期:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

    證據:現有 runtime before=nowrap、after=wrap、property=flex-wrap;完整證據需 firstWrapWidth、itemRects 與 mediaMatches;現有 runtime before=2rem、after=clamp(2rem,4vw,3rem)、property=--heading-size;完整證據需 values[390,800,1440] 皆為32–48px;現有 runtime before=100vw、after=(max-width:480px) 100vw, 50vw、attribute=sizes;完整證據需 currentSrc/naturalWidth/candidateByWidth;現有 runtime before=16px、after=32px、property=font-size;完整證據需 scrollWidth<=clientWidth、controlsVisible=true、focusVisible=true

    原因:逐項整合能把回歸定位到明確 module。

    卡住先查:回到第一個未通過的 module,只修該處。

  3. 3驗證所有 module 在整合後仍成立。

    檔案:files/solution/index.html 定位:#verify

    操作:重新整理並依 outcomes 順序重跑所有證據。

    // VERIFY:逐項記錄 pass/fail,不以看到畫面代替 evidence。

    預期:4/4 module 通過。

    證據:每個 moduleId 都有實際結果與重設後重現紀錄。

    原因:單項通過不代表整合後沒有 selector、id 或狀態衝突。

    卡住先查:若失敗,回到第一個失敗 module,不同時修多項。

  4. 4把本章責任轉用到不同內容情境。

    檔案:files/starter/index.html 定位:#challenge

    操作:替另一個商家情境重做,仍達成:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

    // CHALLENGE:只替換內容與資料,不新增框架或跳過 self-check。

    預期:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

    證據:轉用後的作品與全數 module self-check。

    原因:能轉用才代表理解責任,而非只記住原範例。

    卡住先查:先確認共同基底與 module id 未被改壞。

重設方法:重新載入 files/starter/index.html;若 localStorage 不可用,使用頁面內記憶狀態並提供重設按鈕。

6. 自學挑戰與三層提示

挑戰:將同一組責任轉用到另一個商家情境,仍達成:完成由內容決定斷點、文字有界縮放且圖片選擇合理的響應式頁。

限制:不新增框架。;保留 module id 與驗收證據。

提交證據:提交可直接開啟的作品與逐項 self-check。

提示 1|方向提示

拆小:在「內容驅動響應式與圖片」先完成第一個尚未通過的 module:內容驅動 breakpoint,不同時修改其他責任。

提示 2|關鍵片段

串接:依 10-01-breakpoint → 10-02-clamp → 10-03-srcset-sizes → 10-04-200-zoom-reflow 核對各自輸入與證據。

提示 3|完整解答與原因

完整解答與原因:依序執行 執行實驗替導覽切換 compact-navigation class,讀取 computed flex-wrap;再以 viewport probe 找內容失效寬度。 → 執行實驗切換 fluid-heading class,讀取實際供 font-size 使用的 --heading-size;再由 viewport probe 計算三個寬度。 → 執行實驗將圖片 sizes attribute 由 100vw 改為 `(max-width:480px) 100vw, 50vw`;完整 probe 再於兩個寬度讀 currentSrc。 → 執行實驗替預覽根節點切換 zoom-200 class,讀取 computed font-size;再以 390px overflow probe 量測 reflow。;這個順序能把每個結果對回單一 module,避免整合後無法定位。

完整解答預設收合;先留下自己的嘗試,再開第三層。

7. 症狀式除錯指南

8. 理解測驗、驗收證據與下一步

哪個狀態代表 10-01-breakpoint 完成?

尚未作答。

查看解釋

裝置尺寸會變、文字也可能翻譯變長;由內容失效點決定斷點,版面才對真實內容有韌性。

哪個狀態代表 10-02-clamp 完成?

尚未作答。

查看解釋

只有 vw 會在極窄頁過小、極寬頁過大;clamp 同時保留流動性與可讀邊界。

哪個狀態代表 10-03-srcset-sizes 完成?

尚未作答。

查看解釋

只有 srcset 沒有正確 sizes,瀏覽器可能高估顯示寬度而下載過大圖片,浪費行動網路與解碼時間。

哪個狀態代表 10-04-200-zoom-reflow 完成?

尚未作答。

查看解釋

低視力使用者會放大內容;固定高度、不可換行列與隱藏 overflow 可能裁掉文字或按鈕。

驗收清單

官方來源

回到課程地圖選擇下一章