# CSS Variables

### 起源

因為大家都在用 sass/scss 寫樣式了，而且也真的方便許多，所以 CSS 學習這些優點開始改造，而本篇要談的便是變數系統。

### 用法

先來看看 [scss variables 是如何使用](https://sass-lang.com/documentation/variables)的：

```scss
// scss 變數定義
$color-primary: blue;
$prefix: 'blueplanet'

.primary-text {
    // scss 變數使用
    color: $color-primary;
}

/*
scss 變數轉換成為字串使用方式
    會輸出成
    .blueplanet-button {
        color: red;
    }  
*/
.#{$prefix}-button {
    color: red;
}
```

現在來看看 CSS Variables 是如何使用的：

```css
:root {
    --color: red;
}

.primary-text {
    color: var(--color);
}
```

首先使用 `--` 定義名稱與內容，要使用時再加上 `var()` 把變數包起來即可。

注意，這裡不一定要使用 :root ，只是通常會先在根目錄定義變成全局都可以使用比較方便。詳細規則如下說明。

### 規則

#### `--` 開頭做定義

如剛才的範例，要定義一個 CSS 變數，會先使用 `--` 開頭做定義。

#### 變數名稱區分大小寫

名稱針對大小寫是敏感的，請確保名稱的大小寫完全一致。

#### 要使用變數時，在「值」的地方，使用 var() 把變數包起來

請注意，`var()` 僅能使用在「值」，不能使用在屬性、selector 名稱等其他地方。

<pre class="language-css"><code class="lang-css"><strong>/* 定義在 root 裡，是方便全局都可使用 */
</strong>:root {
    --color: red;
    --width: 768px;
}

/* 正確使用方式 */
.primary-text {
    color: var(--color);
}

/* 以下都是錯誤方式 */

.primary-var(--color) {
    color: red;
}

.primary-text {
    var(--color): red;
}

</code></pre>

也就是說，CSS Variables 目前功能還沒辦法跟 SASS/SCSS 相提並論，但已經算很好用了。

#### CSS 變數具有繼承的概念、有區域性(Scoping)、且可重複定義。

如果沒有額外定義，CSS 變數將會一直繼承到子元素裡。假設我們現在有：

```html
<div class="parent">
    <div class="child-1">Child</div>
    <div class="child-2">Child</div>
</div>
```

```css
:root {
    /* 
        如果是定義在個別的 selector 裡，
        變數就只能在該 selector 裡使用。
        在 root 則是全部 selector 都能使用
    */
    --color: red;
}

.parent {
    color: var(--color);
}

.child-1 {
    color: var(--color);
}

.child-2 {
    --color: green;
    color: var(--color);
}
```

{% embed url="<https://codepen.io/neillin1023/pen/ZEmEJvQ>" %}

在此範例中，我們在 `.child-2` 重新定義了 `--color` 變數，所以再次指定屬性時，文字顏色便會改變，而 `.child-1` 指定屬性時，還是一樣的顏色。

{% hint style="info" %}
一定要再次針對該屬性給值，否則是不會改變的喔！

例如 `.child-2` 如果沒有再寫一次 `color: var(--color);` ，

`.child-2` 的文字依舊會是紅色的！

請注意，並不是改變變數，就會直接套用在外觀上，還得再應用一次！
{% endhint %}

#### 應用 var() 時，如果取不到 CSS 變數，可以給予預設值(fallback)

其實 var() 可以給予兩個變數，第一個為想取得的變數，第二個為預設值，而預設值裡也可用變數。

```css
:root {
    --default-color: red;
    --color: green;
}

.demo {
    /* 如果取不到 --color，則會是橘色 */
    color: var(--color, orange);
}

.demo-2 {
    /* 如果取不到 --color，則會是紅色 */
    color: var(--color, var(--default-color));
}

.demo-3 {
    /* 如果取不到 --color 也取不到 --default-color，則會是紫色 */
    color: var(--color, var(--default-color, purple));
}
```

#### 遇到屬性不可以使用的 `var()` 值時，本身視為「沒有樣式」，要先檢查是否有繼承，如果沒有繼承則會使用瀏覽器預設值

一般來說，當我們重複定義元素的 css 時，如果最後套用的屬性值是不合規定的，通常就是視為無效，會套回到次一個權重的樣式。例如：

```css
.demo {
    color: red;
}

.demo {
    color: 16px;
}
```

<figure><img src="https://3433349642-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfNa3cQXA0JgS2aeBbzYg%2Fuploads%2FGSgNGEWuTYjzt5SccApQ%2F%E6%88%AA%E5%9C%96%202023-06-07%20%E4%B8%8A%E5%8D%8810.12.24.png?alt=media&#x26;token=2bd16814-0c3e-484d-9f99-aed9144e5068" alt=""><figcaption></figcaption></figure>

這樣的結果，`.demo` 會是紅色的，後面是無效值瀏覽器會無視，直接使用前一個定義的樣式。

而 `var()` 是不一樣的，瀏覽器依舊會套用變數後的結果不會無視，造成之前定義的視為被覆蓋了，但又因為我們的變數值跟屬性是不合的，所以等同於這個 selector 完全沒有定義樣式了，依照 CSS 的規則，也就是說，要不就是使用繼承，要不就是使用預設值，例如：

```css
:root {
    --width: 768px;
    --color: green;
}

html {
    color: red;
}

.demo {
    color: var(--color, orange);
}

.demo {
    color: var(--width);
}
```

{% embed url="<https://codepen.io/neillin1023/pen/oNQNeQq>" %}

<figure><img src="https://3433349642-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfNa3cQXA0JgS2aeBbzYg%2Fuploads%2Fg9qCCGP0E7jelK1wcXLe%2F%E6%88%AA%E5%9C%96%202023-06-07%20%E4%B8%8A%E5%8D%8810.07.45.png?alt=media&#x26;token=ec957724-f80a-4aaf-90bb-0e36ff999ca5" alt=""><figcaption></figcaption></figure>

可以看到，第一次定義的 demo 被瀏覽器視為覆蓋，但又因為第二次定義的值有應用但是無效的值，等於 demo 自己本身完全沒有顏色樣式了，所以只能看看上層是否有繼承下來，如果沒有就會變成預設值。

### Javascript 可以控制 CSS 變數

這對於前端工程師來說變得方便極了！以往沒辦法直接改 SASS/SCSS 的變數（因為他需要經過編譯的過程），可能要透過 selector ＋ css 的方式更改樣式，或是直接使用 style.XXX 直接改，搞到後來一團亂，如果現在可以使用 Javascript 取得變數和改變數，在樣式的管理上將會是非常方便的功能，也比較不怕改壞 CSS。

```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);
```

### 進階用法：使用 @container style 樣式查詢

善用 CSS Variables 管理樣式

{% embed url="<https://codepen.io/neillin1023/full/yLmKoQG>" %}

當偵測到 CSS Variables 的值是某特定值時，樣式會自行套用該樣式。

這樣的好處是，可以將 CSS Variables 當成條件使用，當然，可以結合上一小節的 Javascript 改變 CSS Variables 的值，靈活性將大幅上升。（不過還是建議 CSS 的事就 CSS 自行解決，否則很容易找不到到底在哪裡被改變值了）

### 小結

原生 CSS 取代 SASS/SCSS 的開始，且目前主流瀏覽器都已支援，少了一層依賴，就少了一層風險，建議大家可以開始應用在專案/產品上了。

### 作業

請試著規劃出一個主題色變數檔案，方便切換色系。

### 參考連結

* <https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties>
* <https://css-tricks.com/everything-need-know-css-variables/>
