# RWD

<figure><img src="https://thumbs.gfycat.com/MealyHandsomeAruanas-max-1mb.gif" alt=""><figcaption></figcaption></figure>

### 起源

2010 由 [Ethan Marcotte](https://alistapart.com/author/emarcotte/) 提出 [Responsive Web Design](https://alistapart.com/article/responsive-web-design/) 概念，簡稱 RWD，中文可以稱作響應式網站設計，這並非是種技術，而是種方式、最佳實踐的名詞，達成最終目的：<mark style="color:red;">在任何情境下，都有最佳體驗</mark>。

在這個名詞出現之前，已經有類似的型態，只是沒有特別被提出來討論與重視。如果不使用 CSS 特別排版的話，HTML 本身就有某種程度的 RWD 意思，您可以進入以下範例，調整寬高、縮放試試看：

{% embed url="<https://codepen.io/neillin1023/pen/RwJBxvz?editors=1000>" %}

您會發現<mark style="color:red;">調整寬高、縮放網頁之後，還是可以看到所有的內容</mark>，這便是 RWD 的核心：流體佈局。流體佈局其實就是順著網頁原本的順序：由左至右、由上至下的方向顯示內容。在[排版技巧](https://bpio.gitbook.io/docs/edu-css/chapter3-layout/layout-skills)篇的 float、flex、grid原則都是希望以<mark style="color:red;">最小的設定，達成通用的規則</mark>，基本上都是基於流體佈局的思考方式。

{% hint style="info" %}
強烈推薦由左而右、由上至下的觀看順序，跟 DOM 結構高度相關，非必要請「不要」使用特殊的 CSS 任意變換位置，除了可能會造成無障礙的困擾，也可能在後續的維護上沒有準則導致維護困難。

2025-11 更新: flex可以靠著新的reading-flow 和 reading-order改變 閱讀，必要時可善用。
{% endhint %}

另外，在智慧型手機誕生之初，AWD 概念被提出，AWD 全名為 Adaptive Web Design，與 RWD 的不同之處是，AWD 是直接專門為平板和手機做一個網站，等於是多做一個縮小版的網站，爾後因為成本太高不易維護等原因逐漸被淘汰，延伸出 RWD。

但也不是說 AWD 就是不好，如果經過研究發現特別做一版，例如專門做給特定族群，營運上的效益又可以壓過維護成本，還是可以使用 AWD 概念製作網站，畢竟 RWD 雖然一套吃全部，但相對的在初期規劃就必須更縝密，如果沒設計好，在內容彈性上面可能就略遜一籌。

### RWD 的優缺點，評估是否導入

* 優點：
  * 行動裝置已成為主要瀏覽方式
  * 技術熟練的話，開發成本與時間比 APP、AWD 便宜許多
  * 提升 SEO
  * 給用戶最好的體驗
  * 無障礙較好
* 缺點：
  * 越複雜的內容成本越高。
  * 無法完全支援舊版瀏覽器，如有需求需要學習 Hack 技術。
  * 開發時會有高耦合，更動設計可能會影響大螢幕和小螢幕，須特別留意，但可靠技術力解耦。

### 注意事項

緊抓著自然的流體佈局大概念，還有以下幾點是必須要考量的項目：

* 字級：
  * 原則：\
    大小需要依照「顯示裝置」與「使用者現實使用情境」決定，以達成最佳觀看效果，例如用手拿著 6 寸手機觀看和走在街上觀看對街的螢幕廣告，在裝置與觀看距離上有巨大的分別。這裡只是概括說明，實際上還得視字體而定，因為每種語言字體的特性、樣式不同，都會影響。製作網頁時如無特別要求的話，就是只針對桌機、平板、手機大小、解析度和一般使用距離情境。
  * 技巧：字級使用相對單位，例如：em、rem、%、vh、vw、cqw、cqi...，搭配 @media、@container 等查詢，也呼應無障礙網頁設計。

{% hint style="danger" %}
所以在 RWD 概念設計的網頁中，就不能在網頁底部寫類似「使用 1280 x 800 解析度瀏覽，以達最佳瀏覽體驗。」，因為跟 RWD 的觀念互相衝突，除非網站刻意有所限制，否則 RWD 在理想上必須確保在每個裝置與情境下使用時，都是最佳瀏覽體驗，而這也跟無障礙網頁設計的理念一致，不能因為裝置的問題造成體驗不一致。
{% endhint %}

* HTML 的設定：
  * 原則：盡量不要設定太多條件，只需要像 CSS 的 Reset 般，讓每個瀏覽器解讀時有一致的起點。
  * 技巧：\
    `<meta>` 的 name=viewpoint

    <pre class="language-html"><code class="lang-html"><strong>&#x3C;meta name="viewport" content="width=device-width, initial-scale=1" />
    </strong></code></pre>

    一般僅僅需要設定這樣即可，也就是畫面寬度等於裝置的螢幕寬度，初始縮放比例等於 1:1 ，除非特殊情形，請不要強制設定是否可以縮放，在無障礙網頁設計以及使用者經驗來說是不恰當，因為無法預期使用者到底什麼時候會需要使用縮放功能，這裡的設定也會影響到文章後半段提及的@media 媒體查詢，跟版面的呈現息息相關。\
    \
    借用 [Responsive Web Design 基礎 : \<meta name=”viewport” > 設定](https://medium.com/frochu/html-meta-viewport-setting-69fbb06ed3d8) 這篇文章的表格：

<figure><img src="https://miro.medium.com/max/1400/1*6ApOKh50c-Kk-EEJSZxiPA.png" alt=""><figcaption><p>meta name="viewpoint" 支援屬性與說明</p></figcaption></figure>

* 多國語言
  * 原則：\
    不同的語言有不同的文字順序，例如中文有由上至下、由右而左的文章，或由右而左、由上至下的語言，或文字長度普遍較長，掌握文字特行並確保在切換網站目標語言時，體驗一致又不會破版，理念仍然與無障礙網頁設計相同。
  * 技巧：\
    透過 lang、class 等 attributes ，搭配 CSS Selector 改變 writing-mode、text-orientation、text-combine-upright、text-ellipsis、text-align 等方式達成。
* 圖片、影片、嵌入區塊
  * 原則：\
    如同文字一般，隨著「顯示裝置」與「使用者現實使用情境」決定照片大小，且需要注意流量問題與解析度問題，無障礙網頁設計原則亦同。
  * 技巧：\
    CSS 的 background-size、object-fit 等，決定圖片在隨著裝置改變時，情境如何變換。\
    例如在智慧型手機上觀看時，風景圖可能只呈現部分區域，商品圖可能還是要完整呈現。\
    \
    如需加註尺寸，搭配 @media、@container 等查詢使用如 %、vw 等相對單位。\
    \
    解析度問題盡量使用向量圖檔，如果不能，則要將同一張圖至少輸出三種尺寸以上，供不同裝置大小、解析度使用，也要確保圖片是經過壓縮的，甚至使用 Lazy Loading 等提高效能技巧。如果是小圖示，也可以考慮使用 icon font 的方式。
* 內容
  * 原則：由大到小必須有所取捨，若是不能刪減，必須有合理的結構安排資訊或變換形式。
  * 技巧：@media
* 斷點
  * 原則：\
    統計現實使用的多數裝置，並歸納出合理的尺寸切換時間改變排版，這個時間點稱為斷點。
  * 技巧：@media、@container
* 圖表<br>
  * 原則：\
    不管可視範圍如何調整，都要可以清楚辨識圖中資訊，不單只以顏色分辨資訊。
  * 技巧：@media、@container，針對不同情境，決定資訊要傳遞多少，甚至不使用圖改用表格等方式顯示也是可以的。

{% hint style="info" %}
您可能已經發現內文不斷提到無障礙網頁設計，那是因為無障礙網頁設計的理念與 RWD 不謀而合，<mark style="color:red;">在任何情境下，都盡量達成最佳體驗</mark>。
{% endhint %}

### 實作技術一：Columns

目前比較少人用，但 CSS Columns 其實也可以做成很好的 RWD 版面。但除了 RWD 外還有別的用處，這邊僅提醒有這些屬性，可以視形況使用。

| 屬性                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | 相關屬性                                                                                                                                                                                                                                                                                                                                                                      |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <ul><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-width">column-width</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-count">column-count</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#columns">columns</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-rule-color">column-rule-color</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-rule-style">column-rule-style</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-rule-width">column-rule-width</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-rule">column-rule</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-span">column-span</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-fill">column-fill</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#column-gap">column-gap</a></li></ul> | <ul><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#break-after">break-after</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#break-before">break-before</a></li><li><a href="https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/#break-inside">break-inside</a></li></ul> |

請打開以下 codepen 頁面，試著將視窗縮小與放大，可以多暸解 column 的特性。即便設定成 3 欄，在寬度不足的情況下也會自動調整欄位數。

{% embed url="<https://codepen.io/neillin1023/pen/eYKXNwd>" %}

### 實作技術二：@media

當我們真的沒辦法規畫出自然流動的版面，就必須依靠斷點或類型改變版面，上述技巧裡提到的 @media ，全名為 Media Queries，意思是先「查詢」是什麼「媒體類型與特徵」，針對該媒體進行 CSS 的設定。

```
@media <media-query-list> { <stylesheet> }  
```

media-query-list 裡是由四種媒體類型和特徵組成，類型如下：

* all：所有裝置。
* print：印刷，含列印產生的預覽，如 .pdf。關於列印的技巧，我們會在[章節四：進階技巧裡的列印篇](https://bpio.gitbook.io/docs/edu-css/chapter4-advance/print)詳述。
* screen：不包含 print 和 speech 的螢幕裝置。
* speech：可以朗讀頁面的設備，如螢幕報讀。

特徵如下：

* width：包含 max-width 和 min-wifth。
* height：包含 max-height 和 min-height。
* aspect-ratio：螢幕長寬比例，會寫成 1 / 1 這種格式，包含 max-aspect-ratio 和 min-aspect-ratio。
* orientation：螢幕方向，直向為 portrait，橫向為 landscape。
* 其他不常用僅列舉：
  * resolution
  * scan
  * update-frequency
  * overflow-block
  * overflow-inline
  * color
  * color-index
  * display-mode
  * monochrome
  * inverted-colors
  * pointer
  * hover
  * any-pointer
  * light-level
  * scripting

上面有提到類型與特徵可以組成媒體，組成的方式可以使用以下：

* and
* only
* not
* or

{% hint style="info" %}
CSS2.1 和 Media Queries 3 定義了幾種額外的媒體類型（`tty`, `tv`, `projection`, `handheld`, `braille`, `embossed`, `aural`），但它們在 [Media Queries 4](https://dev.w3.org/csswg/mediaqueries/#media-types) 中已棄用，不應使用。
{% endhint %}

{% hint style="danger" %}
截至 2023 ，目前已經可以使用 ( 200px <= width <= 500px) 這種寫法，但僅限於主流瀏覽器，若要使用此種寫法，請思考您的網站所適用的瀏覽器。
{% endhint %}

除了 or 是直接使用「，」分隔之外，其他都必須特定寫出來。使用方式如下範例：

```html
<!-- HTML -->
<link rel="stylesheet" media="not screen and (color)" href="example.css" />
```

```css
/* CSS */
h1 {
  font-size:30px;
}

@media not screen and (max-width:300px),
print and (orientation: landscape) {
  h1{
    font-size:50px;
    color:red;
  }
}
```

{% hint style="info" %}
要特別注意您必須有條理的管理 CSS，否則可能會讓有寫 @media 的地方散落在各處，造成不好維護，或是權重不同一直被覆蓋無法達成您想要的效果。
{% endhint %}

### 實作技術三：@container

容器查詢，2022 最新支援，可以至 [caniuse 網站](https://caniuse.com/?search=%40container)查看支援情況。主流瀏覽器應該都已經支援。

上述 @media 使用時，通常都是用「裝置」的長寬去判定要不要使用某個斷點的 CSS，但有時候我們會在同一個裝置視窗時，因為內部版面配置長寬不同，同樣的元件，想要呈現不一樣的顯示方式。例如：

<figure><img src="https://ishadeed.com/assets/cq/use-case-1-3.png" alt=""><figcaption><p><a href="https://andyyou.github.io/2021/09/26/css-container-query/">https://andyyou.github.io/2021/09/26/css-container-query/</a></p></figcaption></figure>

<figure><img src="https://ishadeed.com/assets/cq/use-case-2.jpg" alt=""><figcaption><p><a href="https://andyyou.github.io/2021/09/26/css-container-query/">https://andyyou.github.io/2021/09/26/css-container-query/</a></p></figcaption></figure>

{% embed url="<https://codepen.io/una/pen/LYbvKpK>" %}

為了脫離跟 root 相對關係的問題，所以產生了 @container 藉此脫離以 root 為基準的關係，改以指定的上層容器為基準，因為有時候我們想要在某個斷點改變某個區域的樣式，但這個斷點並非包含在開發時預設的幾個斷點，所以會增加客製化的斷點，在管理上與樣式上就會變得凌亂與細碎。

<figure><img src="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries/container-query-diagram.png" alt=""><figcaption><p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries</a></p></figcaption></figure>

使用容器查詢時，必須先選擇要當作依據的容器，必且給予容器查詢的類型：

```css
.container {
  container-type: size | inline-size | normal;
}
```

而子容器就可以使用 @container ：

```css
/* Default heading styles for the card title */
.card h2 {
  font-size: 1em;
}

/* Container query applied if the container is larger than 700px */
@container (min-width: 700px) {
  .card h2 {
    font-size: 2em;
  }
}
```

如下範例：

{% embed url="<https://codepen.io/neillin1023/pen/vYrbZVB>" %}

當然，也可能會出現巢狀結構，那麼就可以使用命名的方式：

```css
.container {
  container-type: inline-size;
  container-name: sidebar;
  /* 或是可以寫成一行： container: sidebar / inline-size */
}

@container sidebar (min-width: 700px) {
  .card {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
}
```

另外，也新增了關於容器查詢使用的寬度單位：

* `cqw`: 1% of a query container's width
* `cqh`: 1% of a query container's height
* `cqi`: 1% of a query container's inline size
* `cqb`: 1% of a query container's block size
* `cqmin`: The smaller value of either `cqi` or `cqb`
* `cqmax`: The larger value of either `cqi` or `cqb`

```css
@container (min-width: 700px) {
  .card h1 {
    font-size: max(1.5em, 1.23em + 2cqi);
  }
}
```

至此，我們可以讓容器裡面的排版與外面的排版脫離，也就是不再具有依賴性質，讓每個容器都有獨立的排版機制去呈現最佳的外觀。

### 小結

請保持著最大的原則：能利用最自然的流體排版機制就盡量使用。盡量不重複 HTML、盡量不寫多餘的 CSS、依照內容絕定 DOM 的順序以及不濫用媒體查詢與容器查詢。能達成這些通常也代表著效能越高、品質越好。

### 作業

請試著在網路上找到有 RWD 的設計稿，並且嘗試使用本篇提及的技術排版。

### 參考連結

* [Responsive Web Design](https://alistapart.com/article/responsive-web-design/)
* [Responsive Web Design 基礎 : \<meta name="viewport" > 設定](https://medium.com/frochu/html-meta-viewport-setting-69fbb06ed3d8)
* [什麼是RWD響應式網頁?](https://www.newscan.com.tw/rwd/responsive-web-design.php)
* [写给自己看的CSS columns分栏布局教程](https://www.zhangxinxu.com/wordpress/2019/01/css-css3-columns-layout/)
* [MDN - @media](https://developer.mozilla.org/zh-TW/docs/Web/CSS/@media)
* [oxxostudio - CSS Media Queries 詳細介紹](https://www.oxxostudio.tw/articles/201810/css-media-queries.html)
* [A Practical Guide to CSS Media Queries](https://stackdiary.com/css-media-queries/)
* [Getting started with CSS container queries](https://developer.mozilla.org/en-US/blog/getting-started-with-css-container-queries/)
