Skip to content

Sass 變數與 CSS 自訂屬性:什麼交給誰

Sass 變數與 CSS 自訂屬性的搭配

「有了 CSS 自訂屬性,還需要 Sass 變數 嗎?」

需要。兩者活在 不同的時間點,能做的事根本不同。

核心差異

scss
$brand: #2965f1;          // 編譯期,產出裡不存在

:root {
  --brand: #2965f1;       // 執行期,產出裡還在
}
Sass 變數CSS 自訂屬性
生效時機編譯期瀏覽器執行時
產出的 CSS只剩最終的值變數本身還在
會不會繼承不會,可分區覆蓋
JavaScript 能改嗎不行可以
能用在媒體查詢條件可以不行
能參與迴圈與運算可以不行
能傳給 Sass 函式可以不行

粗體的部分就是各自無法被取代的地方。

決策表

這個值…
要能在瀏覽器裡切換(主題、深色模式)自訂屬性
要用 JavaScript 改(拖曳、動態尺寸)自訂屬性
元件內部要能被外部覆寫自訂屬性
要當媒體查詢的斷點Sass
要拿去 迴圈 產生類別Sass
要傳給 color.scale() 算色階Sass
從頭到尾都不會變的常數Sass

一句話:編譯期算得完的交給 Sass,執行期才知道的交給自訂屬性。

寫進自訂屬性要插值

這是最常踩的坑:

scss
$brand: #2965f1;

:root {
  --a: $brand;      // ❌ 輸出字面上的 $brand
  --b: #{$brand};   // ✅ 輸出 #2965f1
}

自訂屬性的值對 Sass 來說是「原樣輸出」的內容,不會做替換。必須用 插值

兩層變數的組織

實務上的標準做法是 Sass 當資料來源,自訂屬性當執行期介面

scss
// abstracts/_tokens.scss:Sass 這一層
$spacing: (1: 4px, 2: 8px, 3: 16px) !default;
$themes: (
  light: (bg: #fff, text: #212529),
  dark:  (bg: #1a1a1a, text: #f1f3f5),
) !default;
scss
// base/_variables.scss:產生 CSS 這一層
@use "sass:map";
@use "../abstracts/tokens";

:root {
  @each $key, $value in map.get(tokens.$themes, light) {
    --#{$key}: #{$value};
  }
}

:root[data-theme="dark"] {
  @each $key, $value in map.get(tokens.$themes, dark) {
    --#{$key}: #{$value};
  }
}
scss
// components/_card.scss:使用
@use "../abstracts" as a;

.card {
  padding: a.space(3);      // Sass:固定值,用不著活到執行期
  background: var(--bg);    // 自訂屬性:跟著主題變
  color: var(--text);
}

完整的主題實作見 多主題切換

自訂屬性不能餵給 Sass 函式

scss
.a {
  background: color.scale(var(--brand), $lightness: -20%);  // ❌ Error
}

Sass 在編譯期看到的只是一段 var(--brand) 文字,它不知道那是什麼顏色。

需要在執行期調色,只能用 CSS 原生的相對色彩語法:

css
.btn:hover {
  background: hsl(from var(--brand) h s calc(l - 10%));
}

或者,事先用 Sass 把每個色階都算成一個自訂屬性:

scss
:root {
  --brand: #{$brand};
  --brand-hover: #{color.scale($brand, $lightness: -12%)};
}

元件的可設定介面

自訂屬性會 繼承,所以能當成元件的「外部參數」:

scss
.card {
  --card-padding: #{a.space(3)};
  --card-radius: 8px;

  padding: var(--card-padding);
  border-radius: var(--card-radius);
}
html
<div class="card" style="--card-padding: 32px">

或在某個區塊整批改:

scss
.sidebar .card {
  --card-padding: 8px;
}

這比多定義一個 .card--compact 修飾子彈性得多。

別把所有東西都變成自訂屬性

scss
// ⚠️ 過頭了
:root {
  --z-modal: 1000;
  --transition-fast: 0.15s;
  --border-width: 1px;
}

這些值從來不會在執行時改變。放成自訂屬性只是讓:

  • 產出的 CSS 多一堆用不到的宣告。
  • 開發者工具裡看到的都是 var(--x) 而不是實際值。
  • 沒辦法拿它們做 Sass 運算。

只有真的會變的才需要活到執行期。

常見問題

有了 CSS 自訂屬性,還需要 Sass 變數嗎?

需要。自訂屬性不能用在媒體查詢的條件裡,也不能參與編譯期的運算與迴圈。斷點、間距刻度、要拿去產生類別的資料,這些仍然只能用 Sass 變數。

為什麼自訂屬性裡的 Sass 變數沒被替換?

因為自訂屬性的值被 Sass 視為原樣輸出的內容。要把 Sass 的值寫進去必須用插值包起來,這是兩者搭配時最常踩的坑。

自訂屬性可以放進 Sass 的色彩函式嗎?

不行。Sass 在編譯期看到的只是一段 var() 文字,不是色彩值,傳給色彩函式會報錯。需要在執行時調色只能用 CSS 原生的相對色彩語法。

所有變數都改成自訂屬性可以嗎?

不建議。只有真的會隨狀態改變的值才需要活到執行期,其餘的用 Sass 變數在編譯期解決掉,產出比較單純,也不會讓開發者工具裡滿是變數參照。

延伸閱讀