兼容性
第一個問題是:CSS 自定義屬性能用在哪?目前從 Can I use 上獲取的信息顯示除了 Edge 外主流瀏覽器的最新版都已經支持這個特性了,而 Edge 也將支持這個屬性。
這說明現在 CSS 自定義屬性已經能用在實際項目中了,相信不久以後開發者們將大大依賴這個特性。但還請在使用之前請先檢查一下本文附錄中 Postcss 對於 CSS 自定義屬性的支持情況,以便做好兼容。
設一個值,任何值都可以
那麽……什麽是自定義屬性呢?簡單來說就是一種開發者可以自主命名和使用的 CSS 屬性。瀏覽器在處理像 color 、 position 這樣的屬性時,需要接收特定的屬性值,而自定義屬性,在開發者賦予它屬性值之前,它是沒有意義的。所以要怎麽給 CSS 自定義屬性賦值呢?這倒和習慣無異:
.foo { color: red; --theme-color: gray; }自定義元素的定義由 -- 開頭,這樣瀏覽器能夠區分自定義屬性和原生屬性,從而將它倆分開處理。假如只是定義了一個自定義元素和它的屬性值,瀏覽器是不會做出反應的。如上面的代碼, .foo 的字體顔色由 color 決定,但 --theme-color 對 .foo 沒有作用。
你可以用 CSS 自定義元素存儲任意有效的 CSS 屬性值:
.foo { --theme-color: blue; --spacer-width: 8px; --favorite-number: 3; --greeting: "Hey, what's up?"; --reusable-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.85); }
使用
假如自定義屬性只能用於設值,那也太沒用了點。至少,瀏覽器得能獲取到它們的屬性值。使用 var() 方法就能實現:
.button { background-color: var(--theme-color); }
下面這段代碼中,我們將 .button 的 background-color 屬性值賦值爲 --theme-color 的值。這例子看起來自定義屬性也沒什麽了不起的嘛,但這是一個硬編碼的情況。你有沒有意識到, --theme-color 的屬性值是可以用在任意選擇器和屬性上的呢?這可就厲害了。
.button { background-color: var(--theme-color); } .title { color: var(--theme-color); } .image-grid > .image { border-color: var(--theme-color); }
缺省值
如果開發者並沒有定義過 --theme-color 這個變量呢? var() 可以接收第二個參數作爲缺省值:
.button { background-color: var(--theme-color, gray); }注意:如果你想把另一個自定義屬性作爲缺省值,語法應該是 background-color:var(--theme-color,var(--fallback-color))
傳參數時總是傳入一個缺省值是一個好習慣,特別是在構建 web components 的時候。爲了讓你的頁面在不支持自定義屬性的瀏覽器上正常顯示,別忘了加上兼容代碼:
.button { background-color: gray; background-color: var(--theme-color, gray); }
作用域和級聯
要在什麽時候定義這些屬性?在使用之前嗎?自定義屬性遵從標準的作用域和級聯槼則,開發者按照平時使用的習慣來就可以了!你可能希望將 --theme-color 設置爲全局變量,處處可用。最簡單的方法是使用 :root 偽元素:
:root { --theme-color: gray; }這樣定義以後,無論是按鈕、標題還是圖片網格乃至整個文档,都可以使用 --theme-color了,
但當你希望不同的模塊使用不同的 --theme-color 值怎麽辦呢?和初始化自定義屬性的步驟相同,只需要在模塊的作用域中給屬性重新賦值,新的顔色就會分模塊生效,而不需要開發者一個個重置使用到 --theme-color 的屬性。
section.about { --theme-color: darkblue; } section.contacts { --theme-color: darkred; } section.news { --theme-color: teal; }
當然,你也可以定義複雜的選擇器槼則,應用特定的屬性值:
section.news > .sidenote { --theme-color: gray; }
CSS 計算
calc() 函數常常被用於跨單位的計算:
.child { width: calc(100% - 16px); }事實上這個計算是在瀏覽器運行時進行的,瀏覽器會將 calc() 的計算結果以像素單位呈現在屏幕上。
calc() 與 CSS 自定義屬性結合以後會很有趣!這一點也是在制定自定義屬性槼範時,經過了深思熟慮才加上的,畢竟這兩者的結合可謂是強強聯手:
:root { --base-size: 4px; } .title { text-size: calc(5 * var(--base-size)); } .body { text-size: calc(3 * var(--base-size)); }
只要最終的結果有意義,開發者無論使用什麽樣的單位都可以:
:root { --base-size: 4px; --title-multiplier: 5; --body-multiplier: 3; } .title { text-size: calc(var(--title-multiplier) * var(--base-size)); } .body { text-size: calc(var(--body-multiplier) * var(--base-size)); }
CSS 和 JavaScript 間的橋梁
自定義屬性和 Sass、Less 或者 PostCSS 這些處理器語言一個非常重要的不同點在於:瀏覽器是可以解析自定義屬性的。這就意味著開發者可以動態改變自定義屬性的值。這是 CSS 邁出的一大步。就和平時用 JS 操作元素任意的屬性一般,自定義屬性也可以通過 getPropertyValue 和 setProperty 方法操作 :
const styles = getComputedStyle(document.querySelector('.foo')); // Read value. Be sure to trim to remove whitespace. const oldColor = styles.getPropertyValue('--color').trim(); // Write value. foo.style.setProperty('--color', 'green');
屬性值一旦被改變,所有與這個自定義屬性相關的 CSS 屬性也都會發生改變,倣佛是開發者直接操作了那些 CSS 屬性值似的。這樣就能輕松實現批量修改元素的屬性值。
關於 JS 和 CSS 自定義屬性之間的合作和最佳實踐,大家可以詳細閲讀這篇文章。
附錄:使用預處理器編寫 CSS 自定義屬性
相信你已經迫不及待地想用上這個新技術,但又要考慮兼容尚未實現此特性的瀏覽器,那目前最好的選擇就是使用 PostCSS。
PostCSS 現在可以支持 CSS 自定義屬性的基本功能,也可以防止開發者不斷重複編寫自己的 CSS,但是像和 JS 結合動態改變自定義屬性值,這相對高級的用法暫時還沒有實現方案。
讓我們來看看 PostCSS 能支持的特性和具體表現情況吧:
上表中對比的兩個 Postcss plugin 爲
postcss custom properties: 網頁鏈接
postcss css variables: 網頁鏈接
PostCSS CSS Varibales 中會出現編譯問題的鏈接從上到下分別是:
Caveats: 網頁鏈接
Issue 30: 網頁鏈接
Caveats: 網頁鏈接
若需要用 calc() 則都要和 Postcss Calc 配合使用。