本文 AI 產出,尚未審核

Vue3 – 樣式與 CSS 管理

主題:CSS Variables(自訂屬性)


簡介

在 Vue3 專案中,CSS Variables(又稱自訂屬性)是管理顏色、字體大小、間距等 UI 常數的利器。相較於傳統的 SCSS 變數只能在編譯階段使用,CSS Variables 是原生瀏覽器支援、在執行階段即可動態改變的屬性,讓我們能夠在單一元件或全域層面即時調整樣式,提升主題切換、暗黑模式、以及多語系 UI 的彈性。

本文將從概念說明、實作範例、常見陷阱與最佳實踐,帶你在 Vue3 中善用 CSS Variables,寫出 可維護、可重用 的樣式程式碼。


核心概念

1. CSS Variables 基礎語法

/* 定義變數,必須在 :root(全域)或任何元素上 */
:root {
  --primary-color: #42b983;
  --font-base: 16px;
}

/* 使用變數 */
.button {
  background-color: var(--primary-color);
  font-size: var(--font-base);
}
  • -- 開頭的名稱是 自訂屬性
  • var(--name, fallback) 可帶第二個參數作為備援值。

2. 在 Vue3 中使用 CSS Variables

2.1 直接在 <style> 中定義

<template>
  <button class="btn">按鈕</button>
</template>

<style scoped>
:root {
  --btn-bg: #409eff;
  --btn-color: #fff;
}
.btn {
  background: var(--btn-bg);
  color: var(--btn-color);
  padding: 0.5rem 1rem;
}
</style>

注意scoped 仍會把變數提升到對應的元素上,若想全域變數,請改用 :root 在非 scoped 樣式或 src/assets/css/variables.css 中統一管理。

2.2 透過 Vue 組件的 style 屬性動態改變

<template>
  <div :style="themeStyle">
    <p>這段文字會跟著主題變色</p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const theme = ref('light')   // light / dark

const themeStyle = computed(() => ({
  '--bg-color': theme.value === 'dark' ? '#1e1e1e' : '#fff',
  '--text-color': theme.value === 'dark' ? '#eee' : '#333'
}))
</script>

<style scoped>
div {
  background: var(--bg-color);
  color: var(--text-color);
  padding: 1rem;
}
</style>
  • 使用 :style 綁定一個 物件,鍵名即為 CSS Variable 名稱(含 --),即可在執行階段切換主題。

2.3 在 setup() 中使用 useCssModule(Vue CLI 5+)

<template>
  <header :class="$style.header">Vue3 樣式範例</header>
</template>

<script setup>
import { onMounted } from 'vue'
import styles from './Header.module.css'

onMounted(() => {
  // 直接操作 CSS 變數
  document.documentElement.style.setProperty('--header-height', '80px')
})
</script>

<style module>
.header {
  height: var(--header-height, 60px);
  background: #35495e;
  color: #fff;
  line-height: var(--header-height, 60px);
  text-align: center;
}
</style>
  • module 讓 class 名稱自動轉為唯一值,仍可透過 document.documentElement.style.setProperty 全域設定變數。

3. 結合 SCSS / PostCSS

即使使用 CSS Pre‑processor,也能同時保留原生變數的彈性:

/* variables.scss */
:root {
  --primary: #42b983;
  --spacing: 1rem;
}

/* 其他 SCSS 檔案 */
.button {
  padding: var(--spacing);
  background: var(--primary);
}

SCSS 只負責計算、混入等功能,最終仍交給瀏覽器解析 var()


程式碼範例(3–5 個實用範例)

範例 1:全域主題切換(暗黑 / 明亮)

<template>
  <div class="app" :class="{ dark: isDark }">
    <button @click="toggleTheme">切換主題</button>
    <slot />
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const isDark = ref(false)

function toggleTheme() {
  isDark.value = !isDark.value
}

// 監聽變化,寫入根層變數
watch(isDark, (val) => {
  const root = document.documentElement
  if (val) {
    root.style.setProperty('--bg', '#1e1e1e')
    root.style.setProperty('--text', '#e0e0e0')
  } else {
    root.style.setProperty('--bg', '#fff')
    root.style.setProperty('--text', '#333')
  }
})
</script>

<style scoped>
.app {
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  transition: background 0.3s, color 0.3s;
}
button {
  margin: 1rem;
}
</style>

重點:只需要在根層改變變數,即可讓所有子元件自動套用新樣式,避免逐一調整 class。

範例 2:動態調整間距(Responsive Gap)

<template>
  <div class="grid" :style="{ '--gap': gap + 'px' }">
    <div v-for="n in 6" :key="n" class="item">#{{ n }}</div>
  </div>

  <input type="range" min="8" max="40" v-model="gap" />
</template>

<script setup>
import { ref } from 'vue'
const gap = ref(16)
</script>

<style scoped>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
  gap: var(--gap);
}
.item {
  background: #42b983;
  color: #fff;
  padding: 0.5rem;
  text-align: center;
}
</style>

使用 :style 動態設定 --gap,即時改變 Grid 間距,適合 UI 設計師提供「間距調整」的即時預覽功能。

範例 3:多語系字體切換

<template>
  <div class="content" :style="{ '--font-family': currentFont }">
    <p>這是一段示範文字。</p>
    <select v-model="currentFont">
      <option value="'Noto Sans TC', sans-serif">中文(Noto Sans TC)</option>
      <option value="'Roboto', sans-serif">英文(Roboto)</option>
    </select>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const currentFont = ref("'Noto Sans TC', sans-serif")
</script>

<style scoped>
.content {
  font-family: var(--font-family);
  line-height: 1.6;
}
select {
  margin-top: 1rem;
}
</style>

透過變數切換 font-family,不需要重新載入樣式檔,適合 多語系或客製化字體 的需求。

範例 4:在組件庫中提供可自訂的顏色變數

// MyButton.vue
<template>
  <button class="my-btn"><slot /></button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    /** 自訂顏色,如不提供則使用全域變數 */
    color: { type: String, default: null }
  },
  mounted() {
    if (this.color) {
      this.$el.style.setProperty('--btn-color', this.color)
    }
  }
}
</script>

<style scoped>
.my-btn {
  --btn-color: var(--primary-color, #42b983); /* fallback */
  background: var(--btn-color);
  color: #fff;
  border: none;
  padding: 0.5rem 1rem;
  cursor: pointer;
}
</style>

使用者在使用 <MyButton color="#ff5722"> 時,只會改變該實例的背景色,而不影響其他按鈕。


常見陷阱與最佳實踐

陷阱 說明 解決方案
變數作用域不明 scoped 樣式裡定義 :root 仍會被限制在該元件,導致全域無法使用。 將全域變數寫在 src/assets/css/variables.css,在 main.jsimport
變數名稱拼寫錯誤 var(--primary-colr) 會回傳 unset,且不會拋錯。 使用 IDE 的 CSS 變數自動補全,或在 :root 中寫註解列舉所有變數。
fallback 被忽略 若變數在父層未定義,var(--x) 會回傳 invalid不會自動使用 CSS 預設值 明確寫 var(--x, #default),或在根層提供基本值。
過度依賴 JavaScript 更改 每次 setProperty 會觸發 repaint,若頻繁更新會影響效能。 限制變更頻率(如 debounce),或改用 CSS @mediaprefers-color-scheme
瀏覽器相容性 IE 不支援 CSS Variables。 若需支援舊版瀏覽器,可使用 postcss-custom-properties 轉譯。

最佳實踐

  1. 集中管理:所有全域變數放在 variables.css,以 :root 為起點。
  2. 語意化命名:如 --color-primary, --spacing-md,避免硬編碼顏色。
  3. 提供 fallbackvar(--color-primary, #42b983),保證在變數缺失時仍有預設。
  4. 結合 prefers-color-scheme:自動偵測系統暗色模式,減少 JavaScript 程式碼。
  5. 使用 @layer(CSS Layers):讓變數覆寫有明確層級,避免意外被 later CSS 覆寫。

實際應用場景

  1. 主題系統:企業內部平台需要支援多套品牌色,只要在 :root 中切換 --brand-primary 即可完成。
  2. 動態尺寸調整:設計師在 Figma 上調整間距後,前端只需改變 --spacing-lg,所有相關元件自動更新。
  3. 多語系或客製字體:根據使用者語系切換 --font-family,避免重新載入樣式表。
  4. 組件庫:提供可透過 styleprop 覆寫的變數,讓使用者在不改寫原始碼的情況下客製化 UI。
  5. 動畫與過渡:使用 var(--duration) 控制所有過渡時間,讓全站動畫節奏統一,未來調整只改一個變數。

總結

CSS Variables 在 Vue3 中不只是「顏色的別名」,更是 跨元件、跨主題、跨執行階段 的樣式溝通橋樑。透過 :root 集中管理、setProperty 動態更新、以及在 <style scoped> 中的語意化使用,我們可以:

  • 快速實作暗黑/亮色主題,不需重寫大量 CSS。
  • 即時調整間距、字體、動畫時間,提升 UI 設計的彈性。
  • 在組件庫中提供可自訂的樣式 API,降低使用門檻。

只要遵守 命名規範、提供 fallback、避免過度頻繁變更,CSS Variables 就能成為 Vue3 前端開發者手中最實用的樣式工具。快把它加入你的專案,讓樣式管理更簡潔、維護更容易吧!