CSS 變數 - 重要性

CSS 變數 (更準確來說是 CSS 自訂屬性) 已在 Chrome 49 中推出。這些元素可用於減少 CSS 中的重複內容,以及實現強大的執行階段效果,例如切換主題,以及可能會擴充/polyfill 未來 CSS 功能。

CSS 雜訊

設計應用程式時,常見的做法是設定一組品牌顏色,以便重複使用,讓應用程式外觀保持一致。很遺憾,在 CSS 中重複輸入這些顏色值不僅費時,也容易出錯。如果在某個時間點需要變更其中一個顏色,您可以放心大膽地使用「尋找並取代」功能,但在規模較大的專案中,這可能會帶來危險。

最近,許多開發人員都改用 CSS 前置處理器,例如 SASS 或 LESS,透過使用前置處理器變數解決這個問題。雖然這些工具大大提升了開發人員的工作效率,但它們使用的變數有個重大缺點,那就是這些變數是靜態的,無法在執行階段變更。在執行階段新增變數變更功能,不僅可用於動態應用程式主題設定等功能,還可為回應式設計帶來重大影響,並有機會為未來的 CSS 功能提供 polyfill。隨著 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 */
}

這項技巧特別適合用於設定使用 Shadow DOM 的 Web 元件的主題,因為自訂屬性可穿越陰影邊界。Web 元件作者可以使用備用值建立初始設計,並以自訂屬性形式公開配色「鉤子」。

<!-- 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 中,查看其他瀏覽器的進度。