CSS變數(自定義屬性)使用指南 — SitePoint
CSS前處理器,如Sass和Less,使得CSS程式碼易於組織和維護。通過提供變數、混合、迴圈等特性,使得CSS具有動態編寫的能力,從而減少重複性工作,提高開發速度。
最近,CSS開始新增一些動態特性。CSS變數(自定義屬性)已經加入規範,並且獲得了大多數瀏覽器的支援。但是CSS混合特性還在進行中。
在這篇文章中,我們將會向你展示怎麼把CSS變數應用到開發中,從而使得樣式表更加可維護和DRY (Don’t Repeat Yourself)。
讓我們現在開始!
CSS變數是什麼?
如果你使用過任何程式語言,你肯定熟悉變數這個概念。變數讓你儲存和更新程式執行需要的值。
例如,考慮下面的JavaScript片段:
let number1 = 2; let number2 = 3; let total = number1 + number2; console.log(total); // 5 number1 = 4; total = number1 + number2; console.log(total); // 7
number1和number2是兩個變數,分別儲存數字2和3。
total也是一個變數,儲存number1和number2變數的和,在這個例子中是5。你可以動態更新這些變數的值,並且在程式的任何地方使用更新後的值。在上面的程式碼片段中,我把number1的值更新為4,當我使用相同的變數再次執行加法操作時,儲存在total中的值就變成7,而不是5了。
變數的好處在於你可以把值儲存在一個地方,然後在你需要的地方修改它。這樣你就不用在程式的不同地方為不同的值新增不同的變數:所有變數更新使用同一個儲存地址,比如你的變數。
CSS主要是一門宣告式語言,缺乏動態性。你可能會說給CSS新增變數會與CSS本身相矛盾。如果前端開發僅僅關注語義,那麼給CSS新增變數確實會與CSS本身矛盾。幸運的是,網路語言更像動態語言,它會隨著周圍環境和開發者的需求不斷變化。CSS也不例外。
總而言之,變數已經成為CSS中令人激動的實現,你很快也會發現,學習和使用它非常直觀。
使用CSS變數有什麼好處?
在CSS中使用變數的好處和在程式語言中沒有特別大的不同。
下面是規範對上述問題的回答:
[使用CSS變數]使大檔案更易於閱讀,因為看起來很隨意的值有了一個提示資訊的名字,並且編輯這些檔案更加簡單,更不易於出錯。因為你只需要在自定義屬性處修改一次,然後這個修改就會自動應用到使用該變數的任何地方。
W3C規範.
換句話說:
通過與專案相關的方式命名變數,管理和維護程式碼會變得更加容易。例如,如果專案的主色調儲存在--primary-color中,修改專案的主色調就會變得很容易,僅僅改變該變數的值就可以,而不用去修改遍佈在程式碼各處、不同CSS屬性中的顏色值。
CSS變數和前處理器變數的不同之處?
在給網站新增樣式時,你可能已經通過前處理器,如Sass和Less,體驗過變數的靈活性帶來的好處。
前處理器可以讓你設定變數,並且在函式、迴圈和數學操作等中使用。這是不是意味著CSS變數就沒有什麼用處了?
不完全是,主要是因為CSS變數和前處理器變數並不一樣。
不同之處在於CSS變數是執行在瀏覽器中的動態CSS屬性,而前處理器變數會被編譯成普通的CSS程式碼。因此,瀏覽器並不知道前處理器變數的存在。
這就意味著你可以更改樣式表、行內樣式屬性和SVG展示型屬性中的CSS變數,或者使用JavaScript操作它們。這是前處理器變數做不到的。CSS變數提供了更多可能性!
但這並不是說你需要在二者之間選擇其一:你可以同時使用CSS變數和前處理器變數的強大功能。
CSS變數的語法
為了簡單起見,在這篇文章中我使用了CSS變數這個術語,但是官方文件給出的是級聯變數的CSS自定義屬性。CSS自定義屬性形式如下:
--my-cool-background: #73a4f4;`
在自定義屬性前面新增兩個短橫線,然後像普通的CSS屬性一樣給它賦值。在上面的程式碼片段中,給--my-cool-background自定義屬性賦了一個顏色值。
級聯變數部分包括使用var()函式應用自定義屬性,形式如下:
var(--my-cool-background)`
自定義屬性的使用範圍是CSS選擇器的內部,var()像一個真正的CSS屬性值被使用。
:root { --my-cool-background: #73a4f4; } /* CSS檔案的其他部分 */ #foo { background-color: var(--my-cool-background); }
上面的程式碼片段把--my-cool-background自定義屬性定義在:root偽元素內,這使得自定義屬性的值全域性可用(:root匹配<html>元素內的任何元素)。然後使用var()函式把值應用到ID是foo的容器的background-color屬性上,然後這個容器就會得到一個淡藍色背景。
除此之外,還可以把淡藍色應用到多個HTML元素的其他顏色屬性上,如color,border-color等。你需要做得僅僅是通過var(--my-cool-background)獲取自定義屬性的值,然後應用到相應的屬性上。當然,你需要好好考慮CSS變數的命名規範,使你的變數名能更好地反映變數的內容。
p { color: var(--my-cool-background); }
檢視CodePen上SitePoint(@SitePoint)的 CSS變數執行例項 。
你也可以在CSS變數中使用另一個CSS變數,舉例如下:
--top-color: orange; --bottom-color: yellow; --my-gradient: linear-gradient(var(--top-color), var(--bottom-color));
上面的程式碼片段建立了--my-gradient變數,它的值是使用--top-color和--bottom-color變數建立的一個漸變。現在,你可以在任何地方通過僅僅改變變數的值來修改漸變,而不必到處在樣式表中建立漸變例項。
下面是一個線上CodePen演示。
檢視CodePen上SitePoint(@SitePoint)的 在CSS變數中使用另一個CSS變數 。
最後,在使用CSS變數的時候,還可以新增一個或多個後備值,舉例如下:
var(--main-color, #333);`
在上面的程式碼片段中,#333是一個後備值。如果沒有提供後備值,當自定義屬性無效或者沒有賦值的時候,會使用繼承值。
CSS變數是大小寫敏感的
和一般的CSS屬性不一樣,CSS變數是大小寫敏感的。
例如,var(--foo)和var(--Foo)使用的是兩個不同的自定義屬性,分別是--foo和--Foo。
CSS變數是級聯的
類似一般的CSS屬性,CSS變數也會繼承。例如,我們定義一個值是blue的自定義屬性:
:root { --main-color: blue; }
<html>根元素內的所有元素如果應用--main-color就會繼承值blue。
如果你在另一個元素裡面給自定義屬性賦了一個不同的值,這個元素的所有子元素就會繼承這個新值,舉例如下:
:root { --main-color: blue; } .alert { --main-color: red; } p { color: var(--main-color); }
<--! HTML --> <html> <head> <!-- head code here --> </head> <body> <div> <p>blue paragraph.</p> <div class="alert"> <p>red paragraph.</p> </div> </div> </body> </html>
上面的標記語言中的第一個段落會繼承全域性--main-color的值,所以字型顏色是藍色。
具有.alert類的div元素內部的段落元素的字型顏色是紅色的,因為它繼承了局部範圍內的--main-color變數,這個變數的值是red。
檢視CodePen上SitePoint(@SitePoint)的 CSS變數繼承的簡單例子 。
現在瞭解了規則,讓我們開始實踐吧!
在SVG中使用CSS變數
CSS變數和SVG可以很好的一起工作!你可以使用CSS變數來修改內聯SVG中的樣式和展示型屬性。
比如,你想通過SVG圖示元素的父元素來給它一個不同的顏色。你可以在父元素內設定一個區域性的CSS變數,然後把它賦值成你想要的顏色,然後,父元素內的圖示就能從父元素繼承到合適的顏色。
下面是相關程式碼:
/* 圖示的內聯SVG symbol */ <svg> <symbol id="close-icon" viewbox="0 0 200 200"> <circle cx="96" cy="96" r="88" fill="none" stroke="var(--icon-color)" stroke-width="15" /> <text x="100" y="160" fill="var(--icon-color)" text-anchor="middle" style="font-size:250px;">x</text> </symbol> </svg> /* 圖示的第一個例項*/ <svg> <use xlink:href="#close-icon" /> </svg>
上面的標記語言使用了<symbol>標籤,使用它可以建立不可見的SVG圖形。然後使用<use>標籤例項化了一個上述圖形的可見版本。使用這種方式通過簡單地引用<symbol>元素的ID(#close-icon)就能建立大量的圖示,然後再根據你的喜好對圖示進行自定義。這比重複的寫同一段程式碼要簡便的多。如果你想複習這個技術,Massimo Cassandro在他的創造你自己的SVG圖示中提供了一個快速教程。
注意SVG中的圓形元素的stroke屬性值和文字元素的fill屬性值:它們都使用了一個CSS變數,--icon-color,這個變數定義在CSS文件的:root選擇器上,如下所示:
:root { --icon-color: black; }
圖示現在的樣子如下:
如果你現在把SVG圖示放到不同的容器中,然後在每個父元素的選擇器中給這個變數賦不同的顏色值,你就能在不新增任何樣式規則的情況下建立不同顏色的圖示。真酷!
舉個例子,把上面圖示的一個例項放在一個有類.success的div中。
HTML:
<div class="success"> <svg> <use xlink:href="#close-icon" /> </svg> </div>
現在,在.success選擇器內給--icon-color變數賦值green,然後看下效果。
CSS:
.success { --icon-color: green; }
現在,圖示的顏色變成了綠色:
檢視下面完整的演示示例:
檢視CodePen上SitePoint(@SitePoint)的 SVG圖示和CSS變數的基本使用 。
在@keyframes動畫中使用CSS變數
CSS變數可以和CSS動畫一起使用,不論是在一般的HTML元素還是內聯SVG元素上。只需要記住在想新增動畫的元素的選擇器上定義自定義屬性,然後使用var()函式在@keyframes中引用。
比如,給SVG的一個有類.bubble的<ellipse>元素新增動畫,CSS程式碼如下:
.bubble { --direction-y: 30px; --transparency: 0; animation: bubbling 3s forwards infinite; } @keyframes bubbling { 0% { transform: translatey(var(--direction-y)); opacity: var(--transparency); } 40% { opacity: calc(var(--transparency) + 0.2); } 70% { opacity: calc(var(--transparency) + 0.1); } 100% { opacity: var(--transparency); } }
你可能已經注意到我們可以使用CSS的calc()通過var()函式對變數進行計算,這使程式碼更加靈活。
這個例子中使用CSS變數的靈活之處是通過簡單的改變相應選擇器內部的變數值,就可以改變動畫效果,而不必查詢@keyframes指令中的每個屬性。
下面是完整的CodePen演示:
檢視CodePen上SitePoint(@SitePoint)的 使用CSS變數和SVG的簡單動畫。
使用JavaScript操作CSS變數
一件更酷的事情是你可以直接通過JavaScript程式碼訪問CSS變數。
假設有一個叫--left-pos的CSS變數,它的值的100px,定義在CSS文件的.sidebar類中:
.sidebar { --left-pos: 100px; }
使用類似下面的JavaScript程式碼獲取--left-pos的值:
// 獲取你想新增動畫的元素 const sidebarElement = document.querySelector('.sidebar'); // 把側邊欄元素的樣式儲存在cssStyles變數中 const cssStyles = getComputedStyle(sidebarElement); // 獲取CSS變數--left-pos的值 const cssVal = String(cssStyles.getPropertyValue('--left-pos')).trim(); // 在控制檯列印CSS變數的值 // 控制檯會輸出變數的值為100px console.log(cssVal);
使用類似下面的JavaScript程式碼給CSS變數賦值:
sidebarElement.style.setProperty('--left-pos', '200px');`
上面的程式碼片段把側邊欄元素的--left-pos變數設定成200px。
相對於改變大量的類或者重寫全部的CSS規則,使用CSS變數給網站新增互動更直接,也更易於維護。
檢視下面的CodePen演示,你可以通過側邊欄來改變混合模式屬性和背景顏色,而這僅僅需要CSS變數和JavaScript:
檢視CodePen上SitePoint(@SitePoint)的 混合模式,CSS變數和JavaScript。
瀏覽器對CSS變數的支援情況
除了IE11(不支援)和Microsoft Edge(buggy支援),在本文編寫的時候,所有主流瀏覽器都完全支援CSS變數。
適配有問題的瀏覽器的方式之一是使用@supports進行條件查詢:
section { color: gray; } @supports(--css: variables) { section { --my-color: blue; color: var(--my-color, 'blue'); } }
因為IE/Edge支援@supports,所以上面的程式碼會生效。如果在var()函式中新增一個後備值,你的程式碼將會更加健壯,在支援的更加不好的瀏覽器中也能優雅降級。
所以,在Chrome和其他支援CSS變數的瀏覽器中,<section>元素內部的文字是藍色的:
IE11不支援CSS變數,文字會被渲染成灰色:
檢視線上演示:
檢視CodePen上SitePoint(@SitePoint)的 @supports和CSS變數。
這種方式的缺點是如果你在專案中使用了大量的CSS變數,但是該專案主要通過不支援CSS變數的瀏覽器開啟,那麼程式碼不僅會變得有點兒複雜,維護也將會是噩夢。
在這種情況下,你可以選擇使用支援 cssnext 的PostCSS,然後你就可以編寫尖端的CSS程式碼了,相容不支援的瀏覽器交給PostCSS去做就可以了,這有點兒像JavaScript的編譯器。如果你想了解PostCSS,SitePoint Premium為其所有成員提供了有關此主題的精彩視訊課程。