CSS 變數 - 重要性

CSS 變數 (更準確的 CSS 自訂屬性) 是在 Chrome 49 中到達。這些屬性有助於在 CSS 中減少重複內容,也可以發揮強大的執行階段效果,例如主題切換,且未來可能會擴充/簡化日後的 CSS 功能。

CSS 看起來十分簡潔

設計應用程式時,我們通常會設定一組品牌顏色,以便重複使用,維持應用程式的外觀。遺憾的是,在 CSS 中一再重複這些顏色值,不僅是常規做法,也容易出錯。如果有某個時候某個顏色需要改變,您可對風力做出「尋找與取代」所有事物,但對規模夠大的專案,這可能會變得危險。

近來,許多開發人員已採用 SASS 或 LESS 等 CSS 預先處理器,透過使用預先處理工具變數解決這個問題。這些工具雖已大幅提升開發人員的工作效率,但使用的變數有很大的缺點,也就是這些變數採用靜態模式,且無法在執行階段進行變更。新增在執行階段變更變數的功能,不但增加了動態應用程式主題設定等用途,還具備回應式設計的主要變形,並有機會支援日後的 CSS 功能。隨著 Chrome 49 推出,這些功能現在以 CSS 自訂屬性的形式提供。

自訂屬性簡介

自訂屬性會為 CSS 工具箱新增兩項功能:

  • 可讓作者將任意值指派給具有作者選擇的名稱屬性。
  • var() 函式,可允許作者在其他屬性中使用這些值。

這個簡單的例子

:root {
    --main-color: #06c;
}

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

--main-color 是作者定義的自訂屬性,其值為 #06c。請注意,所有自訂屬性開頭都是兩個破折號。

var() 函式會擷取自訂屬性值,並以自訂屬性值取代本身,因此產生的 color: #06c;。只要自訂屬性是在樣式表中的某個位置定義,就可以供 var 函式使用。

語法一開始看起來有點奇怪。許多開發人員都會問:「為什麼不只使用 $foo 做為變數名稱?」該做法經過特別挑選,盡可能提供彈性,且未來或許允許 $foo 巨集。針對背景故事,您可以閱讀其中一個規格作者 Tab Atkins 的這篇文章

自訂屬性語法

自訂屬性的語法相當簡單。

--header-color: #06c;

請注意,自訂屬性會區分大小寫,因此 --header-color--Header-Color 是不同的自訂屬性。儘管表面值看似簡單,但允許自訂屬性的語法實際上卻是相當寬鬆的。舉例來說,以下是有效的自訂屬性:

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

雖然這不適合當做變數,因為在任何一般屬性中都會失效,但系統可能會在執行階段讀取 JavaScript 資料並採取行動。這表示自訂屬性能夠發揮現今 CSS 預先處理工具所無法提供的各種有趣技術。因此,如果您想知道「真的,我有點 SASS,因此關心...」,那就再來一探究竟!這些並非您常用的變數。

瀑布

自訂屬性遵循標準階層式規則,因此您可以在不同等級的不同層級定義相同屬性

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

也就是說,您可以在媒體查詢中使用自訂屬性來輔助回應式設計。其中一個用途可能是隨著螢幕大小增加,主要區段元素的邊界可能會擴大:

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

請特別注意,由於目前的 CSS 預先處理工具無法定義媒體查詢內部的變數,因此無法使用上述程式碼片段。有這項能力可以帶來許多潛在機會!

您也可以透過自訂屬性,從其他自訂屬性擷取其值。設定主題時,這項功能非常實用:

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

var() 函式

如要擷取及使用自訂屬性的值,您必須使用 var() 函式。var() 函式的語法如下所示:

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

其中 <custom-property-name> 是作者定義的自訂屬性名稱 (例如 --foo),而 <declaration-value> 是參照的自訂屬性無效時要使用的備用值。備用值可以是以半形逗號分隔的清單,將合併為單一值。例如,var(--font-stack, "Roboto", "Helvetica"); 定義 "Roboto", "Helvetica" 的備用項。請記住,短值 (例如邊界和邊框間距的值) 不會以半形逗號隔開,因此邊框間距的適當備用值看起來會像這樣。

p {
    padding: var(--pad, 10px 15px 20px);
}

有了這些備用值,元件作者就能為元素編寫防禦樣式:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

這項技術特別適合使用陰影 DOM 設定網頁元件的主題,因為自訂屬性可以周遊陰影邊界。網頁元件作者可以使用備用值建立初始設計,並以自訂屬性的形式讓主題「掛鉤」。

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

使用 var() 時必須注意幾個事項。變數不能是屬性名稱。例如:

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

不過,這並不等同於設定 margin-top: 20px;。反之,第二個宣告無效,並擲回為錯誤。

同樣地,如果值是由變數提供,則您無法 (一般) 建構值:

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

同樣地,這並不等於設定 margin-top: 20px;。如要建構值,則需要其他項目:calc() 函式。

使用 calc() 建構值

如果您從未使用過這項工具,calc() 函式是簡易的工具,可讓您執行計算以決定 CSS 值。所有新版瀏覽器皆支援此架構,而且可搭配自訂屬性使用以建構新的值。例如:

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

在 JavaScript 中使用自訂屬性

如要在執行階段取得自訂屬性的值,請使用已計算的 CSSStyleDeclaration 物件的 getPropertyValue() 方法。

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

同樣地,如要在執行階段設定自訂屬性的值,請使用 CSSStyleDeclaration 物件的 setProperty() 方法。

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

您也可以在對 setProperty() 的呼叫中使用 var() 函式,將自訂屬性的值設為參照其他自訂屬性。

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

由於自訂屬性可以參照樣式表中的其他自訂屬性,因此您可以想像這種做法會如何帶來各種有趣的執行階段效果。

瀏覽器支援

目前 Chrome 49、Firefox 42、Safari 9.1 和 iOS Safari 9.3 支援自訂屬性。

操作示範

歡迎試用範例,瞭解您現在可以運用自訂屬性使用的所有有趣技巧。

其他資訊

如要進一步瞭解自訂屬性,Google Analytics (分析) 團隊的 Philip Walton 寫了他對自訂資源如此期待的原因。您可以透過 chromestatus.com 掌握其他瀏覽器的運作進度。