使用 CSS 自定義屬性(變數)
自定義屬性(有時也稱 CSS 變數或級聯變數)是由 CSS 作者定義的實體,表示在整個文件中可重用的特定值。它們透過 @property @ 規則或自定義屬性語法(例如:--primary-color: blue;)設定。自定義屬性可以使用 CSS 的 var() 函式進行訪問(例如:color: var(--primary-color);)。
複雜的網站含有大量的 CSS,通常會導致許多重複的 CSS 值。例如,在樣式表中,同一個顏色在數百個不同的地方使用是很常見的。要更改一個在多處重複的顏色,需要在所有規則和 CSS 檔案中進行查詢和替換。自定義屬性允許一個值在一個地方定義,然後在多個其他地方引用,從而使其更易於使用。另一個好處是可讀性和語義化。例如,--main-text-color 比十六進位制顏色 #00ff00 更容易理解,尤其是在不同上下文中使用該顏色時。
使用兩個破折號(--)定義的自定義屬性受層疊規則的影響,並從其父元素繼承其值。@property @ 規則允許對自定義屬性進行更多控制,並允許你指定它是否從父元素繼承值、初始值是什麼以及應應用的型別約束。
宣告自定義屬性
在 CSS 中,你可以使用兩個破折號作為屬性名稱的字首來宣告自定義屬性,或者使用 @property @ 規則。以下部分將介紹如何使用這兩種方法。
使用兩個破折號(--)作為字首
以兩個破折號為字首的自定義屬性以 -- 開頭,後跟屬性名稱(例如 --my-property)和可以是任何有效的 CSS 值的屬性值。與任何其他屬性一樣,它寫在規則集內部。以下示例展示瞭如何建立一個自定義屬性 --main-bg-color,並使用 <named-color> 值 brown。
section {
--main-bg-color: brown;
}
規則集的選擇器(上例中的 <section> 元素)定義了自定義屬性可以使用的作用域。因此,通常的做法是在 :root 偽類上定義自定義屬性,這樣就可以全域性引用它。
:root {
--main-bg-color: brown;
}
情況並非總是如此:你可能有充分的理由限制自定義屬性的作用域。
注意:自定義屬性名稱區分大小寫——--my-color 和 --My-color 將被視為兩個獨立的自定義屬性。
使用 @property @ 規則
@property @ 規則允許你更具表現力地定義自定義屬性,可以為屬性關聯型別、設定預設值和控制繼承。以下示例建立了一個名為 --logo-color 的自定義屬性,它需要一個 <color>。
@property --logo-color {
syntax: "<color>";
inherits: false;
initial-value: #c0ffee;
}
如果你想在 JavaScript 中而不是直接在 CSS 中定義或使用自定義屬性,有一個相應的 API 可以實現此目的。你可以在 CSS 屬性和值 API 頁面上了解其工作原理。
使用 var() 引用自定義屬性
無論你選擇哪種方法定義自定義屬性,都可以在 var() 函式中引用該屬性,以替代標準的屬性值。
details {
background-color: var(--main-bg-color);
}
自定義屬性入門
讓我們從一些我們想要應用樣式的 HTML 開始。有一個作為容器的 <div>,其中包含一些子元素,有些還有巢狀元素。
<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 根據元素的類來為它們設定樣式(下面未顯示一些佈局規則,以便我們專注於顏色)。根據它們的類,我們給元素設定 teal 或 pink 的背景色。
/* 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 並在多處引用其值後,更新後的樣式如下所示:
/* 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 作者可以減少重複的需要。
/* 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 定義的自定義屬性總是繼承其父元素的值。下面的示例演示了這一點:
<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>
div {
background-color: var(--box-color);
}
.two {
--box-color: teal;
}
.three {
--box-color: pink;
}
var(--box-color) 的結果取決於繼承,如下所示:
class="one":無效值,這是以這種方式定義的自定義屬性的預設值。class="two":tealclass="three":pinkclass="four":teal(從其父元素繼承)
以上示例展示了自定義屬性的一個方面:它們的行為與其它程式語言中的變數不完全相同。值是在需要時計算的,而不是儲存起來並在樣式表的其它地方重用。例如,你不能設定一個屬性的值,並期望在兄弟元素的後代規則中檢索到該值。該屬性僅為匹配的選擇器及其後代設定。
使用 @property 控制繼承
@property @ 規則允許你明確宣告屬性是否繼承。以下示例使用 @property @ 規則建立一個自定義屬性。繼承被停用,定義了 <color> 資料型別,並設定了初始值為 teal。
父元素將 --box-color 設定為 green,並使用 --box-color 作為其背景色的值。子元素也使用 background-color: var(--box-color),如果啟用了繼承(或者如果它是用雙破折號語法定義的),我們期望它的顏色是 green。
<div class="parent">
<p>Parent element</p>
<div class="child">
<p>Child element with inheritance disabled for --box-color.</p>
</div>
</div>
@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 時非常有用。
函式的第一個引數是自定義屬性的名稱。函式的第二個引數是一個可選的回退值,當引用的自定義屬性無效時,該值將用作替代值。該函式接受兩個引數,將第一個逗號之後的所有內容都賦給第二個引數。如果第二個引數無效,回退將失敗。例如:
.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> 值的自定義屬性使用了 2rem。2rem 和 peenk 都是無效的顏色值,因此應用了初始值 teal。
@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 規則被應用。
<p>This paragraph is initially black.</p>
p {
font-weight: bold;
color: blue;
}
p {
/* oops, not a valid color */
color: 16px;
}
然而,當自定義屬性的值被解析時,瀏覽器還不知道它們將在哪裡使用,所以它必須將幾乎所有的值都視為有效。不幸的是,這些有效的值可以透過 var() 函式符號在可能沒有意義的上下文中使用。屬性和自定義變數可能導致無效的 CSS 語句,從而引出了計算時有效的概念。
當瀏覽器遇到一個無效的 var() 替換時,將使用該屬性的初始值或繼承值。這個例子和上一個很像,只是我們使用了一個自定義屬性。
瀏覽器將 --text-color 的值替換 var(--text-color),但 16px 對於 color 來說不是一個有效的屬性值。替換後,該屬性沒有意義,所以瀏覽器分兩步處理這種情況:
- 檢查
color屬性是否可繼承。它是可繼承的,但是這個<p>沒有任何父元素設定了color屬性。所以我們進入下一步。 - 將值設定為其預設初始值,即黑色。
<p>This paragraph is initially black.</p>
:root {
--text-color: 16px;
}
p {
font-weight: bold;
color: blue;
}
p {
color: var(--text-color);
}
對於這種情況,@property @ 規則可以透過允許定義屬性的初始值來防止意外結果。
<p>This paragraph is initially black.</p>
@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 中使用自定義屬性的值,就像使用標準屬性一樣。
// 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);