如何更愉快地使用rem —— 別說你懂CSS相對單位

Yuying_Wu發表於2018-07-16

前段時間試譯了Keith J.Grant的CSS好書《CSS in Depth》,其中的第二章《Working with relative units》,書中對relative units的講解和舉例可以說相當全面,看完之後發現自己並不太懂CSS相對單位,也希望分享給大家,所以有了這個譯文系列。(若有勘誤或翻譯建議,歡迎 Github PR ^_^)

《別說你懂CSS相對單位》系列譯文:

本文對應的章節目錄:

  • 2.2 em和rem
    • 2.2.2 對font-size使用rem
      • 可用性:對font-size使用相對長度單位
  • 2.3 停止使用畫素思維去思考
    • 2.3.1 設定一個合理的字號預設值
    • 2.3.2 讓這個皮膚變得“響應式”
    • 2.3.3 調整單個元件的大小

2.2 em和rem

2.2.2 對font-size使用rem

當瀏覽器解析HTML文件時,建立了一個用來代表頁面元素的集合,叫做DOM(文件物件模型,Document Object Model)。樹狀結構,每一個節點代表一個元素。<html>就是頂層節點(根節點),在下面的是它的子節點<head><body>,再往下就是它們的子節點,還有後代節點,如此類推。

根節點是文件裡所有其他元素的祖先。它有一個特別的偽類(pseudo-class)選擇器(:root),在樣式表裡可以用這個選擇器表示。使用帶類名的型別選擇器html,或者直接用標籤選擇器,效果是一樣的。

rem是根em(root em)的縮寫。rem是和根元素關聯的,不依賴當前元素。不管你在文件中的什麼地方使用這個單位,1.2rem的計算值是相等的,等於1.2倍的根元素的字號大小。下面的示例程式碼中,宣告瞭根元素的字號大小,並在巢狀的無序列表中使用rem宣告字號大小。

[ 程式碼片段 2.10 使用rem宣告字號大小 ]

  • 1 偽類 :root 等價於 html 選擇器
  • 2 使用瀏覽器的預設字號大小(16px)

在這個示例裡,根字號大小是瀏覽器的預設大小16px(根元素的1em等於瀏覽器的預設字號大小)。無序列表的字號大小為0.8rem,計算結果是12.8px。因為這隻跟根元素相關,儘管你在列表裡巢狀了列表,巢狀子列表的字號仍然保持不變。

可用性:對font-size使用相對長度單位

一些瀏覽器會提供給使用者2種方式定製文字的大小:縮放和設定一個預設的字號大小。通過按Ctrl+或者Ctrl-,使用者可以對頁面進行縮放。這在視覺上會把整個頁面的文字或圖片(其實是所有元素)都放大或縮小了。在一些瀏覽器,這個改變只針對當前的標籤頁且是臨時的,不會影響到新開的標籤頁。

設定預設字號大小,會有點不一樣。不僅僅是設定的入口比較難找(一般在瀏覽器的設定頁),而且這個設定是永久的,直到使用者把預設值還原。值得注意的是,這個設定對使用px或其他絕對單位定義的字號大小無效。因為預設字號大小對一些使用者是必要的,尤其是弱視的群體,你應該用相對單位或百分比來定義字號的大小。

rem簡化了很多em帶來的複雜度。事實上,rem提供了一個在px和em間的相對單位折中解決方案,而且更易於使用。那麼,是不是意味著你應該在對所有元素都使用rem,去掉其他長度單位呢?當然不是。

在CSS的世界裡,這個答案通常是,看情況。rem只是你的工具箱中的其中一個。掌握CSS很重要的一點,就是學會分辨在什麼場景下該使用什麼工具。我的選擇是,對font-size使用rem,對border使用px,對其他的度量方式如paddingmarginborder-radius等使用em。然而在必要時,需要宣告容器的寬度的話,我更喜歡使用百分比。

這樣,字號大小就變得可預測,而當其他因素影響到元素的字號大小時,你也可以藉助em去縮放元素的padding和margin。在border上使用畫素是很合適的,尤其當你想要一根漂亮的線的時候。以上就是我對不同屬性使用不同單位的理想方案,不過我要再次宣告,這些都是工具,在某些特定場景下,利用不同的工具可能取到更好的效果。

提示

當你不確定的時候,對font-size使用rem,對border使用px,以及對其他大多數屬性使用em。

2.3 停止使用畫素思維去思考

把頁面的根元素字號大小定義為0.625em或者62.5%,在最近幾年來,這樣的用法很常見,這是一種模式,或者更貼切地說,這是一種反模式。

[ 程式碼片段 2.11 反模式:全域性地把font-size定義為10px ]

我並不推薦這種用法。這個用法把瀏覽器預設的字號大小16px縮小到10px。這樣做的好處是簡化了計算,如果設計師告訴你字號大小應該是14px,那你可以很輕易地計算出1.4rem,畢竟我們還是在使用相對單位。

一開始,這看起來很方便,但事實上這樣的實現方式有兩個問題。第一,強制你寫了很多重複的樣式程式碼。10px對於大多數文字來說太小了,你需要在整個頁面中,來來回回地覆蓋它。你會發現,自己把一段段落(<p>)的字號大小宣告為1.4rem,然後又把導航(<nav>)的連結字號大小宣告為1.4rem,樣式程式碼中還有很多這樣的用法。這樣引入了更高的錯誤風險,當你需要修改時發現程式碼耦合程度比較高,同時也會讓樣式檔案變大。

第二個問題是,你這麼做的時候,其實你還是在用畫素的思維在思考。雖然在程式碼裡寫的是1.4rem,但是在你的腦子裡,其實還是想的是14px。在響應式網頁開發中,你應該學會適應那些“模糊”的值。1.2em實際等於多少畫素,並不重要,你只需要知道這是比繼承的字號大一點點,那就足夠了。而且,如果在螢幕上這不是你想要的效果,那就改吧。這是需要時間實驗和試錯的,但事實上,使用px的時候我們也需要這樣做。(在第13章,我們會有更具體的方式來優化這個實現方式。)

當使用em時,我們很容易陷入糾結,這個值轉化成畫素值會是多少呢?尤其對於字號大小。你一直在乘和除以em值,這樣你很快就會瘋掉了。相反,我希望你可以接受一項挑戰,嘗試培養先開始使用em的習慣。如果你習慣使用畫素,那轉成em是需要一定時間和練習的,但相信我,這很值得。

這不是在說你再也不使用畫素了。如果你跟一個設計師合作,你可能需要用更精確的畫素值去溝通,這沒問題的。在專案的開始,你需要宣告一個基礎的字號大小(通常是對標題或者標註的常用字號)。使用絕對值去描述大小,往往會更加容易。

轉換成rem會有計算環節,那就讓計算器去忙吧(通常我會在Mac電腦上按cmd+空格,在Spotlight裡計算)。首先在根元素上宣告根字號大小,從那開始,使用畫素應該是例外的情況,而不是常態。

在這章內容裡,我還是會持續地聊起畫素。這會有助於我解釋相對單位的工作原理,同時也能幫助你培養計算em值的習慣。在這章之後,我基本會使用相對單位來討論字號的大小。

2.3.1 設定一個合理的字號預設值

先假設你想把預設字號設定為14px。把10px設定為基準值,再在頁面中去覆蓋它的寫法,我們不推薦這種寫法,相反,你應該在根元素上直接宣告一個值。
在這個程式碼片段裡,目標字號值是繼承的,瀏覽器的預設值16px,那麼14/16 = 0.875。

把下面的程式碼新增到一個新的樣式表的最上面,我們會在這上面新增其他程式碼。這裡設定根元素(<html>)的預設字號大小。

[ 程式碼片段 2.12 設定正確的預設字號大小 ]

  • 1 或者使用 HTML 選擇器
  • 2 14/16(期望值px / 繼承值px)等於0.875

現在,你的期望基準字號14px對整個頁面的元素有效,你不需要在其他地方重新宣告瞭。你只需要在設計不一樣的地方修改成新的字號,譬如標題。

我們一起來建立圖2.7那樣的皮膚吧!你建立的這個皮膚,基於14px字號,使用相對單位。

[ 圖 2.7 使用相對單位和繼承字號的皮膚 ]

"圖 2.7"

下面是模板,加到你的頁面吧。

[ 程式碼片段 2.13 皮膚的模板 ]

下一段程式碼是樣式的。你會在paddingborder-radius使用em,標題的字號使用rem,以及border使用px。把下面程式碼新增到你的樣式表吧。

[ 程式碼片段 2.14 使用相對單位的皮膚 ]

  • 1 對padding和border-radius使用em
  • 2 用1px定義細邊框
  • 3 把皮膚上面多餘的空間去掉,更多解釋看第3章
  • 4 用rem控制標題的字號大小

這段程式碼給皮膚新增了一個細邊框以及定義了標題的樣式。我希望標題的字號小一點,但要加粗和全是大寫。(你可以根據自己的設計,把字號改大點或者使用不同的排版方式)

第二個選擇器>是一個直接後代組合選擇符(direct descendant combinator),它代表的是.panel下的子元素h2。更完整的選擇器和組合選擇符的索引可以看附錄A。

在程式碼片段2.13中,為了更清晰看到效果,我給body新增了一個類panel-body,不過你會發現,在你自己的程式碼裡是不需要的。因為這個元素從根元素上繼承了字號大小,它已經是你想要看到的那樣。

2.3.2 讓這個皮膚變得“響應式”

我們再更深入地看看這個問題。你可以根據螢幕尺寸的變化,新增媒體查詢來改變基礎字號大小,這可以令皮膚在不同尺寸的螢幕下,有不同的大小變化。(見圖2.8)

[ 圖 2.8 在不同螢幕大小的響應式皮膚:300px(左上角),800px(右上角),1440px(下面) ]

"圖 2.8"

媒體查詢(media query) —— 通過@media規則來宣告樣式,在不同的螢幕尺寸或者媒體型別(如印表機或顯示器)下,觸發對應的樣式控制。這是響應式設計的關鍵要素。詳情看程式碼片段2.15的這個例子,我在第8章會更深入的討論媒體查詢這個話題。

為了實現上面說的效果,把你的樣式程式碼改成這樣。

[ 程式碼片段 2.15 ]

  • 1 針對所有螢幕,但是在更大的螢幕會被覆蓋
  • 2 針對比800px更寬的螢幕,覆蓋預設樣式程式碼
  • 3 針對比1200px更寬的螢幕,覆蓋以上兩套樣式程式碼

第一套樣式規則,宣告瞭小螢幕中的預設字號大小,這是我們想要在較小的螢幕上看到的字號大小。然後使用媒體查詢,把800px和1200px分別作為兩個分水嶺逐級增加字號的大小,覆蓋掉預設的程式碼。

針對頁面的根元素使用這些字號大小,響應式地重新定義em和rem對應的值,從而達到響應改變整個頁面的效果。儘管你沒有直接對這個皮膚做任何的修改,它現在是響應式的。在小螢幕上,譬如一臺手機,字號大小會被渲染成更小的(12px)。然後,在更大的螢幕上,寬大於800px和大於1200px的,元件的字號會分別放大到14px和16px。改變你的瀏覽器視窗,看看元件是怎麼變化的吧。

如果你在整個頁面中像這樣嚴格使用相對單位,整個頁面會隨著視窗大小放大和縮小。這會是你的響應式策略裡很重要的一部分。上面的2套媒體查詢宣告程式碼,可以幫助你節省在頁面的其他部分使用媒體查詢的額外程式碼。不過,如果你在元素中宣告的字號大小是以畫素為單位的,那就不會產生任何效果了。

類似地,如果你的老闆或者客戶覺得現在網站的字號太小或者太大,你隨時可以做到通過修改一行程式碼影響到全域性的元素,這項改變會影響到頁面上的其他元素,不費吹灰之力。

2.3.3 調整單個元件的大小

你也可以通過使用em縮放頁面上的一個獨立元件。有時,你可能會需要介面上的某些元件可以有個大號的版本。在我們的皮膚上這麼做吧,首先你需要給皮膚新增一個類名large<div class="panel large">

在圖2.9,我們看到了皮膚的普通版和大號版的比較。效果類似響應式皮膚,但是兩種尺寸是可以同時在同一個頁面中使用的。

[ 圖 2.9 在一個頁面上的普通尺寸皮膚和大號皮膚 ]

"圖 2.9"

我們來對皮膚的字號宣告方式做一些小的修改。你還是在使用相對單位,但需要調整它們的基準值。第一點,給每個皮膚的父元素字號大小的定義font-size: 1rem。這裡指的是,不管在什麼地方使用這個皮膚,每個皮膚的字號大小是一個確定值。

第二點,使用em重新宣告標題的字號大小,而不使用rem,這樣標題就可以和剛才宣告的父元素字號1rem關聯起來。下面是對應的程式碼,更新下你的樣式表程式碼吧。

[ 程式碼片段 2.16 建立一個皮膚的大號版本 ]

  • 1 給元件宣告確定的字號大小
  • 2 其他元素的字號大小用em和父元素字號關聯

這些修改看起來並沒有影響皮膚的樣式,但是現在你已經準備好了,做一個大號的皮膚只需要修改一小行程式碼。你需要做的,就是把父元素字號大小改寫成1rem以外的一個值。因為其他元素的計算方式都依賴父元素的字號大小,只要修改它,整個皮膚的相關尺寸都會發生改變。新增下一個CSS程式碼片段到你的樣式表,定義一個大號皮膚吧。

[ 程式碼片段 2.17 利用一行程式碼放大整個皮膚 ]

  • 1 組合選擇器指向同時有panel類和large類的元素

現在,你可以給普通皮膚新增class="panel"和給大號皮膚新增class="panel large"。類似地,你也可以定義一個小號版本,只需要把父元素的字號設得比1rem小。如果這個皮膚是一個更復雜的元件,包含多種字號大小或padding,也只需要一個宣告就可以重置大小,只要所有的子元素都是使用em宣告的。

相關文章