# 10個CSS小技巧 :::info :bulb: 一項投票調查中,關於Web開發人員合作中使他們最痛苦的技術,CSS位居榜首。 ::: CSS確實臃腫且難以全面學習,但這只是因為它在過去25年中不斷發展。它出現在1996年,netscape那時還是top瀏覽器。而如今RWD(Responsive Web Design)的想法也已有十多年,越來越多瀏覽器進入市場,它們又以不同方式去呈現CSS。這導致code在一個瀏覽器中工作,另一個又不工作,使得開發人員就必須在代碼中編寫一堆令人困惑的供應商前綴。 ```css .lame-sauce { -webkit-transition: all 4s ease; /* chrome */ -moz-transition: all 4s ease; /* firefox */ -ms-transition: all 4s ease; /* IE */ -o-transition: all 4s ease; /* opera */ transition: all 4s ease; } ``` 所以我今天會介紹幾個最近在做side project查資料時,看到國外大佬使用的一些tips。讓我學習用現代功能編寫乾淨的CSS,同時避免3202年還在編寫意義不明的:poop:。 ## :book: 學習Box Model :::success "學習時不要用==bootstrap==或==tailwind==這樣的框架。它們確實非常sexy,可以快速幫助你獲得一個漂亮的UI。但在你的project中用這類東西,就像結婚一樣,你不會學習到CSS基礎知識,當你主意變了,你還必須歷經離婚一般的痛苦。" ---Fireship ::: 當你學習基本的CSS,你將更好更自由的去控制你的code。 開始說回第一個標題。關於Box Model,你可以先把每個html元素都視為一個box,每個box都有自己的寬高,你可在內壁填充去擠壓內容。外部可以添加邊框,而周邊還有個不可見的範圍叫做邊距。 CSS中與佈局和位置相關的所有內容都受到Box Model的影響,所以關於如何應用就顯得更為重要。 打開瀏覽器的開發人員選項有圖化形界面可以參考。 ![](https://hackmd.io/_uploads/SkZ5KhhE2.png) ## :book: 調試時使用Firefox Firefox的開發工具更優秀,尤其是涉及CSS時。檢查一個元素時,能像在chrome中那樣分解Box Model,列出所有屬性細目,甚至也可以直接在上面編輯屬性。 ![](https://hackmd.io/_uploads/BkQdAhhV2.png) 它還在HTML中提供了有料的註釋,例如當一個元素使另一元素溢出時會有overflow標記。 ![](https://hackmd.io/_uploads/HyUj46nVn.png) 它還為flex和grid提供了很好的圖形。 ![](https://hackmd.io/_uploads/BkgaxH63N2.png) ## :book: Flexbox好棒棒 關於佈局和元素間的定位,歷史上一直是CSS最具挑戰性的方面之一。就像問你如何在水平和垂直方向居中一個div這種古老問題。一種方法是給child絕對定位,然後用top和left屬性把它移動到右下角,然後反向位移50%讓它回到中間。但它非常不直觀,也讓看的人也覺得很有病。 ```css .classic { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } ``` 使用flex就相當於給div一個x、y軸,想要置中就讓它沿著水平軸和鉛直軸對齊就好了。 ```css .flex { display: flex; justify-content: center; align-items: center; } ``` 雖然flex用起來挺簡便的,但如果你今天遇到一個很大還很複雜的UI,它的一個主要缺點就出來了。許多相交的行與列,會使你HTML中有很多容器或包裝元素。這些元素沒有語意意義,它們只會一直待在那裡。 ```htmlmixed <div class="row-wrapper"> <div class="column-wrapper"> <div class="box">💩</div> <div class="box">💩</div> </div> <div class="column-wrapper"> <div class="box">💩</div> <div class="box">💩</div> </div> <div class="column-wrapper"> <div class="box">💩</div> <div class="box">💩</div> </div> </div> ``` ```css .row-wrapper { display: flex; align-items: center; justify-content: space-between; } .column-wrapper { display: flex; flex-direction: column; align-items: center; justify-content: center; } ``` ## :book: Grid好棒棒 上述的問題,grid佈局可以幫你解決。它不像felxbox只能處理單獨的行與列。而它看起來像舊時代的表格佈局。 ![](https://hackmd.io/_uploads/S1EtCThEn.png) 你若使用grid,你的code會相對友善一些。你可以將children定義為一堆列元素,行列寬度可以使用模板列屬性定義。這裡注意一下fr值,它會與grid中其它列元素共享可用空間。我們還可以定義一些行,grid內元素都將自動定位。和flexbox或table layout相比,能減少許多HTML和CSS的code數量。 ```htmlmixed <div class="grid"> <i>😎</i> <i>😎</i> <i>😎</i> <i>😎</i> <i>😎</i> <i>😎</i> </div> ``` ```css .grid { display: grid; grid-template-columns: 1fr 500px 1fr; grid-template-rows: 100px 200px; place-items: center; } ``` ![](https://hackmd.io/_uploads/BJ2HzAhE3.png) ## :book: Clamp夾爛它 前面講了一堆關於佈局和定位的東西,而當我們在實現RWD時,我們要因應用戶的設備螢幕可用空間調整畫面呈現。這時候有很多方式能做到這點。 例如你可能有一篇文章,它首選寬度為50%。但在小螢幕上,你可能會希望它寬度固定在200px。而換到大螢幕時,又希望它固定在800px。你可以透過==media query==來實現。 ```css article { width: 50%; } @media only screen and (max-width: 600px) { article { width: 200px; } } @media only screen and (max-width: 1200px) { article { width: 800px; } } ``` 但唯一的缺點是,==media query==會讓你在project變大時想自殺。 但好消息是你可以透過使用==min==、==max==、==clamp==之類的函數去改寫它。 ```css article { width: clamp(200px, 50%, 600px); } ``` ## :book: 用emoji挑戰你的同事與上司 當你的className長到你想死的時候,試試看用emoji代替。為你煩躁的一天增添些許樂趣。 ![](https://hackmd.io/_uploads/ryGqvC2Vh.png) ![](https://hackmd.io/_uploads/BJlsw0243.png) ## :book: 使用Aspect Ratio 如果你曾經不得不編寫一個保持一定寬高比的RWD圖片或影片(好像有種既視感🤔,你說對吧,日程本),這東西會make u shock😮 例如今天你想放一個16x9的影片,你可能會被迫寫出一個像這樣的鳥東西。 ```css .container-16x9 { position: relative; padding-top: 56.25%; } video { width: 100%; position: absolute; top: 0; } ``` 這時候,你可以使用==aspect-ratio==這屬性。 ```css video { width: 100%; aspect-ratio: 16/9; } ``` 但有個缺點是老地方不一定支援這屬性,這部份自己斟酌。 ## :book: 創造變量 除了減少代碼量,更重要的一點是讓code更靈活,這樣在改寫和重構時才不會讓人想死。一種方式是用CSS自定義屬性和變量。像下列code中,我們在多個地方使用了相同顏色,當我們想更改顏色時,你會開始想鑽個洞把自己埋進去。 ```css p { color: rgb(255, 0, 0); } h1 { color: rgb(255, 0, 0); } h2 { color: rgb(255, 0, 0); } ``` 而更好的方法是在==root==選擇器上定義一個全局變量,然後在需要的地方進行引用。 ```css :root { --text-color: rgb(255, 0, 0); } p { color: var(--text-color); } h1 { color: var(--text-color); } h2 { color: var(--text-color); } ``` 你甚至可以在tree更深的的地方重新定義去override。 ```css :root { --text-color: rgb(255, 0, 0); } p { --text-color: rgb(0, 255, 0); color: var(--text-color); } ``` 抑或分解組合成得更細。 ```css :root { --r: 255; --g: 0; --b: 0; --text-color: rgb(var(--r), var(--g), var(--b)); } p { --text-color: rgb(0, 255, 0); color: var(--text-color); } ``` ## :book: 花式calc 現在CSS不是傳統意義上的編程語言,但它確實能夠使用calc函數進行基本運算。了解這點的你,現在你可以快樂的和小學生一起做算術題了。 ```css calc(2px + 2px) ``` 單位甚至不用相同。 ```css width: calc(100vw - 80px); font-size: calc(1rem * 1.25); padding: calc(5% + 2px); ``` 更酷的是,你可以這樣做動畫,就能在不增加CSS代碼的情況下實現相同效果。 ```htmlmixed <style> .drop { animation: dropIn 1s ease forwards; animation-delay: calc(var(--order) * 100ms); } @keyframes dropIn { from { transform: translateY(-500px); } to { transform: translateY(0); } } </style> <i class="drop" style="--order: 1">1️⃣</i> <i class="drop" style="--order: 2">2️⃣</i> <i class="drop" style="--order: 3">3️⃣</i> ``` ## :book: 狀態管理計數器 雖然剛才說CSS不是一種編程語言,但它實際上有一個內置的狀態管理機制,你可以追蹤你code中的運行記數,而不用多編寫一行JavaScript。 若你想對標題進行編號,最白癡的方法就長這樣。 ```htmlmixed <h1> 1. apple </h1> <h1> 2. bird </h1> <h1> 3. dog </h1> ``` 當你突然想要從中間補上一個新的標題時,就會有種想去頂樓自由落體的感覺。 ```htmlmixed <h1> 1. apple </h1> <h1> 2. bird </h1> <h1> 3. cat </h1> ←←←←←←←←←←←←←← <h1> 3. dog </h1> ``` 聰明的方法是去root創建一個計數器,用==counter-reset==給一個你喜歡的名字,然後在需要應用的地方去用==counter-increment==遞增它,它將從0開始將1添加到dom的每個h1元素。 ```css :root { counter-reset: headings; } h1 { counter-increment: headings; } h1::before { content: counter(headings); } ``` ## :book: focus-within的魔力 構建複雜下拉式選單時,你可能會認為這必須涉及一些JavaScript來管理選單的開啟和關閉state,但你可能也會想你若只透過純CSS可以做到多遠。 大家都很熟悉==focus==這偽類,當你進行表單輸入或單擊按鈕時,該偽類會應用於元素上。問題是,在構建下拉式選單時,你可能會使用==focus==打開選單,但當你點擊選單內的某個內容時,它會失去焦點並關閉。這就為何你會使用JavaScript去管理state。 但有趣的是,有個偽類叫做==focus-within==,如果任何children也有這偽類,它就會保持active的狀態。 這樣簡單的一個功能,可以多減少幾行用於管理打開和關閉state的JavaScript代碼。 ```css .dropdown { opacity: 0; visibility: hidden; } button:focus-withing .dropdown { opacity: 1; visibility: visible; } ``` ## :book: 結語 這樣,我們就簡單提完了幾個CSS技巧。還記的剛開頭提到的瀏覽器供應商前綴嗎,那東西就像皰疹一樣不會消失。但幸運的是我們有些有料的小工具可以讓它變得不成問題。 其中一個工具就是==PostCSS==(看起來像光明會來的),它使用一個叫做auto prefixer的工具,來自動添加所有供應商前綴。此外,它還允許你使用現代CSS功能,會幫你自動轉換代碼,以盡可能讓所有目標瀏覽器都支援。你還可以去看看==Sass==、==stylus==、==less==等預處理器。之後有緣再分享。 ![](https://hackmd.io/_uploads/H1FmMgpVn.png)