SASS, LESS 退散,原生 CSS 可以使用變數啦!

今天要跟大家分享一個新的 CSS 語法:變數。而且這個變數是原生的 CSS 就有的語法,不是其他預處理器例如 SASS, LESS… 的功能喔!

一想到以後可以使用 CSS 寫變數就超級興奮的😊~,但正因為這是 CSS 的新功能,想當然要考慮的第一個問題就是「瀏覽器的相容度」,透過 Can I Use 查詢後,可以發現支援度真是滿江紅啊 XDDD (紅色=不支援,綠色=支援)

但因為這功能實在是太實用了,即使支援度不好,我還是想介紹給各位,希望未來所有瀏覽器都能支援,就可以輕鬆地運用在網站上囉!Chrome 跟 Firefox 都有支援 CSS 變數的語法,歡迎使用這兩個瀏覽器觀看效果。

CSS 變數的基本語法

如同我們使用各種預處理器設定變數般,首先要命名變數與設定值,接下來在適當的地方呼叫該變數。

以下是一段簡單的範例語法:

:root {
  --main-color: #369;
}

h1 {
  color: var(--main-color);
}

把握幾個原則:

  • :root命名變數,我們也可以在任意的選擇器內命名變數,後面會提到該怎麼寫,以及怎麼使用。
  • --main-color是變數名字,#369是該變數的值。
  • 承上,變數命名的規則為:開頭必須是兩個破折號 --
  • 在要呼叫變數的選擇器裡面,使用var()呼叫該變數,可以把它想成 function。
  • 變數的大小寫是兩個不同的變數,例:--main-color--Main-Color是不同的變數喔。

變數的命名規則看起來有點麻煩,有一些開發者曾經回饋:「為什麼不用$foo呢?」,而針對這個討論,他們的回應是,不排除這個需求,未來可能會允許使用$作為命名規則,相關的閱讀可以參考這篇文章

CSS 變數繼承觀念

前面有提到,除了可以將變數寫在:root外,還能寫在任意的選擇器,以下是範例語法:

/* 變數命名 */
:root { --bg-color: #369;}
h3    { --bg-color: #d9444a;}
#main { --bg-color: #94a437;}

/* 呼叫變數 */
* {
  background: var(--bg-color);
}
<p>我的背景顏色是藍色,繼承的是 root 元素的設定</p>
<h3>我的背景顏色是紅色,繼承的是 h3 的設定</h3>
<div id="main">我的背景顏色是綠色,繼承的是 #main 的設定</div>

也可以從 CodePen 看到實際效果 (記得確認瀏覽器有支援喔):

See the Pen PNZzbd by MUKi (@mukiwu) on CodePen.

我們在通用選擇器(*)使用--bg-color語法,照理說,所有的 HTML 都會套用:root的設定。

但同時,我們又特別為h3以及#main設定了不同的背景顏色,因此,當我們的 HTML 有使用這兩個標籤時,他就會自動套用這兩個標籤設定的變數顏色囉!

也因此我們了解到,變數可以因應不同的狀況而更改它的數值 (這不就是變數的精髓嗎 XDDD),我們還能利用這種特性設計 RWD 網站:

:root {
  --bg-color: #369;
}

section {
  background: var(--bg-color);
}

/* 視窗寬度在 0 ~ 600px 時,--bg-color 變數會改為紅色 */
@media (min-width: 600px) {
  :root {
    --bg-color: #d9444a;
  }
}

▼ 變數的另外一個特性:可以互相 assign 來 assign 去,也能在 CSS 變數中實現喔!

:root {
  --bg-color: #369;
  --font-color: var(--bg-color);
}

這時候呼叫--font-color,他的顏色就是 #369


var() function 結構

var()的完整結構如下:

var() = var( <custom-property-name> [, <declaration-value> ]? )

cutsom-property-name是我們呼叫的變數,例如前面使用的--bg-color。我們也能在declaration-value設定其他的值,避免自定義的屬性無效時產生錯誤。如果寫成邏輯方程式大概就是:

if (custom-property-name) {
  --bg-color
} else {
  declaration-value
}

這種寫法有一種高檔的名詞叫做 fallback,如果你有機會閱讀 W3C 官方文件,會發現他們用 fallback 稱呼該處理。

declaration-value要用「逗號」去分隔多個數值,例如var(--bg-color, #369, black);。但如果要處理例如marginpadding的 fallback 時,請注意他們原本的值就沒有逗號,所以不需加逗號,例:var(--padding, 10px 20px);

如用 CSS 語法表示 fallback 應用,請看以下範例:

#main {
  --bg-color: red;
}

#main .header {
  background: var(--header-color, blue);
  /* 並沒有 --header-color 這個變數,所以會顯示他的 fallback color: 藍色 */
}

#main .text {
  background: var(--bg-color, black);
  /* 有 --bg-color 這個變數,所以會顯示紅色 */
}

至於這個東西可以怎麼用呢?我暫時也不太懂 XDDD,不過看了一些 CSS Variables 的介紹後,有人提到可以用來作為元件的樣式設定,例如用 fallback 設定元件初始樣式,變數則是當作元件的 hook。

以下做了一個簡單的範例給大家參考,希望可以給大家更多的靈感囉:

template-default {
  --bg-color: #d9444a;
}

p {
  background: var(--bg-color, #369);
}
<template-default>
  <p>我會繼承 template-default 的 --bg-color 設定,所以背景是紅色</p>
</template-default>

<p>因為我不在 tempalte-default 裡面,找不到 --bg-color 這個變數,所以會顯示 fallback 的藍色</p>

See the Pen XdXNaw by MUKi (@mukiwu) on CodePen.

常見的 var() 錯誤寫法

使用 var() 時,有一些要特別注意的地方

1. var() 的位置

var() 只能設定值,不能作為屬性的變數,以下是錯誤寫法:

.main {
  --side: margin-top;
  var(--side): 20px;
}

2. var() 不能直接合併單位

.main {
  --gap: 20;
  margin-top: var(--gap)px;
}

上述寫法並不等於marign-top: 20px,如果我們要這樣設定,必須改用calc()幫我們計算數值。

利用 calc() 計算數值

以前面提到的margin-top: 20px為例子,我們可以改寫為:

.main {
  --gap: 20;
  /* 運算式的前後必須要空白,不然會出錯喔 */
  margin-top: calc(var(--gap) * 1px);
}
calc()是個很好用的計算工具,更多的介紹可以參考 W3C 的 calc() 文件

利用 JavaScript 讀寫 CSS 變數

最後,我們也可以使用 JS 來讀寫 CSS 變數的設定。

  • 讀取:getPropertyValue()
  • 寫入:setProperty()

請直接看以下語法與範例介紹:

var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--bg-primary-color')).trim();

/* 讀取背景顏色 */
document.getElementById("color-name").innerHTML=value;

/* 修改背景顏色 */
document.documentElement.style.setProperty('--bg-primary-color', '#369');

使用 Codepen 觀看效果時,記得把第 8 行的註解打開以看不同的效果:

See the Pen zqrNEo by MUKi (@mukiwu) on CodePen.


已經很習慣使用預處理器的朋友,可能會對 CSS 變數有所疑問:「我用 SASS 或 LESS 不就好了嗎?」

當然,這些預處理器推出的原意,就是為了補足 CSS 本身的不足,如果今天原生的 CSS 提供了變數的功能,我還是很樂意去了解以及使用,如果一味使用預處理器而不去管原生的 CSS 推出了什麼改變,不覺得有點本末倒置嗎 XDDD。

另外在 W3C 文件裡,也有提到 CSS 變數其實有很多變化,例如以下這段語法:

--gap: if(x > 5) this.width = 10;

雖然我不知道這段語法該怎麼運用,但是從文件看來,這段語法應該是正確且可以跟 JavaScript 有所連結的。這樣看來 CSS 變數真的可以延伸出很多有趣的東西,這些改變都令我非常期待 XD。

也能參考 Google Developers 有趣的一段敘述

While this would not be useful as a variable, as it would be invalid in any normal property, it could potentially be read and acted upon with JavaScript at runtime. This means custom properties have the potential to unlock all kinds of interesting techniques not currently possible with today’s CSS preprocessors. So if you’re thinking 「yawn I have SASS so who cares…」 then take a second look! These are not the variables you’re used to working with.
CSS Variables: Why Should You Care?

以上,希望大家會喜歡這次的介紹囉,有任何問題都可以留言給我 ❤️

參考資料: