使用 CSS 自定義屬性(變數)

自定義屬性(有時也稱 CSS 變數級聯變數)是由 CSS 作者定義的實體,表示在整個文件中可重用的特定值。它們透過 @property @ 規則或自定義屬性語法(例如:--primary-color: blue;)設定。自定義屬性可以使用 CSS 的 var() 函式進行訪問(例如:color: var(--primary-color);)。

複雜的網站含有大量的 CSS,通常會導致許多重複的 CSS 值。例如,在樣式表中,同一個顏色在數百個不同的地方使用是很常見的。要更改一個在多處重複的顏色,需要在所有規則和 CSS 檔案中進行查詢和替換。自定義屬性允許一個值在一個地方定義,然後在多個其他地方引用,從而使其更易於使用。另一個好處是可讀性和語義化。例如,--main-text-color 比十六進位制顏色 #00ff00 更容易理解,尤其是在不同上下文中使用該顏色時。

使用兩個破折號(--)定義的自定義屬性受層疊規則的影響,並從其父元素繼承其值。@property @ 規則允許對自定義屬性進行更多控制,並允許你指定它是否從父元素繼承值、初始值是什麼以及應應用的型別約束。

注意:變數在媒體查詢和容器查詢中不起作用。你可以在元素上任何屬性值的任何部分使用 var() 函式。但你不能將 var() 用於屬性名、選擇器或除屬性值之外的任何東西,這意味著你不能在媒體查詢或容器查詢中使用它。

宣告自定義屬性

在 CSS 中,你可以使用兩個破折號作為屬性名稱的字首來宣告自定義屬性,或者使用 @property @ 規則。以下部分將介紹如何使用這兩種方法。

使用兩個破折號(--)作為字首

以兩個破折號為字首的自定義屬性以 -- 開頭,後跟屬性名稱(例如 --my-property)和可以是任何有效的 CSS 值的屬性值。與任何其他屬性一樣,它寫在規則集內部。以下示例展示瞭如何建立一個自定義屬性 --main-bg-color,並使用 <named-color>brown

css
section {
  --main-bg-color: brown;
}

規則集的選擇器(上例中的 <section> 元素)定義了自定義屬性可以使用的作用域。因此,通常的做法是在 :root 偽類上定義自定義屬性,這樣就可以全域性引用它。

css
:root {
  --main-bg-color: brown;
}

情況並非總是如此:你可能有充分的理由限制自定義屬性的作用域。

注意:自定義屬性名稱區分大小寫——--my-color--My-color 將被視為兩個獨立的自定義屬性。

使用 @property @ 規則

@property @ 規則允許你更具表現力地定義自定義屬性,可以為屬性關聯型別、設定預設值和控制繼承。以下示例建立了一個名為 --logo-color 的自定義屬性,它需要一個 <color>

css
@property --logo-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #c0ffee;
}

如果你想在 JavaScript 中而不是直接在 CSS 中定義或使用自定義屬性,有一個相應的 API 可以實現此目的。你可以在 CSS 屬性和值 API 頁面上了解其工作原理。

使用 var() 引用自定義屬性

無論你選擇哪種方法定義自定義屬性,都可以在 var() 函式中引用該屬性,以替代標準的屬性值。

css
details {
  background-color: var(--main-bg-color);
}

自定義屬性入門

讓我們從一些我們想要應用樣式的 HTML 開始。有一個作為容器的 <div>,其中包含一些子元素,有些還有巢狀元素。

html
<div class="container">
  <div class="one">
    <p>One</p>
  </div>
  <div class="two">
    <p>Two</p>
    <div class="three">
      <p>Three</p>
    </div>
  </div>
  <input class="four" placeholder="Four" />
  <textarea class="five">Five</textarea>
</div>

我們將使用以下 CSS 根據元素的類來為它們設定樣式(下面未顯示一些佈局規則,以便我們專注於顏色)。根據它們的類,我們給元素設定 tealpink 的背景色。

css
/* For each class, set some colors */
.one {
  background-color: teal;
}

.two {
  color: black;
  background-color: pink;
}

.three {
  color: white;
  background-color: teal;
}

.four {
  background-color: teal;
}

.five {
  background-color: teal;
}

這會產生以下結果:

我們可以使用自定義屬性來替換這些規則中重複的值。在 .container 作用域中定義了 --main-bg-color 並在多處引用其值後,更新後的樣式如下所示:

css
/* Define --main-bg-color here */
.container {
  --main-bg-color: teal;
}

/* For each class, set some colors */
.one {
  background-color: var(--main-bg-color);
}

.two {
  color: black;
  background-color: pink;
}

.three {
  color: white;
  background-color: var(--main-bg-color);
}

.four {
  background-color: var(--main-bg-color);
}

.five {
  background-color: var(--main-bg-color);
}

使用 :root 偽類

對於某些 CSS 宣告,可以在層疊的更高層級宣告,讓 CSS 繼承來解決這個問題。但對於非小型專案,這並不總是可行的。透過在 :root 偽類上宣告自定義屬性,並在整個文件中需要的地方使用它,CSS 作者可以減少重複的需要。

css
/* Define --main-bg-color here */
:root {
  --main-bg-color: teal;
}

/* For each class, set some colors */
.one,
.three,
.four,
.five {
  background-color: var(--main-bg-color);
}

.two {
  color: black;
  background-color: pink;
}

這會產生與前一個示例相同的結果,但允許對所需屬性值進行一次權威性宣告(--main-bg-color: teal;),這在你以後想在整個專案中更改該值時非常有用。

自定義屬性的繼承

使用兩個破折號 -- 而非 @property 定義的自定義屬性總是繼承其父元素的值。下面的示例演示了這一點:

html
<div class="one">
  <p>One</p>
  <div class="two">
    <p>Two</p>
    <div class="three"><p>Three</p></div>
    <div class="four"><p>Four</p></div>
  </div>
</div>
css
div {
  background-color: var(--box-color);
}

.two {
  --box-color: teal;
}

.three {
  --box-color: pink;
}

var(--box-color) 的結果取決於繼承,如下所示:

  • class="one"無效值,這是以這種方式定義的自定義屬性的預設值。
  • class="two"teal
  • class="three"pink
  • class="four"teal(從其父元素繼承)

以上示例展示了自定義屬性的一個方面:它們的行為與其它程式語言中的變數不完全相同。值是在需要時計算的,而不是儲存起來並在樣式表的其它地方重用。例如,你不能設定一個屬性的值,並期望在兄弟元素的後代規則中檢索到該值。該屬性僅為匹配的選擇器及其後代設定。

使用 @property 控制繼承

@property @ 規則允許你明確宣告屬性是否繼承。以下示例使用 @property @ 規則建立一個自定義屬性。繼承被停用,定義了 <color> 資料型別,並設定了初始值為 teal

父元素將 --box-color 設定為 green,並使用 --box-color 作為其背景色的值。子元素也使用 background-color: var(--box-color),如果啟用了繼承(或者如果它是用雙破折號語法定義的),我們期望它的顏色是 green

html
<div class="parent">
  <p>Parent element</p>
  <div class="child">
    <p>Child element with inheritance disabled for --box-color.</p>
  </div>
</div>
css
@property --box-color {
  syntax: "<color>";
  inherits: false;
  initial-value: teal;
}

.parent {
  --box-color: green;
  background-color: var(--box-color);
}

.child {
  width: 80%;
  height: 40%;
  background-color: var(--box-color);
}

因為在 @ 規則中設定了 inherits: false;,並且在 .child 作用域內沒有宣告 --box-color 屬性的值,所以使用了初始值 teal,而不是本應從父元素繼承的 green

自定義屬性的回退值

你可以使用 var() 函式為自定義屬性定義回退值,也可以使用 @property @ 規則的 initial-value

注意:回退值不用於修復 CSS 自定義屬性不受支援時的相容性問題,因為在這種情況下回退值也無濟於事。回退涵蓋了瀏覽器支援 CSS 自定義屬性,並且在所需變數尚未定義或值無效時能夠使用不同值的情況。

var() 函式中定義回退值

使用 var() 函式,你可以在給定變數尚未定義時定義多個回退值;這在處理自定義元素Shadow DOM 時非常有用。

函式的第一個引數是自定義屬性的名稱。函式的第二個引數是一個可選的回退值,當引用的自定義屬性無效時,該值將用作替代值。該函式接受兩個引數,將第一個逗號之後的所有內容都賦給第二個引數。如果第二個引數無效,回退將失敗。例如:

css
.one {
  /* Red if --my-var is not defined */
  color: var(--my-var, red);
}

.two {
  /* pink if --my-var and --my-background are not defined */
  color: var(--my-var, var(--my-background, pink));
}

.three {
  /* Invalid: "--my-background, pink" */
  color: var(--my-var, --my-background, pink);
}

如上面第二個示例所示(var(--my-var, var(--my-background, pink))),將一個自定義屬性作為回退值是使用 var() 提供多個回退的正確方法。但是,你應該注意這種方法的效能影響,因為它需要更多時間來解析巢狀的變數。

注意:回退的語法與自定義屬性的語法一樣,允許使用逗號。例如,var(--foo, red, blue) 定義了一個 red, blue 的回退值——第一個逗號和函式結束之間的任何內容都被視為回退值。

使用 @property 初始值作為回退

除了使用 var(),在 @property @ 規則中定義的 initial-value 也可以作為一種回退機制。事實上,我們已經在 @property 繼承部分看到過這一點。

以下示例使用 @property @ 規則將 --box-color 的初始值設定為 teal。在 @ 規則之後的規則集中,我們想將 --box-color 設定為 pink,但值名中有一個拼寫錯誤。第三個 <div> 也是如此,我們為一個需要有效 <color>的自定義屬性使用了 2rem2rempeenk 都是無效的顏色值,因此應用了初始值 teal

css
@property --box-color {
  syntax: "<color>";
  initial-value: teal;
  inherits: false;
}

.one {
  --box-color: pink;
  background-color: var(--box-color);
}

.two {
  --box-color: peenk;
  background-color: var(--box-color);
}

.three {
  --box-color: 2rem;
  background-color: var(--box-color);
}

無效的自定義屬性

每個 CSS 屬性都可以被賦予一組定義的。如果你試圖為一個屬性賦予一個超出其有效值集合的值,它將被視為無效

當瀏覽器遇到一個常規 CSS 屬性的無效值時(例如,為 color 屬性設定值為 16px),它會丟棄該宣告,元素將被賦予它們在沒有該宣告時應有的值。在下面的例子中,我們看到當一個常規 CSS 宣告無效時會發生什麼;color: 16px; 被丟棄,而之前的 color: blue 規則被應用。

html
<p>This paragraph is initially black.</p>
css
p {
  font-weight: bold;
  color: blue;
}

p {
  /* oops, not a valid color */
  color: 16px;
}

然而,當自定義屬性的值被解析時,瀏覽器還不知道它們將在哪裡使用,所以它必須將幾乎所有的值都視為有效。不幸的是,這些有效的值可以透過 var() 函式符號在可能沒有意義的上下文中使用。屬性和自定義變數可能導致無效的 CSS 語句,從而引出了計算時有效的概念。

當瀏覽器遇到一個無效的 var() 替換時,將使用該屬性的初始值繼承值。這個例子和上一個很像,只是我們使用了一個自定義屬性。

瀏覽器將 --text-color 的值替換 var(--text-color),但 16px 對於 color 來說不是一個有效的屬性值。替換後,該屬性沒有意義,所以瀏覽器分兩步處理這種情況:

  1. 檢查 color 屬性是否可繼承。它是可繼承的,但是這個 <p> 沒有任何父元素設定了 color 屬性。所以我們進入下一步。
  2. 將值設定為其預設初始值,即黑色。
html
<p>This paragraph is initially black.</p>
css
:root {
  --text-color: 16px;
}

p {
  font-weight: bold;
  color: blue;
}

p {
  color: var(--text-color);
}

對於這種情況,@property @ 規則可以透過允許定義屬性的初始值來防止意外結果。

html
<p>This paragraph is initially black.</p>
css
@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: teal;
}

:root {
  --text-color: 16px;
}

p {
  font-weight: bold;
  color: blue;
}

p {
  color: var(--text-color);
}

JavaScript 中的值

要在 JavaScript 中使用自定義屬性的值,就像使用標準屬性一樣。

js
// get variable from inline style
element.style.getPropertyValue("--my-var");

// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-var");

// set variable on inline style
element.style.setProperty("--my-var", jsVar + 4);

另見