https://hackmd.io/@SZmgzagsQOi1Bf3cEXt3WQ/r1BNy1ud3
# RWD應用撰寫要點
AWD自適應和RWD響應式的不同


做活動頁時,大部份的排版變化不會太大.所以較適合RWD方式,如是文章為主的類別,又會做版面上的變化,這個就較適用於AWD
## 1. margin和padding的使用時機
W3C 的標準規則所告成的,W3C 規定在第一個子物件使用 margin-top 的時候,會被父層物件拿去用,例如當 DIV 裡面的第一段文字使用 margin-top 的時候,我們所預期的上間距效果不會出現在第一段文字上面,反而是父層的DIV 產生了上間距。同理,W3C 的標準規則在最後一個子物件使用 margin-bottom 的時候,會被父層物件拿去用!
以下為解決方式
1、設置父元素或者自身的display:inline-block;
2、設置父元素的border 大於1px(可設置transparent)
3、設置父元素的padding 大於1px(代替子層的margin-top)
4、給父元素設置overflow:hidden / auto;
5、給父元素或者自身設置position:absolute或者設定float:left / right
6、設置父元素非空,填充一定的內容。
範例 https://codepen.io/knstbp/pen/XWodwXj
<br>
## 2. Meta 的 Viewport說明
當在設計手機行動版網頁 (mobile web) 或響應式 (RWD, Responsive Web Design) 網頁時,我們需要用 <meta> viewport 來指定瀏覽器怎麼渲染和縮放網頁畫面的寬高。如果沒有設定 meta viewport,移動設備會以典型的桌面設備螢幕寬度渲染頁面,然後對頁面進行縮放以適合移動設備屏幕,這時候畫面看起來就會擠在一起或內容變很小不好閱讀。
| 屬性 | 說明 | 建議設定
| -------- | -------- | -------- |
| width | 設定畫面寬度 | device-width |
| height | 設定畫面高度 | device-hight |
| initial-scale | 設定畫面的初始縮放比例 | 1.0 |
| minimum-scale | 設定畫面的最小縮放比例 | 1.0 |
| maximum-scale | 設定畫面的最大縮放比例 | 1.0 |
| user-scalable | 設定是否允許使用者改變縮放比例 | 0 |
可用以下設定來因應RWD和IOS系統的縮放
```
<meta name="viewport" content="width=device-width,hight=device-hight,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0">
```
minimum-scale=1 設定畫面最小的縮放比例為 1,
maximum-scale=1 設定畫面最大的縮放比例為 1,都設為 1 的意思其實就是說不能縮放
user-scalable 用來設定是否允許使用者改變縮放比例,user-scalable=no 就是不允許縮放
另外有IPHONE 有瀏海的設計. 預設畫面範圍為

需修改content設定
```
<meta name="viewport" content="viewport-fit=cover">
```
畫面會變成

<br>
## 3. 單位%是依父級元素的寬還是高要清楚
1.相對於父級寬度的:
max-width、min-width、width、left、right、text-indent、padding、margin、grid-template-columns、grid-auto-columns、column-gap、、、、、等;
2.相對於父級高度的:max-height、min-height、height、top、bottom、grid-template-rows、grid-auto-rows、row-gap 等;
3.相對於主軸長度的:flex-basis等;
4.相對於繼承字號的:font-size等 (基於父層字型,如父層沒設定 預設html 100% = 16px);
5.相對於自身或繼承的字號(font-size)的:line-height;
6.相對於自身寬高的:border-radius、background-size、border-image-width、transform: translate()、transform-origin、zoom、clip-path 等;
7.相對於行高的:vertical-align等;
8.特殊算法的:background-position(方向長度/ 該方向除背景圖之外部分總長度)、border-image-slice(相對於圖片尺寸)、filter系列函數等;
9.position: absolute 基於自身設置百分比,會參照“父級”,如果父級沒有任何繼承寬高,則會使用body的設定
10.如果設定position: fixed,“父級”會參照viewport的設定(父級不存在transform為非none值的情況下)。
<br>
## 4. 父級元素的區塊保持完整大小
基於以上說明,會發現大部份都會參照到父元素的尺寸,所以在切版撰寫程式時,盡量維持每個區塊都會是應該有的大小及縮放方式,內容元素做百分比縮放時才不會出錯
尤其注意像A標籤的行內元素,都需轉換成區塊元素,讓A標籤裡的東西可以參照到A的正確空間大小
> A連結為預設行內元素(有設定紅背景)
```
<div class="out" style="height: 50px; width: 100px; border: 1px solid black;">
<a href="#" style="background: red; height: 100%;">
<div>我是按鈕</div>
</a>
</div>
```
> A連結設定成區塊元素(有設定紅背景)
```
<div class="out" style="height: 50px; width: 100px; border: 1px solid black;">
<a href="#" style="background: red; height: 100%; display: block; ">
<div>我是按鈕</div>
</a>
</div>
```

<br>
## 5. 圖片的自適應及等比縮放
> 使用圖片縮放時,要先確認圖片的縮放是要以父元素的高度還是寬度為基準,才決定程序怎麼寫
基礎基於父元素寬度寫法(父元素加上1px黑框):
> 外框最大為90vw,小於90vw時都維持100%寬度
> 圖片最大寬度和最大高度都為100%,此設定會讓圖片最大為自身尺寸,小於自身尺寸則會縮放
```
max-width: 100%; max-height: 100;
```
> 如果同時使用width:100% 和 max-width:100%; max-width屬性就失效了
> 如果以高度為主的圖片縮放則使用下方CSS寫法
```
height:100%; width: auto;
```
> 如果以寬度為主的圖片縮放則使用下方CSS寫法
```
width:100%; height: auto;
```
> 注意以上寫法. 除了參照的寬或高. 另外設置auto的 高/寬如果比父元素大. 還是會有出框的情形
> 如果寬度和高度都設定100%的話,一些瀏覽器會出現強制變形成外框大小的情形(如下圖)
```
width:100%; height: 100%;
```

> 這時圖片元素可再加上object-fit:contain 讓縮放能按圖片比例縮放在父元素框內
```
height:100%; width: 100%; object-fit:contain;
```


> 另外objct-fit:scale-down則是最大為圖片尺寸且圖片置中,如果外框小於圖片,圖片則是會自動按比例縮放,但是此方法,外框超過圖片時,雖然看起來圖片沒有超過原尺寸大小, 還是會佔有圖片縮放的空間, 只是圖片顯示最大為圖片原尺寸,
```
height:100%; width: 100%; object-fit:scale-down;
```

> 如果要控制在object-fit裡的位置,有下列表示方式 object-position:距左邊距離 距上面距離
> 或者直接指定靠上下(擇一)、左右(擇一)
```
object-position: 50% 50%;
object-position: right top;
object-position: left bottom;
object-position: 250px 125px;
```
<br>
## 6. 區塊的等比縮放
有時候我們也會需要控制區塊去做等比的區域限制,內容物也比較會有正常的參照,下方為解法
比較通的用作法,是Container區塊 <span style="color:blue">高度設成0</span> ,再用 <span style="color:blue"> padding-bottom</span> 的20%去和父元素的寬度做參照比例,所以Container區塊變成了寬度100%,高度20%,比例為5:1等比縮放的區塊
裡面子元素也因為要保持可運用的區域大小,所以在設定Absolute的同時,也設定定位和100%的寬高,讓子元素裡面的元素都能有正確的參考空間比例
```
<div class="container">
<div>test12345</div>
</div>
<style>
.container {
height: 0;
padding-bottom: 20%;
position: relative;
}
.container div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
```
參考範例 https://codepen.io/knstbp/pen/VwqaOQB
另外CSS還有一個 aspect-ratio 屬性,可直接指定比例,不使用單位,目前支援只有主流瀏覽器
```
aspect-ratio:16 / 9; // 比例:寬16/高9
```
附上支援表

<br>
## 7. Absolute的縮放與定位
在做網頁時,會遇到素材需要做絕對定位及RWD的設定,下方大概說明一下會影響的要點
參考 https://codepen.io/knstbp/pen/YzdqbEj
### 1.參照的父層元素尺寸明確
在做物件縮放前,父元素的尺寸是最重要的,因為子元素所有的百份比單位都是參照父元素,這步沒做好的話,裡面元素因為依據的大小不是正確的,所以設定的百分比都不會是想要的比例
### 2.盡量不使用固定大小(可限制最大或最小尺寸)
除非是一些小icon之類的, 不需要和其他元素一起做縮放,並在任何尺寸都不會有影響的狀況下,才會使用固定尺寸,一般在其他的狀況,不建議一開始就設定固定尺寸;
因為設定固定尺寸下去,後面的@Media會需要設定很多層去控制大小,在撰寫和修改的程度上會比較麻煩,也增加了不少處理時間
所以在父元素外框大小都設定好之下,裡面子元素盡量以父元素的比例去設定大小,在響應式設計縮放上會比較輕鬆
### 3.定位方式以重點元素為準
在多個元素需要組合在一起(絕對定位)做縮放的時候,這時依據的父層縮放來源就很重要,
> ※這邊注意:子元素設定absolute的時候,父元素的position如果是預設的Static,那子元素的top,left,bottom,right的定位會以body為依據,父元素設定<font color="red">除了Static以外</font>的屬性,子元素的定位基準才會是<font color="blue">父元素的左上角(座標0,0)</font>;
### 4.Absolute的置中
以下圖為範例:父元素設定好寬及高還有realative,讓子元素的Absolute百分比變成依據父元素的寬高時,設定以下數值即可

```
父元素 {
width: 30vw;
height: 30vw;
position: relative;
}
子元素{
width:400px;
height:200px;
top: 50%;
left:50%;
margin-top: -100px; //扣掉子元素自身高的一半
margin-left:-200px; //扣掉子元素自身寬的一半
}
```
第2個方法
1.這個子元素的必須有設定寬度和高度
2.這個子元素必須是絕對定位position: absolute
3.top、left、bottom、right皆設定為0;
4.設定margin:auto;
```
.wrapper {
position: relative;
}
.item {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
width: 200px;
height: 100px;
}
```
參考範例 https://codepen.io/knstbp/pen/zYyqQad
<br>
## 8. 新版手機的CHROME和SAFARI的問題
**目前己知手機的Chrome 和Safari 的瀏覽器, 如果使用100vh,會忽略掉瀏覽器的選單,如下圖1**

**目前解法是使用JS去控制偵測瀏覽器的真實可視範圍,應用在文件上,100vh就會正常,如上圖2**
以下為程序
css 部份
```
html,
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
height: var(--app-height);
}
```
js部份
```
<script>
//修改網頁文件的正確高度為偵測手機瀏覽器的可視高度
const appHeight = () => {
const doc = document.documentElement
doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()
</script>
```
**注意:上面JS只有改文件(body和html)部份的VH,如果內容物也使用VH,參照比例一樣會是錯誤的,所以內容的單位都改成百分比才能正確使用
<br>
## 9. 善用flex-box的對齊,Maring和Padding盡量不使用固定尺寸
1.子元素整體對齊時,盡量用父元素去做左右/垂直自動對齊,子元素再去做相對應的微調(也是盡量用百分比)
2.子元素距離父元素的邊要有一些空間時,也可以優先選擇父元素padding去定義內距
3.margin和padding如果都使用固定尺寸,也很容易會產生@media 在各尺寸都需要設定,才會達到比較好的間距比例
<br>
## 10. 關於彈性取得預設值及最大最小值的Clamp()、min()和max()
### max()
是用來定義則是取得兩個計算數值的的「較大值」,例:max(預設計,最小值) 比如說我們以前會像這樣定義 CSS:
```
div{
width:80vw;
min-width:200px;
}
```
如果是在 max() 就可以改成這樣寫:
```
div{
width:max(80vw, 200px);
}
```
意思就是寬度最小是 200px,否則就使用 80vw
因為max()是數學函數,可以在括號內進行數學式的運算 ( 注意 CSS 的算式之間必須有空白 )
```
h1{
font-size:max(1rem + 1vw, 20px);
}
```
### min()
min(預設計,限制的最大值),則是取得兩個計算數值的「較小值」
像是原本我們會這樣寫:
```
div{
width:80vw;
max-width:200px;
}
```
改成用 min() 的話就會是像這樣:
```
div{
width:min(80vw, 200px);
}
```
這樣就會限制最大寬度是 200px,預設的寬度為 80vw。
### clamp()
clamp() 則是可以同時限制最大值、最小值以及給定預設值,所以可以給三個值分別為 clamp(MIN最小值, VAL預設值, MAX最大值),不過更精確的說應該是 max(MIN, min(VAL, MAX))。
用範例來說明,原本我們可能這樣寫:
```
div{
width: 100%;
min-width:640px;
max-width:960px;
}
```
改成用 clamp() 就是
```
div{
width: clamp(640px, 100%, 960px);
}
```
這樣就會設定預設為 100%,但長度是在 640px 到 960px 之間變化
參考範例 https://codepen.io/knstbp/pen/gOZrJeE
另外附上瀏覽器支援度

<br>
## 11. 關於AUTO
原文 https://ishadeed.com/article/auto-css/
> auto關鍵字的使用因屬性而異,以下介紹在不同屬性中的使用auto的情況。
### <div style="background:lightgrey;padding:5px 0;">Width: Auto</div>
一些基礎區塊元素像 div 或 p 會自動填滿父元素寬度
> 一個元素的寬度被設置成auto時, 即使設置了margin,padding,border屬性,也不會超出其父元素的水平空間。 內容框的寬度= 內容本身 - margin值 - padding值 - border值。
> 如果我們把元素的width的值從auto改成100%,這個元素就會佔用其父元素寬度的100%,加上左右兩側的margin值及border值;

測試網址 https://codepen.io/shadeed/pen/PoqRpXx/f305220fbd4b444371bdef11dad014ec?editors=1100
### <div style="background:lightgrey;padding:5px 0;">Height: auto</div>
對於height這個屬性來說, 和width的情況完全不同。一個元素的高度等於子元素自身實際內容佔用的高度,默認值就是auto
以下圖為例,綠底的item 如果設定height是100%,他就會跟著父元素的可用高度(扣掉padding)
```
<div class="wrapper">
<div class="item">
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eos maxime cum non cupiditate, perferendis saepe vitae fugiat id exercitationem officiis voluptate sint ducimus commodi reiciendis error itaque dolores ipsam? Ea!Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eos maxime cum non cupiditate, perferendis saepe vitae fugiat id exercitationem officiis voluptate sint ducimus commodi reiciendis error itaque dolores ipsam? Ea!Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eos maxime cum non cupiditate, perferendis saepe vitae fugiat id exercitationem officiis voluptate sint ducimus commodi reiciendis error itaque dolores ipsam? Ea!</p>
</div>
</div>
```

item 的高度改成auto 之後, 就會依內容物的車際高度撐開

### <div style="background:lightgrey;padding:5px 0;">Margin: auto</div>
一個元素的margin-left和margin-right的值都是auto, 那麼這兩個的使用的真實值就是相等的,也會使得元素相對於父元素的邊緣水平居中。

如果是在絕對定位中使用margin:auto
前提條件:
1.這個子元素的必須有設定寬度和高度
2.這個子元素必須是絕對定位position: absolute
3.top、left、bottom、right皆設定為0;
### <div style="background:lightgrey;padding:5px 0;">Flex裡的Margin: auto</div>
在flex佈局的父元素內,給子元素的margin 設置為auto 會讓這個子元素被"推到" 對應的另一邊,這在有些應用場景下是非常有用的。比如, 如果一個flex佈局的元素設置成margin-left: auto, 那麼它就會被“推到” 最右邊。
```
.wrapper {
display: flex;
}
.item-2 {
margin-left: auto;
}
```

垂直方向同理:

如果flex 區塊裡只有一個元素可以用 來margin: auto將其水平以及垂直居中

### <div style="background:lightgrey;padding:5px 0;">定位屬性:top, right, bottom,left的auto</div>
首先來個範例,我們在父元素裡加入padding,子元素設定Absolute;
```
.wrapper {
position: relative;
padding: 16px;
}
.item {
position: absolute;
width: 100px;
height: 100px;
}
```
這時會發現子元素的定位並不是在左上角

這時再看子元素的left和top都會是父元素的padding值 , 16px,同時這也是相等於設定 left:auto; top:auto;left:auto將使得元素定位時遵循父元素的padding 屬性,而不是遵循父元素的邊界。

這時我們再自己設定left及top都為0,就會回去父元素的邊邊囉(父元素要為relative),<font style="color:red">一個絕對定位的元素是相對於離其最近的相對定位的父元素來定位</font>

#### 另外範例1
通過給.card .icon設置left: auto,就可以很方便的將其放置在卡片的右側了。

```
.card .icon {
position: absolute;
left: 15px;
top: 15px;
}
.card.is-right .icon {
left: auto;
right: 15px;
}
```
參考範例 https://codepen.io/knstbp/pen/vYvGwrQ
#### 另外範例2 Flex 佈局中使用margin: auto
以下圖這個佈局效果為例,每一行都包含一個標題,描述和一個動作按鈕,而且動作按鈕要始終置於每一行的右側位置,我們就可以通過給動作按鈕設置margin-left:auto來實現:

```
<div class="item">
<div class="item-group">
<!-- Title and description -->
</div>
<button class="item__action">Confirm</button>
</div>
```
```
.item {
display: flex;
flex-wrap: wrap;
}
.item__action {
margin-left: auto;
}
```
參考範例 https://codepen.io/knstbp/pen/
## 12.運用 CSS @supports 解決瀏覽器差異問題
#### 其實就是讓我們指定 CSS 應該如何執行。例如我們在不同斷點須撰寫特定樣式時會使用的 @media 就是一種 at-rule:
```
@media screen and (min-width: 900px) {
article {
display: flex;
}
}
```
類似於 @media,@supports 讓我們告訴瀏覽器:「當你懂括號內的屬性和值,再套用裡面的 CSS。」使用方式如下:
```
@supports (<supports-condition>) {
/* 當樣式被瀏覽器所支援,才套用此處的樣式 */
}
```
也可以像是 @media 那樣,運用 and 撰寫多個條件:
```
@supports (<supports-condition>) and (<supports-condition>) {
/* 當兩個樣式都被瀏覽器所支援,才套用此處的樣式 */
}
```
### 實際應用——Firefox 滾動軸問題
Firefox 對於滾動軸的 CSS 語法支援與其他主流瀏覽器是不同的。當我們這樣撰寫時:
```
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: red;
}
```
在 Firefox 上是不會作用的,我們需要撰寫 Firefox 專用的滾動軸屬性才能套用至 Firefox 上
```
/* Supports firefox */
@supports (scrollbar-width: thin) {
* {
scrollbar-color: red #fff;
scrollbar-width: thin;
}
```
### not 運算符
當瀏覽器不支援某屬性時,便會套用其中的樣式:
```
@supports not (<supports-condition>) {
/* 當樣式不被瀏覽器所支援就套用此處的樣式 */
}
```
### 實際應用 — — aspect-ratio 問題
aspect-ratio 讓我們可以為元素定義長寬比,這個屬性在 2021 年下旬才慢慢被 Safari 所支援,而至今仍有部份裝置使用者使用的是較舊版 iOS 的裝置,例如:
```
div {
background-color: red;
width: 300px;
aspect-ratio: 16 / 9;
}
```
這樣的樣式在較舊版本的 Safari 可能就會無法正常顯示。此時我們就可以使用 not 搭配 padding-hack 去解決這個問題:
```
div {
background-color: red;
width: 300px;
aspect-ratio: 16 / 9;
}
/* fallback to use padding-hack */
@supports not (aspect-ratio: 16 / 9) {
div {
padding-top: 56.25%;
}
}
```
除了 not 、 and 之外,我們也能使用 or :
```
@supports (transform-style: preserve) or (-moz-transform-style: preserve) {
/* 當瀏覽器支援 transform-style: preserve 或 -moz-transform-style: preserve 時,採用其中樣式 */
}
```
甚至可以使用 selector:
```
@supports selector(h2 > p) {
/* 當瀏覽器支援子瀏覽器時,採用其中樣式 */
}
```