如何在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解析器就會使用繼承的值(比如說colorfont)。如果屬性是不可繼承的,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%是正常顏色。

調色板.png

在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提供了下面所示的調色板。

image.png

這是一個簡易版本,但你也可以使用自定義屬性來調整飽和度和亮度值。

健全的調色板生成

另一個想法是結合自定義屬性和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為我們提供了如下所示的頗具熱帶風情的色彩方案。

image.png

自定義屬性也能很好地與媒體查詢相互配合,我們會在後面章節中看到。

深色主題調色板

你可以使用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)來降低亮度。

具有不同程度深色的深色主題:

dark-theme.png

使用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-homedocument.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中使用變數或自定義屬性有了更好的理解。

以上就是全部內容,如果對你有所幫助,歡迎點贊收藏轉發~