如何在CSS中使用變數
- 2022 年 8 月 21 日
- 筆記
前言
CSS變數(官方稱為自定義屬性)是用戶定義的值,它可以在你的程式碼庫中設置一次並多次使用。它們使管理顏色、字體、大小和動畫值變得更加容易,並確保整個web應用的一致性。
舉個例子,你可以將品牌顏色設置為一個CSS屬性(--primarycolor: #7232FA
),並在任何使用品牌顏色的組件或樣式中使用這個值(background: var(--primarycolor);
)。
除了更加簡潔以及不重複的程式碼,CSS變數可用於構建調色板,提高響應能力,並創建動態類型系統。
定義CSS變數
要定義一個自定義屬性,選擇一個名稱並在其前面加上兩個連字元。任何字母數字字元都可以是名稱的一部分。連字元(-
)和下劃線(_
)也是被允許的。大範圍的Unicode字元可以成為自定義屬性名稱的一部分。這也包括emoji,但為了清晰度和可讀性,請堅持使用字母數字名稱。
下面是個例子:
--primarycolor: #0ad0f9ff; /* RGB alpha 16進位顏色 */
--
向CSS解釋器表明這是一個自定義屬性。當作為一個變數使用時,解釋器引擎會將該屬性替換為其值。
自定義屬性名稱是區分大小寫的。這意味著--primaryColor
和 --primarycolor
被認為是兩種不同的屬性名稱。這與傳統的CSS不同,在傳統的CSS中,屬性和值的大小寫並不重要。然而,它與ECMAScript中的變數名規則是一致的。
與其他屬性一樣,比如說display
或者font
,CSS自定義屬性必須定義在聲明塊內。一個常見的模式是使用 :root
偽元素作為選擇器來定義自定義屬性。
:root {
--primarycolor: #0ad0f9ff;
}
:root
是一個偽元素,它指向文檔中的根元素。對HTML文檔而言,指向的就是<html>
元素。對SVG文檔而言,指向的就是<svg>
元素。使用:root
會讓屬性在整個文檔中立即可用。
使用CSS變數
為了讓自定義屬性作為變數來使用,我們需要使用var()
函數。例如,如果我們想將--primarycolor
自定義屬性作為背景顏色來使用,我們需要這麼做:
body {
background-color: var(--primarycolor);
}
我們自定義屬性的值將成為background-color
屬性的計算值。
到目前為止,自定義屬性只能被用作變數來為標準的CSS屬性設置值。例如,你不能把一個屬性名稱存儲為一個變數,然後重用它。以下的CSS就無法運行:
:root {
--top-border: border-top; /* Can't set a property as custom property's value */
var(--top-border): 10px solid #bc84d8; /* Can't use a variable as a property */
}
你也不能把屬性-值對存儲為一個變數,然後重用它。下面的例子也是無效的:
:root {
--text-color: 'color: orange'; /* Invalid property value */
}
body {
var(--text-color); /* Invalid use of a property */
}
最後,你不能把一個變數作為值的一部分拼接起來:
:root {
--base-font-size: 10;
}
body {
font: var(--base-font-size)px / 1.25 sans-serif; /* Invalid CSS syntax */
}
CSS自定義屬性 VS CSS變數
“自定義屬性”是一個面向未來的名稱,它說明了這個功能有一天可能會被使用。然而,如果瀏覽器供應商實施了CSS擴展規範,這種情況就會改變。該規範定義了用自定義選擇器組合、函數和at-rules
來擴展CSS的方法。
我們通常稱自定義屬性為 “變數”,到目前為止,這也是我們可以使用它們的唯一方式。在理論上,它們並不是完全可以互換的術語。不過目前為止在實踐中是可以互換的術語。在這篇文章中,我主要使用自定義屬性,因為那是它們的正確叫法。如果能是句子更加清晰,我將使用變數的叫法。
設置備用值
var()
函數接收兩個參數。第一個參數是自定義屬性名稱。第二個參數是可選的,但必須是聲明值。這個聲明值的功能是當自定義屬性值沒有被定義時,作為一個備用值或默認值被應用。
讓我們來看看下面的CSS:
.btn__call-to-action {
background: var(--accent-color, deepskyblue);
}
如果定義了--accent-color
,我們假設其值為#f30
,那麼任何帶有.btn__call-to-action
類的背景色將是橘紅色的。如果沒有定義,背景色將是深天藍色。
聲明值也可以嵌套。換句話說,可以使用變數作為var
函數的備用值。
body {
background-color: var(--books-bg, var(--arts-bg));
}
在上面CSS中,如果定義了--books-bg
,背景色將會被設置為--books-bg
屬性的值。如果沒有定義,背景色將是分配給 --arts-bg
的任何值。如果兩個屬性都沒有定義,背景色將會是屬性的初始值。在這個例子中,初始值為transparent
。
當一個自定義屬性的值對它所使用的屬性來說是無效的,也會發生類似的情況。可以考慮下面的CSS:
:root {
--text-primary: #600;
--footer-link-hover: #0cg; /* Not a valid color value */
}
body {
color: var(--text-primary);
}
a:link {
color: blue;
}
a:hover {
color: red;
}
footer a:hover {
color: var(--footer-link-hover);
}
在這個例子中,--footer-link-hover
屬性的值不是有效顏色。反而,footer a:hover
從<body>
元素中繼承了color
值。
解析自定義屬性的方式與解析其他CSS值的方式相同。倘若值是無效的或者未定義的,如果屬性是可繼承的,CSS解析器就會使用繼承的值(比如說color
或font
)。如果屬性是不可繼承的,CSS解析器就會使用初始值(比如說background-color
)。
層疊值
自定義屬性也遵循層疊的規則。它們的值會被後續的規則所覆蓋:
:root {
--text-color: #190736; /* navy */
}
body {
--text-color: #333; /* dark gray */
}
body {
color: var(--text-color);
}
在上述示例中,body
字體顏色將會是深灰色。我們還可以在每個選擇器的基礎上重置值。讓我們向這個CSS添加更多的規則:
:root {
--text-color: #190736; /* navy */
}
body {
--text-color: #333; /* dark gray */
}
p {
--text-color: #f60; /* orange */
}
body {
color: var(--text-color);
}
p {
color: var(--text-color)
}
在這個例子中,任何包裹在<p>
標籤內的文本會是橘黃色。但是<div>
內的文本或是其他元素內的文本仍然是深灰色。
也可以使用style
屬性來設置自定義屬性的值。比如說,style="--brand-color: #9a09af"
。
調色板
自定義屬性特別適合管理HSL調色板。HSL代表hue, saturation, lightness 。這是一款基於亮度的顏色模型,類似於RGB。我們可以在CSS中使用HSL,這要歸功於hsl()
和hsla()
顏色函數。hsl()
函數接收三個參數,分別是hue, saturation, 和 lightness,也就是色相、飽和度、亮度。hsla()
函數同時接收第四個參數,表示顏色的alpha
透明度(取值範圍是0-1之間)。
RGB系統用紅、綠、藍的比例來表達顏色,而HSL則使用一個顏色圈,色相是該圈上的一個度數位置,而色調或陰影則用飽和度和亮度值來定義。飽和度的範圍從0%到100%,其中0%是灰色,100%是全色。亮度的範圍也是從0%到100%,其中0%是黑色,100%是白色,50%是正常顏色。
在HSL顏色系統中,主要顏色紅、綠、藍三原色在0度/360度、120度和240度處相距120度。二級顏色–青色、品紅和黃色–也相距120度,但位於主要顏色的對面,分別為180度、300度和60度/420度。三級、四級和其他顏色則以大約10度的增量介於兩者之間。藍色用HSL來寫的話,將會是hsl(240, 100%, 50%)
。
HSL參數單位
當你在hsl()
和hsla()
函數的第一個參數中使用無單位的值時,瀏覽器會假定它是一個以度為單位的角度。然而,你可以使用任何受支援的CSS角度單位。藍色也可以表示為hsl(240deg, 100%, 50%)
, hsl(4.188rad, 100%, 50%)
或者 hsla(0.66turn, 100%, 50%)
。
這就是有趣的地方。我們可以使用自定義屬性設置色相值,以及通過調整飽和度和亮度來設置更亮和更暗的陰影:
:root {
--brand-hue: 270deg; /* purple */
--brand-hue-alt: .25turn; /* green */
/*
hsl()和hsla()可以接受逗號分隔的參數或空格分隔的參數。
但舊的瀏覽器(如Internet Explorer 11)只支援逗號分隔的參數。
*/
--brand-primary: hsl( var( --brand-hue ) 100% 50% );
--brand-highlight: hsl( var( --brand-hue ) 100% 75% );
--brand-lowlight: hsl( var( --brand-hue ) 100% 25% );
--brand-inactive: hsl( var( --brand-hue ) 50% 50% );
--brand-secondary: hsl( var( --brand-hue-alt ) 100% 50% );
--brand-2nd-highlight: hsl( var( --brand-hue-alt ) 100% 75% );
--brand-2nd-lowlight: hsl( var( --brand-hue-alt ) 100% 25% );
--brand-2nd-inactive: hsl( var( --brand-hue-alt ) 50% 50% );
}
上面的CSS提供了下面所示的調色板。
這是一個簡易版本,但你也可以使用自定義屬性來調整飽和度和亮度值。
健全的調色板生成
另一個想法是結合自定義屬性和calc()
函數,從一個基本色調中生成一個方形的配色方案。讓我們在下面的例子中創建一個方形配色方案。一個方形配色方案由四種顏色組成,這些顏色在色輪上彼此等距,也就是相距90度:
:root {
--base-hue: 310deg; /* Hot pink */
--distance: 90deg;
--color-a: hsl( var(--base-hue), 100%, 50% );
--color-b: hsl( calc( var(--base-hue) + var( --distance ) ), 100%, 50% );
--color-c: hsl( calc( var(--base-hue) + ( var( --distance ) * 2 ) ), 100%, 50% );
--color-d: hsl( calc( var(--base-hue) + ( var( --distance ) * 3 ) ), 100%, 50% );
}
CSS為我們提供了如下所示的頗具熱帶風情的色彩方案。
自定義屬性也能很好地與媒體查詢相互配合,我們會在後面章節中看到。
深色主題調色板
你可以使用CSS自定義變數為你的網站,定義與深色和淺色主題相關的一系列的變數。
以下面的頁面樣式為例,我們可以在:root
中為相應的顏色定義了自定義屬性後,用變數替換不同選擇器中所有的HSL顏色:
:root {
/*...*/
--nav-bg-color: hsl(var(--primarycolor) , 50%, 50%);
--nav-text-color: hsl(var(--primarycolor), 50%, 10%);
--container-bg-color: hsl(var(--primarycolor) , 50%, 95%);
--content-text-color: hsl(var(--primarycolor) , 50%, 50%);
--title-color: hsl(var(--primarycolor) , 50%, 20%);
--footer-bg-color: hsl(var(--primarycolor) , 93%, 88%);
--button-text-color: hsl(var(--primarycolor), 50%, 20%);
}
已經為自定義屬性使用了適當的名稱。比如說,--nav-bg-color
指的是導航欄背景色,--nav-text-color
指的是導航欄前景色或者文本色。
然後,複製:root
選擇器及其內容,但要為其添加一個帶有dark
值的主題屬性:
:root[theme='dark']{
/*...*/
}
如果帶有dark
值的主題屬性被添加到<html>
元素上,那麼該主題將被激活。
現在我們可以手動操作這些變數值,通過減少HSL顏色的亮度值來提供一個深色主題。或者我們可以使用其他技術,如invert()
和brightness()
等CSS過濾器,它們通常用於調整影像的渲染,但也可用於其他任何元素。
添加如下程式碼到:root[theme='dark']
中:
:root[theme='dark'] {
--dark-hue: 240;
--light-hue: 250;
--primarycolor: var(--dark-hue);
--nav-bg-color: hsl(var(--primarycolor), 50%, 90%);
--nav-text-color: hsl(var(--primarycolor), 50%, 10%);
--container-bg-color: hsl(var(--primarycolor), 50%, 95%);
--content-text-color: hsl(var(--primarycolor), 50%, 50%);
--title-color: hsl(--primarycolor, 50%, 20%);
--footer-bg-color: hsl(var(--primarycolor), 93%, 88%);
--button-text-color: hsl(var(--primarycolor), 50%, 20%);
filter: invert(1) brightness(0.6);
}
invert()
過濾器會反轉所選元素的所有顏色(在本例中是每個元素)。反轉的值可以用百分比或數字來指定。參數值100%或1將完全反轉元素的色相、飽和度和亮度值。
brightness()
過濾器使元素更加明亮或者暗黑。如果數值為0,就會出現一個完全暗黑的元素。
invert()
過濾器會使一些元素變得非常明亮。需要通過設置brightness(0.6)
來降低亮度。
具有不同程度深色的深色主題:
使用JavaScript切換主題
現在,當用戶點擊深色/淺色按鈕時,我們使用JavaScript來切換深色和淺色主題。在HTML</body>
標籤閉合前添加行內<script>
標籤,標籤內具有如下程式碼:
const toggleBtn = document.querySelector("#toggle-theme");
toggleBtn.addEventListener('click', e => {
console.log("Switching theme");
if(document.documentElement.hasAttribute('theme')){
document.documentElement.removeAttribute('theme');
}
else{
document.documentElement.setAttribute('theme', 'dark');
}
});
Document.documentElement指的是文檔中的根DOM元素,也就是<html>
。這段程式碼使用.hasAttribute()
方法檢查theme
屬性是否存在。如果不存在,就將dark
添加到該屬性上,從而導致切換到深色主題。否則,它將刪除該屬性,從而導致切換到淺色主題。
注意:你還應該把它與CSS中的prefers-color-scheme功能結合起來使用,它可以用來從用戶的作業系統或用戶代理(瀏覽器)設置中自動改變淺色/深色主題。這將在下一節中展示。
媒體查詢
我們還可以在媒體查詢中使用自定義屬性。比如說,你可以使用自定義屬性來定義明暗配色方案:
:root {
--background-primary: hsl(34, 78%, 91%);
--text-primary: hsl(25, 76%, 10%);
--button-primary-bg: hsl(214, 77%, 10%);
--button-primary-fg: hsl(214, 77%, 98%);
}
@media screen and ( prefers-color-scheme: dark ) {
:root {
--background-primary: hsl(25, 76%, 10%);
--text-primary: hsl(34, 78%, 91%);
--button-primary-bg: hsl(214, 77%, 98%);
--button-primary-fg: hsl(214, 77%, 10%);
}
}
同樣地,我們可以使用自定義屬性來改變適合於螢幕與列印的基準字體大小。
:root {
--base-font-size: 10px;
}
@media print {
:root {
--base-font-size: 10pt;
}
}
html {
font: var(--base-font-size) / 1.5 sans-serif;
}
body {
font-size: 1.6rem;
}
在本例中,我們正在使用適合於列印和螢幕的媒體單位。對於這兩種媒體,我們將使用10個單位的基準字體大小,對於螢幕來說是像素(px),對於列印來說是點(pt)。我們還將使用--base-font-size:
的值來為我們的根元素(html
)設置一個起始尺寸。然後我們可以rem
單位來調整相對於基準字體尺寸的排版。
在JavaScript中使用自定義屬性
請記住:自定義屬性是CSS屬性,而且我們可以與它們進行互動。比如說,我們可以使用CSS.supports()
API來檢測瀏覽器是否支援自定義屬性:
const supportsCustomProps = CSS.supports('--primary-text: #000');
// 如果瀏覽器支援自定義屬性,則列印true
console.log(supportsCustomProps);
我們也可以使用setProperty()
方法來設置自定義屬性值:
document.body.style.setProperty('--bg-home', 'whitesmoke');
使用removeProperty()
的方式也是類似的。只需傳遞自定義屬性名稱作為參數:
document.body.style.removeProperty('--bg-home');
要使用自定義屬性作為JavaScript的值,可以使用var()
函數,並將屬性名稱作為其參數。
document.body.style.backgroundColor = 'var(--bg-home)';
還有,你不能使用style
對象的方括弧語法或駝峰屬性來設置自定義屬性。換句話說,document.body.style.--bg-home
或document.body.style['--bg-home']
都無法發揮作用。
在組件中使用自定義屬性
像React、Angular和Vue這樣的JavaScript框架讓開發者使用JavaScript來創建可重用、可共享的HTML塊,通常會在組件層面上定義CSS。
這是一個有關React組件的示例,使用JSX語法編寫:
import React from 'react';
/* Importing the associated CSS into this component */
import '../css/field-button.css';
class FieldButtonGroup extends React.Component {
render() {
return (
<div className="field__button__group">
<label htmlFor={this.props.id}>{this.props.labelText}</label>
<div>
<input type={this.props.type}
name={this.props.name}
id={this.props.id}
onChange={this.props.onChangeHandler} />
<button type="submit">{this.props.buttonText}</button>
</div>
</div>
);
}
}
export default FieldButtonGroup;
我們的React組件將CSS導入到一個JavaScript文件中。編譯時,field-button.css
的內容被內聯載入。這裡有一個可行的方法來使用自定義屬性:
.field__button__group label {
display: block;
}
.field__button__group button {
flex: 0 1 10rem;
background-color: var(--button-bg-color, rgb(103, 58, 183)); /* include a default */
color: #fff;
border: none;
}
在該示例中,我們使用自定義屬性--button-bg-color
作為按鈕的背景顏色,同時伴隨著默認顏色,以防--button-bg-color
沒有定義。在這裡,我們可以在全局樣式表或通過style
屬性設置--button-bg-color
的值。
讓我們將值設置為React屬性。React props(簡稱為屬性)模仿元素屬性。它們是一種將數據傳入React組件的方式。在本例中,我們將添加一個名為buttonBgColor
的屬性:
import FieldButtonGroup from '../FieldButtonGroup';
class NewsletterSignup extends React.Component {
render() {
// For brevity, we've left out the onChangeHandler prop.
return (
<FieldButtonGroup type="email" name="newsletter" id="newsletter"
labelText="E-mail address" buttonText="Subscribe"
buttonBgColor="rgb(75, 97, 108)" />
);
}
}
export default NewsletterSignup;
現在,我們需要更新FieldButtonGroup
組件,來支援本次更改:
class FieldButtonGroup extends React.Component {
render() {
/*
In React, the style attribute value must be set using a JavaScript
object in which the object keys are CSS properties. Properties
should either be camelCased (e.g. backgroundColor) or enclosed in
quotes.
*/
const buttonStyle = {
'--button-bg-color': this.props.buttonBgColor
};
return (
<div className="field__button__group">
<label htmlFor={this.props.id}>{this.props.labelText}</label>
<div>
<input type={this.props.type}
name={this.props.name} id={this.props.id}
onChange={this.props.onChangeHandler} />
<button type="submit" style={buttonStyle}>
{this.props.buttonText}
</button>
</div>
</div>
);
}
}
在上述程式碼中,我們添加了一個buttonStyle
對象,該對象包含自定義屬性的名稱,並將其值設置為buttonBgColor
屬性的值,並為按鈕添加style
屬性。
使用style
屬性可能與你所學到的關於編寫CSS的知識相悖。CSS的一個賣點是,我們可以定義一套樣式,在多個HTML和XML文檔中使用。另一方面,style屬性將CSS的範圍限制在它所應用的元素上,導致我們不能重用它。並且也不能利用層疊的優勢。
但在一個基於組件的前端架構中,一個組件可能在多種情況下被多個團隊使用,甚至可能在客戶端項目中共享。在這些情況下,你可能想把層疊的全局範圍與style
屬性所提供的局部範圍結合起來。
用style
屬性設置自定義屬性值,可以將效果限制在FieldButtonGroup
組件這個特定實例中。但是,由於我們使用了一個自定義屬性而不是標準的CSS屬性,我們仍然可以選擇在樣式表中定義--button-bg-color
,而不是作為一個組件屬性。
總結
自定義屬性採用了預處理器的最佳功能之一–變數,並使其成為CSS的原生功能。使用自定義屬性,我們可以:
- 創建可重用的、主題化的組件
- 輕鬆調整內邊距、外邊距以及排版,以適應各種視口尺寸和媒體
- 改進CSS顏色值的一致性
變數有一系列的應用,在基於組件的設計系統中特別有用。我希望你現在對如何在CSS中使用變數或自定義屬性有了更好的理解。
以上就是全部內容,如果對你有所幫助,歡迎點贊收藏轉發~