CSS變數實用指南及注意事項
近年來,一些動態特性已經開始成為 CSS 語言本身的一部分。 CSS變數 – 官方的術語為 “自定義屬性” – 已經已經加入規範並且具有很好的瀏覽器支援,而 CSS mixins 目前正在開發中 。
在本文中,你將瞭解如何開始將CSS變數整合到CSS開發工作流程中,讓你的樣式表更好維護,且減少重複。
讓我們一起深入瞭解吧!
什麼是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
nubmer1 和 number2 是兩個變數,分別儲存著數字 2 和 3 。
total
同樣是變數,儲存著 number1
與 number2
之和。在這裡它的值就是 5。你可以動態地修改變數裡的值,並在程式中使用它們。在上面的程式碼中,我把 number1
的值更新為 4,然後再進行求和。使用相同的變數,這個時候 total裡儲存的值就不再是 5 ,而是 7 了。
變數的好處在於你可以把值儲存在一個地方,然後在你需要的地方修改它。這樣你就不用在程式的不同地方為不同的值新增不同的變數:所有變數更新使用同一個儲存地址,比如你的變數。
CSS在很大程度上是一種宣告式的語言,缺乏動態性。你也許會認為,讓 CSS 擁有變數,似乎讓上面的說法自相矛盾。如果前端開發僅僅關注語義,那可以這麼說。幸運的是,Web的程式語言很像生活中的語言,它們會隨著周圍環境和實踐需求而不斷進化與適應。CSS也不例外。
總而言之,變數已經成為 CSS 中令人激動的實現,你很快也會發現,對於這個厲害的新技術,學習和使用起來都非常直觀。
使用CSS變數有什麼好處?
使用CSS變數的好處,跟在其他程式語言中使用變數的好處沒什麼大的區別。
以下是規範對此的說法:
[使用CSS變數]可以更容易地閱讀大檔案,因為看似任意的值,現在具有資訊性名稱,並且使此類檔案更容易編輯,且更不容易出錯,因為,你只需要在自定義屬性中改變一次值,所有應用了這個變數的地方都會自動跟著一起改變。W3C 規範
換句話說:
通過給變數起一個對你來說在專案中有意義的名字,你能更容易的管理和維護你的程式碼。例如,當你為專案中的主色調設定一個變數名--primary-color ,那麼你後面再修改這個主色調時,只需要改動一處,而不需要在不同位置的多個CSS檔案中去手動多次修改這個值。
CSS變數和前處理器中的變數有什麼不同?
你可能已經在CSS前處理器中嘗試過使用變數而帶來的好處了,比如 Sass
和 Less
。
前處理器讓你能設定變數,以及在函式、迴圈、數學計算等等地方中使用它們。這是否意味著CSS變數已經無關緊要了呢?
那可未必,主要是因為,CSS變數與前處理器中的變數其實並不是同樣的東西。
不同之處在於CSS變數是執行在瀏覽器中的動態CSS屬性,而前處理器變數會被編譯成普通的CSS程式碼。因此,瀏覽器並不知道前處理器變數的存在。
這意味著,你可以在樣式表,內聯樣式和SVG的標籤中直接更新CSS變數,或者使用JavaScript操作它們。這是前處理器變數做不到的。CSS變數提供了更多可能性!
但這並不是說你需要在二者之間選擇其一:沒有什麼東西限制你,你可以同時使用CSS變數和預處理變數,並享有它們各自帶來的巨大好處。
CSS變數:語法
雖然本文為了簡潔,我使用了CSS變數(CSS variables)這個術語,但是官方的規範把它們稱作為 級聯變數的CSS自定義屬性。CSS自定義屬性形式如下:
--my-cool-background: #73a4f4;
在自定義屬性前面新增雙橫線字首,然後像普通的CSS屬性一樣給它賦值。在上面的程式碼片段中,我給 --my-cool-background
自定義屬性賦了一個顏色值。
而 級聯變數(cascading variable) 的部分,由通過 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
這個偽類中,這讓該自定義屬效能在全域性可用(它匹配<html>
元素內的所有內容)。然後,使用 var() 函式把 ID 為 foo 的容器的 background-color
設定為自定義屬性的值,這時該容器就有了淺藍的背景色。
除此之外,還可以把淡藍色應用到多個HTML元素的其他顏色屬性上,如 color
,border-color
等。方法很簡單,就是通過 var(--my-cool-background)
獲取自定義屬性的值,然後給相應的CSS屬性設定上去。(當然,我建議在事情變得混亂之前考慮一下CSS變數的命名約定):
p {
color: var(--my-cool-background);
}
你也可以在CSS變數中使用另一個CSS變數,例如:
--top-color: orange;
--bottom-color: yellow;
--my-gradient: linear-gradient(var(--top-color), var(--bottom-color));
上面的程式碼建立了一個 --my-gradient
變數,是一個漸變樣式,它的值是使用 --top-color
和 --bottom-color
變數建立的一個漸變。現在,你可以在任何地方通過僅僅改變變數的值來修改漸變,而不必到處在樣式表中建立漸變例項。
最後,你可以在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這個值。
如果你在另一個元素裡面給自定義屬性賦了一個不同的值,這個元素的所有子元素就會繼承這個新值,例如:
CSS:
:root {
--main-color: blue;
}
.alert {
--main-color: red;
}
p {
color: var(--main-color);
}
HTML:
<--! HTML -->
<html>
<head>
<!-- head code here -->
</head>
<body>
<div>
<p>blue 的段落</p>
<div class="alert">
<p>red 的段落</p>
</div>
</div>
</body>
</html>
在上面的標籤中,第一個段落會繼承到全域性的 --main-color
值,它是藍色。
在div標籤中擁有 .alert 類的段落會是紅色,因為它的值繼承自區域性作用域裡的 --main-color
,這個變數的值是 red
。
內聯樣式中的 CSS 變數
CSS變數也可以在元素的內聯樣式中定義。 假設您有一個可以控制大小的元件,
你可以看到元素的內聯樣式中同樣可以定義CSS變數,而且同樣遵循相同的級聯規則。
var()函式
現在你知道了 var()
函式的用法。有關此功能的更多資訊。 看下面的程式碼,有一個紅色的 div
和一個綠色的div
。只有一個CSS變數用於子綠色 div
。
現在從:root
選擇器 刪除CSS變數 --background: green;
,看看會發生什麼。
你可能猜測子元素將具有從父元素繼承的背景紅色。 錯了,這裡有點特殊情況。 當您在任何CSS屬性中使用該變數時,如果沒有定義變數,那麼它將預設採用預設值。 在這個例子中,背景顏色將是transparent(透明的):
你可能猜測子元素將具有從父元素繼承的背景紅色。 錯了,這裡有點特殊情況。 當您在任何CSS屬性中使用該變數時,如果沒有定義變數,那麼它將預設採用預設值。 在這個例子中,背景顏色將是transparent
(透明的):
無效的值
如果CSS變數有一個無效的值,比如 --background: blah blah blah;
或拼寫錯誤 --background: yelow; /* yellow
拼寫錯誤 */,那麼 CSS 屬性將預設採用預設值,如:
background
預設值是 transparent
width
預設值是 auto
position
預設值是 static
opacity
預設值是 1
display
預設值是 inline
下面這個例子中,background
的值為transparent
,也就是說背景顏色是透明的。
回退值(fallback value)
有時可能會出現無法定義CSS變數的情況。在這種情況下,您可以將回退值設定為 var()
函式中的第二個引數
你也可以巢狀多個var()
函式 background: var(--color1, var(--color2,
var(--color3, #00BCD4)));
。
結合 calc()函式
如果你以前從未使用過它,那麼我現在告訴你 calc()
函式是一個很實用的小工具,可以讓您執行計算以確定CSS值。 它在所有現代瀏覽器上都得到了很好的支援,並且可以與 CSS變數結合使用,以構建新值。 下面的例子中 <div>
的 width
是動態計算的,
結合媒體查詢 @media
你甚至可以在媒體查詢中重新設定變數值,並讓這些新值在任何地方使用它們級聯,特別要說明的是:這是前處理器變數無法實現的。
檢視此示例,其中媒體查詢更改用於設定非常簡單網格的變數,開啟 codepen,然後嘗試調整瀏覽器的大小,你可以看到媒體查詢中的 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>
標籤生成一個可見的副本。這種方法可以讓你根據自己的喜好建立任意多個自定義的圖示,也就是通過它的ID( #close-icon
)指向那個 <symbol>
。這比一遍又一遍地寫重複的程式碼建立圖形更加簡便。如果你想提高這方便的技術,Massimo Cassandro在他的 創造你自己的SVG圖示 中提供了一個快速教程。
注意 SVG中的圓形元素的 stroke
屬性值和文字元素的 fill
屬性值:它們都使用了一個CSS變數,--icon-color
,這個變數定義在CSS文件的 :root
選擇器上,如下所示:
:root {
--icon-color: black;
}
這是當前圖示看起來的樣子:
如果你現在把SVG圖示放到不同的容器中,然後在每個父元素的選擇器中給這個變數賦不同的顏色值,你就能在不新增任何樣式規則的情況下建立不同顏色的圖示。這很酷!
為了展示這一點,我們把上面圖示的一個例項放在一個有 .success
類的 div 中。
HTML 程式碼:
<!-- html -->
<div class="success">
<svg>
<use xlink:href="#close-icon" />
</svg>
</div>
現在,讓 --icon-color
變數區域性化,即把它放在 .success
中,並設定一個 green 值。我們來看看發生的變化:
CSS 程式碼:
/* css */
.success {
--icon-color: green;
}
這個圖示的顏色就變成了綠色:
如何在@keyframes中使用CSS變數
CSS變數可以在CSS動畫中使用,不論是在一般的HTML元素還是內聯SVG元素上。只需要記得,你得知道讓什麼元素動,把它視為目標元素,然後建立對該目標元素的選擇器,在選擇器的作用範圍中定義你的CSS變數,然後,使 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
裡的屬性了。
如何通過JavaScript操作CSS變數
另一個超級酷的事情就是,你可以直接通過JavaScript程式碼訪問CSS變數。通過 getComputedStyle
,setProperty
和 getPropertyValu
e從JavaScript訪問CSS變數非常簡單。 要獲取變數,請使用 getPropertyValue()
。
假設在你的CSS檔案中,有一個叫做 --left-pos
的變數,作用在 .sidebar
選擇器中,值為 100px:
.sidebar {
--left-pos: 100px;
}
那麼,使用類似下面的 JavaScript 程式碼獲取 --left-pos
的值:
// 快取你即將操縱的元素
const sidebarElement = document.querySelector('.sidebar');
// 快取sidebarElement的樣式於cssStyles中
const cssStyles = getComputedStyle(sidebarElement);
// 獲取 --left-pos CSS變數的值
const cssVal = String(cssStyles.getPropertyValue('--left-pos')).trim();
// 將CSS 變數的值列印到控制檯: 100px
console.log(cssVal);
使用類似下面的JavaScript程式碼給CSS變數賦值:
sidebarElement.style.setProperty('--left-pos', '200px');
上面的程式碼將sidebar元素中 --left-pos
變數的值設定為 200px
。
請看看CodePen中的如下示例,你可以互動式地點選側邊欄,修改 blend mode 屬性和背景色。這些實現只用到了CSS變數和JavaScript。
還有一些簡單的方法,這裡來看看不使用 getComputedStyle()
,獲取變數值:
JavaScript 程式碼:
/* 從 :root 根元素獲取變數值 */
document.documentElement.style.getPropertyValue('--background');
/* 從 .block-3 元素獲取變數值 */
document.querySelector('.block-3').style.getPropertyValue('--background');
修改變數值:
/* 修改 :root 根元素的變數值 */
document.documentElement.style.setProperty('--background', '#ff0000');
/* 修改 .block-3 元素的變數值 */
document.querySelector('.block-3').style.setProperty('--background', '#ff0000');
其他一些注意點
還有一些有趣的事情,在開發時候需要注意。
空值和空格
/* 無效的 */
--color:;
/* 有效的 */
--color: ; /* 值是空格 */
背景圖片 url()
/* 無效的 - CSS 不支援拼接*/
.logo{
--logo-url: 'logo';
background: url('assets/img/' var(--logo-url) '.png');
}
/* 無效的 - CSS bug */
.logo{
--logo-url: 'assets/img/logo.png';
background: url(var(--logo-url));
}
/* 有效的 */
.logo{
--logo-url: url('assets/img/logo.png');
background: var(--logo-url);
}
單位相關
/* 無效的 */
--width: 10;
width: var(--width)px;
/* 有效的 */
--width: 10px;
width: var(--width);
/* 有效的 */
--width: 10;
width: calc(1px * var(--width)); /* 乘以1個單位進行轉換 */
width: calc(1em * var(--width));
瀏覽器對CSS變數的支援情況
除了IE11(它不支援CSS變數),所有主流瀏覽器都對CSS變數有全面地支援。
對於不支援CSS變數的瀏覽器,一個變通的方案是使用具有虛擬查詢條件(dummy conditional query)的 @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變數,頁面將顯示灰色文字:
這種方式的缺點是如果你在專案中使用了大量的CSS變數,但是該專案主要通過不支援CSS變數的瀏覽器開啟,那麼程式碼不僅會變得有點兒複雜,維護也將會是噩夢。
在這種情況下,你可以選擇使用支援 cssnext 的PostCSS,然後你就可以編寫尖端的CSS程式碼了,相容不支援的瀏覽器交給PostCSS去做就可以了,這有點兒像JavaScript的編譯器。
這裡推薦一下我的前端學習交流群:784783012,裡面都是學習前端的,如果你想製作酷炫的網頁,想學習程式設計。自己整理了一份2018最全面前端學習資料,從最基礎的HTML+CSS+JS【炫酷特效,遊戲,外掛封裝,設計模式】到移動端HTML5的專案實戰的學習資料都有整理
點選:加入