# CSS 專家密技 - CSS Protips
[竹白筆記本](https://chupainotebook.blogspot.com/2019/03/css-css-protips.html),學習筆記,2019/03/21
###### tags: `CSS 專家密技` `CSS Protips`
>[CSS 專家密技 ](https://github.com/AllThingsSmitty/css-protips/blob/master/translations/zh-TW/README.md)
[CSS專家密技3 - CSS Protips - 阿莫斯の網頁料理室](https://www.youtube.com/playlist?list=PLqivELodHt3gYOrZe4oVUP4_TNRJunD5P)
---
# 1. 使用 CSS Reset
CSS Reset 可以幫你在不同的瀏覽器上維持一致的樣式風格。
幾套常見的 CSS Reset:
- [Reset CSS](https://meyerweb.com/eric/tools/css/reset/),此為 Eric Meyer 的版本
- [HTML5 Reset Stylesheet](http://html5doctor.com/html-5-reset-stylesheet/),HTML5 Doctor 網站修改自 - Eric A. Meyer 的版本。
- [CSS Reset - YUI Library](https://yuilibrary.com/yui/docs/cssreset/),由 Yahoo UI Library v3 所提供的 CSS Reset 版本。
因為 reset.css 重置了各個瀏覽器的樣式設定,使得有些有用、常用標籤的默認樣式必須要重新設定,因為這個問題,有人開發出了 [Normalize.css](https://necolas.github.io/normalize.css/)。
在 Normalize.css 的官方頁面上點出了他們的目標:
- 保留有用的瀏覽器默認設置,而不是將其刪除。
- 為廣泛的 HTML 元素提供一般化的樣式。
- 修正瀏覽器的 Bug 與不一致。
- 透過微妙的改善提高可用性。
- 有詳細的文檔來解釋代碼。(每個樣式都有註解是處理什麼問題。)
最大的特色就是保留原本預設 HTML 標籤的樣式,僅針對不同瀏覽器與各版本間不相容的標籤進行些微調整。
更簡潔的 CSS Reset 方法:
```css
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
```
---
# 2. 繼承 `box-sizing`
一般使用 `box-sizing` 都是:
```css
*, *::before, *::after {
box-sizing: border-box;
}
```
但假設今天有一個元件是使用 `box-sizing: content-box;`:
```css
.component {
box-sizing: content-box;
}
```
元件中的其他子元素也要是 `content-box`,但使用第一種方法設定 `box-sizing` 會影響到。
所以讓 `box-sizing` 屬性自動從 `html` 元素繼承下來 :
```css
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
```
如此一來,你就很容易的在其他外掛或元件裡改變 `box-sizing` 的值。
---
# 3. 使用 `unset` 而不是重置所有屬性
`unset` 從其父級繼承,則將該屬性重新設置為繼承的值,如果沒有繼承父級樣式,則將該屬性重新設置為初始值。
- 如果該屬性是默認繼承屬性,該值等同於 `inherit`
- 如果該屬性是非繼承屬性,該值等同於 `initial`
舉個例子,先列舉一些 CSS 中默認繼承父級樣式的屬性:
- 部分可繼承樣式:`font-size`, `font-family`, `color`, `text-indent`
- 部分不可繼承樣式:`border`, `padding`, `margin`, `width`, `height`
`all: unset;` 將元素中所有樣式屬性回復到預設值:
```css
button {
all: unset;
}
```
>[all|MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/all)
---
# 4. 使用 `:not()` 選擇器來決定表單是否顯示邊框
假設你用以下樣式先替元素新增邊框
```css
/* 新增邊框 */
.nav li {
border-right: 1px solid #666;
}
```
然後在最後一個元素去除邊框
```css
/* 去掉邊框 */
.nav li:last-child {
border-right: none;
}
```
不過你可以改用 `:not()` 偽類別 (pseudo-class) 來做到完全相同的效果:
```css
.nav li:not(:last-child) {
border-right: 1px solid #666;
}
```
當然,你也可以用 `.nav li + li`,但是使用 `:not()` 可以使語句更加清晰,更具可讀性。
**CodePen:**[:not 選擇器](https://codepen.io/CHUPAIWANG/pen/drzVYM)
<iframe height="265" style="width: 100%;" scrolling="no" title=":not 選擇器" src="//codepen.io/CHUPAIWANG/embed/drzVYM/?height=265&theme-id=0&default-tab=css,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/drzVYM/'>:not 選擇器</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
---
# 5. 將 `line-height` 加入到 `body` 元素
你不必為分別為每一個 `<p>`、`<h*>` 元素加入 `line-height` 樣式,相反的,你應該直接新增到 `body` 元素:
```css
body {
line-height: 1.5;
}
```
所有的文字元素預設就會繼承 `body` 的樣式。
---
# 6. 為表單元素設置 focus
給予使用者回饋,讓使用者可以距焦、確定所在位置。
```css
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus {
box-shadow: none;
outline: #000 dotted 2px;
outline-offset: .05em;
}
```
**CodePen:**[Search Box](https://codepen.io/chupai/pen/JeyOJJ)
<iframe height="265" style="width: 100%;" scrolling="no" title="Search Box" src="//codepen.io/chupai/embed/JeyOJJ/?height=265&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/chupai/pen/JeyOJJ/'>Search Box</a> by Chupai@Design
(<a href='https://codepen.io/chupai'>@chupai</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
## focus 在超連結中
考量到 CSS 的優先權,在超連結中要註意擺放順序,由上而下依序為:
```css
a:link {}
a:visited {}
a:focus {}
a:hover {}
a:active {}
```
---
# 7. 將所有元素設定垂直居中
此處有問題,故跳過。
>[CSS 垂直置中技巧 23 招](https://www.youtube.com/redirect?v=PiczFSV9xGg&redir_token=gMr7j4xI4g56HDaNOenf8-zplQl8MTU1MjM3NTM4MEAxNTUyMjg4OTgw&event=video_description&q=http%3A%2F%2Fcsscoke.com%2F2018%2F08%2F21%2Fcss-vertical-align%2F)
---
# 8. 逗號分隔列表
使列表的每項都由逗號分隔:
```css
ul > li:not(:last-child)::after {
content: ',';
}
```
列表中最後一項不用加逗號,所以可以使用 :not() 偽類別 (pseudo-class)。
---
# 9. 使用負數的 `nth-child` 來選擇元素
>[CSS3 :nth-child(n) 選取器教學](http://tinyurl.com/pr9m6b6)
使用負數的 `nth-child` 可以選擇 1 至 n 個元素。
```css
li {
display: none;
}
/* 選擇第 1 至第 3 個元素並顯示出來 */
li:nth-child(-n+3) {
display: block;
}
```
或者使用 `:not()` 選擇器來決定表單是否顯示邊框:
```css
/* 選擇除了前 3 個之外的所有項目,並顯示出來 */
li:not(:nth-child(-n + 3)) {
display: none;
}
```
---
# 10. 使用 SVG 圖示
SVG 在所有解析度下都可以良好縮放,並且支援 IE9 之後的所有瀏覽器。
**注意:** 如果你有一個只用 SVG 圖示的按鈕,只給看的見的人來點選。當 SVG 無法載入的時候,以下樣式可以幫助你維持網頁的可及性(Accessibility):
```css
.no-svg .icon-only::after {
content: attr(aria-label);
}
```
>[attr()|MDN](https://developer.mozilla.org/zh-TW/docs/Web/CSS/attr)
---
# 11. 貓頭鷹 選擇器
Lobotomized Owl(貓頭鷹)
```css
* + * {
margin-top: 1.5em;
}
```
在此範例中,在文件中所有的元素,只要緊接著其他元素,就會套用一個 `margin-top: 1.5em` 樣式。
:::danger
效能不好,不建議使用。
:::
---
# 12. 使用 `max-height` 來建立純 CSS 的捲動軸
你可以透過 `max-height` 與 `overflow-y: hidden` 來實作出 CSS-only 的捲動軸:
```css
.slider {
max-height: 200px;
overflow-y: hidden;
width: 300px;
}
.slider:hover {
max-height: 600px;
overflow-y: scroll;
}
```
當滑鼠移到 `.slider` 的元素時,元素的內容如果過多,最大高度只會擴展到 `max-height` 的值,而且會自動顯示捲動軸。
**CodePen:**[建立純 CSS 的捲動軸](https://codepen.io/CHUPAIWANG/pen/ZPXYqR?editors=1100)
:::warning
此方法有個缺點,因為需要鼠標移過去才會顯示卷軸,使用者會不知道此處可以捲動,所以需要額外增加提示。
:::
---
# 13. 讓表格中每個儲存格維持等寬
表格中要維持每一格都等寬是一件痛苦的是,所以你應該嘗試用 `table-layout: fixed` 來讓所有儲存格維持等寬:
```css
.calendar {
table-layout: fixed;
}
```
**CodePen:**[Equal-Width Table Cells](http://codepen.io/AllThingsSmitty/pen/jALALm)
---
# 14. 利用 Flexbox 去除多餘的 Margin 技巧
排版的時候,為了設計出每一欄的間隙(gutters),一般都會用到像是 `nth-`、`first-` 和 `last-child` 的技巧,來去除頭尾多餘的間隙,不如使用 Flexbox 的 `space-between` 屬性:
```css
.list {
display: flex;
justify-content: space-between;
}
.list .person {
flex-basis: 23%;
}
```
<iframe height="265" style="width: 100%;" scrolling="no" title="Flex justify-content: space-between" src="//codepen.io/CHUPAIWANG/embed/PLybBQ/?height=265&theme-id=0&default-tab=css,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/PLybBQ/'>Flex justify-content: space-between</a> by CHUPAIWANG
(<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
# 15. 利用屬性選擇器填滿空白連結的文字內容
當 `<a>` 元素沒有文字內容,但有 `href` 屬性的時候,可以這樣做:
```css
a[href^='http']:empty::before {
content: attr(href);
}
```
[`:empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty) 代表沒有子元素的元素。子元素只可以是元素節點或文本(包括空格)。
**CodePen:**[Use Attribute Selectors with Empty Links](https://codepen.io/CHUPAIWANG/pen/ywRgbN)
# 16. 幫沒有類別的連結設定一個預設樣式
幫沒有套用 class 的超連結設定一個預設樣式:
```css
a[href]:not([class]) {
color: #008000;
text-decoration: underline;
}
```
使用者透過後台 CMS 系統插入的超連結通常沒有 `class` 屬性,以上樣式可以針對這些超連結進行設定,且不會影響其它樣式定義。
# 17. 一致的垂直韻律
在元素中使用 通用選擇器 `*`,可以確保一致的垂直韻律 (consistent vertical rhythm):
```css
.intro > * {
margin-bottom: 1.25rem;
}
```
一致的垂直韻律可以提供視覺美感,並且讓內容更具可讀性。
:::info
沒啥用的技巧
:::
# 18. 等比例的方塊(RWD 相關)
要建立一個固定比例的方塊(Box),你需要的就是將 `padding-top` 或 `padding-bottom` 設定到 `<div>` 元素:
```css
.container {
height: 0;
padding-bottom: 20%;
position: relative;
}
.container div {
border: 2px dashed #ddd;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
```
在 `padding-bottom` 設定 `20%` 意味著各個 div 方塊的高度等同於 20% 的寬度。無論 Viewport 現在的寬度多少,子元素的 div 將維持其寬高比(100% / 20% = 5:1)。
**CodePen:**[Intrinsic Ratio Boxes](http://codepen.io/AllThingsSmitty/pen/jALZvE)
# 19. 為破圖定義樣式
只要一點 CSS 就可以美化所有破圖:
```css
img {
display: block;
font-family: Helvetica, Arial, sans-serif;
font-weight: 300;
height: auto;
line-height: 2;
position: relative;
text-align: center;
width: 100%;
}
```
接著新增一個 偽元素規則(pseudo-elements rules)來顯示使用者訊息,以及這張破圖的 URL 參考:
```css
img::before {
content: "We're sorry, the image below is broken :(";
display: block;
margin-bottom: 10px;
}
img::after {
content: '(url: ' attr(src) ')';
display: block;
font-size: 12px;
}
```
想學習更多這類樣式技巧,可以參考 [Ire Aderinokun](https://github.com/ireade/) 的 [原始文章](http://bitsofco.de/styling-broken-images/)。
# 20. 用 `rem` 來調整全域大小;用 `em` 來調整區域大小
在根元素設定基礎字體大小後(`html { font-size: 100%; }`), 使用 `em` 設定文字元素的字體大小:
```css
h2 {
font-size: 2em;
}
p {
font-size: 1em;
}
```
然後設定模組的字體大小為 `rem`:
```css
article {
font-size: 1.25rem;
}
aside .module {
font-size: 0.9rem;
}
```
現在,每個模組變得獨立,更容易、靈活的樣式便於維護。
>[CSS 中的 EM REM PX 解析與運用|六角學院](https://www.youtube.com/watch?v=sKrVyhaeZqU)
# 21. 隱藏沒有靜音並設定自動播放的影片
當你在一個可以自訂樣式的後台環境設定網站樣式時,這是一個不錯的小技巧。畢竟自動撥放影片是蠻惱人的,這個技巧可以幫助你避免影片在沒有靜音的情況下自動撥放。
```css
video[autoplay]:not([muted]) {
display: none;
}
```
# 22. 使用 `:root` 選擇器來設定彈性的字體大小
在響應式版面(responsive layout)中,字體大小通常需要根據不同的 Viewport(畫面大小)進行調整。你可以根據 `:root` 所定義的 Viewport 高度與寬度來調整字體大小:
```css
:root {
font-size: calc(1vw + 1vh + 0.5vmin);
}
```
現在你便能使用依 `:root` 字級為基準的 `rem` 單位了。
```css
body {
font: 1rem/1.6 sans-serif;
}
```
**CodePen:**[Use :root for Flexible Type](http://codepen.io/AllThingsSmitty/pen/XKgOkR)
# 23. 為了更好的行動體驗來設定表單元素的 `font-size`
為了避免使用者在行動瀏覽器(iOS Safari, 等等)點擊 `<select>` 的下拉選單時在 HTML 表單元素進行縮放,你可以加上`font-size` 到這些選取器樣式規則:
```css
input[type='text'],
input[type='number'],
select,
textarea {
font-size: 16px;
}
```
# 24. 使用 `pointer-events` 控制鼠標事件
[`pointer-events
`](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) 允許您指定鼠標如何與其觸摸的元素進行交互。要禁用按鈕上的默認指針事件,例如:
```css
.button-disabled {
opacity: .5;
pointer-events: none;
}
```