CSS前端開發@layerCascadeDesign SystemSpecificity

CSS @layer:戰勝 Specificity 的終極武器

CSS @layer 完整教學!解決 Specificity Wars、取代 !important、Design System 應用攻略。2026 年工程師必看的 CSS 進階指南!

· 6 分鐘閱讀

CSS 的 Specificity(優先級)問題,大概是每一個 CSS 寫久了的人都會踩過的坑。你加了一條 .button.primary 的樣式,結果被 .form .button 覆蓋了;你再加一條 !important,結果這個 !important 開始在專案裡擴散,最後到處都是 !important,沒有人記得為什麼一開始要用。@layer 的出現,終於提供了一個不用 !important 就能控制樣式優先順序的方法。本文整理 @layer 的觀念和實際應用場景。


前端工程師為什麼要關心 @layer

CSS 的 cascade(層疊)規則,本質上是用「後來居上」(last wins)原則解決樣式衝突。但當你的專案變大、CSS 檔案變多、「後來」的意思就越來越模糊。

大型前端專案裡的 CSS 問題通常長這樣:

/* 你寫的 */
.my-component .title {
  color: blue;
}

/* 第三方 UI Library 加的 */
[data-theme="dark"] .component-wrapper .title {
  color: white;
}

/* 某個舊同事加的 */
.title {
  color: red !important; /* 為什麼這裡有 !important?沒人知道 */
}

/* 結果:這個 .title 到底是什麼顏色? */

@layer 解決的問題:讓你有辦法明確宣告「哪個來源的樣式,優先於哪個」,而不是靠選擇器的複雜度或 !important 來猜測結果。


@layer 的基本語法

定義一個 Layer

/* 定義一個名為 "reset" 的 layer */
@layer reset {
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
}

/* 定義一個名為 "base" 的 layer */
@layer base {
  body {
    font-family: system-ui, sans-serif;
    line-height: 1.5;
  }

  h1 { font-size: 2rem; }
  h2 { font-size: 1.5rem; }
}

/* 定義一個名為 "components" 的 layer */
@layer components {
  .button {
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
  }

  .card {
    border: 1px solid #e5e5e5;
    border-radius: 8px;
  }
}

/* 定義一個名為 "utilities" 的 layer */
@layer utilities {
  .text-center { text-align: center; }
  .mt-1 { margin-top: 1rem; }
  .hidden { display: none; }
}

Layer 的優先順序規則

後定義的 layer,優先於先定義的 layer。

@layer reset;
@layer base;
@layer components;
@layer utilities;

/* utilities 優先於 components
   components 優先於 base
   base 優先於 reset */

也就是說,這三條規則同時出現時:

@layer components {
  .title { color: blue; }
}

@layer utilities {
  .title { color: green; }  /* 這個會勝出 */
}

@layer base {
  .title { color: red; }  /* 這個被 components 覆蓋 */
}

color: green 會勝出。因為 utilities@layer 宣告順序中排在 componentsbase 之後。


Layer 與 Specificity 完全無關

@layer 的最重要特性:同一個 layer 內的優先順序,還是依照 standard CSS specificity 規則。跨 layer 的時候,layer 的順序才是決定因素,specificity 不論多高都沒用

/* 不管這個選擇器多強,只要它在同一個 layer 內,就按 specificity 決定 */
@layer components {
  /* specificity: 0,1,0 */
  .card .title {
    color: blue; /* 勝出 */
  }

  /* specificity: 0,2,0 */
  .card .title.special {
    color: purple; /* 因為 specificity 更高,所以這個勝出 */
  }
}

/* utilities 的任何規則,即使 specificity 只有 0,0,1,
   也會覆蓋 components 裡所有針對 .title 的規則 */
@layer utilities {
  .title {
    color: green; /* 會覆蓋上面兩條,即使 specificity 更低 */
  }
}

這個行為看起來可能有點反直覺,但這正是 @layer 的設計目的:讓你先決定「架構」,再讓 specificity 只在「同一個架構層級內」起作用


!important 的替代方案

!important 會讓一條規則無視 specificity 和 cascade 順序,強行覆蓋一切。這種做法簡單粗暴,但代價是讓你的 CSS 系統失去可預測性。

@layer 讓 !important 變得不必要

/* ❌ 過去的做法:用 !important */
/* third-party.css */
.button {
  color: red !important; /* 強行覆蓋,阻礙任何自定義 */
}

/* custom.css */
.button {
  color: blue; /* 無法覆蓋 !important */
}

/* ✅ 現在的做法:用 @layer */
/* 先宣告 layer 順序 */
@layer third-party, custom;

/* 讓 third-party.css 的規則在 custom layer 之前 */
@layer third-party {
  @import url('third-party.css') layer(third-party);
}

@layer custom {
  .button {
    color: blue; /* 這個會勝出,因為 custom 排在 third-party 之後 */
  }
}

custom layer 的樣式優先於 third-party layer,不需要 !important,結果完全可預測。


匿名 Layer

如果你只需要一個一次性的 layer,不想給它命名,可以用匿名 layer:

@layer reset {
  * { margin: 0; }
}

@layer {
  /* 匿名 layer,沒有名字 */
  /* 這個 layer 的優先順序在 reset 之後 */
  body {
    background: #f5f5f5;
  }
}

匿名 layer 的用處:隔離一段樣式,但不打算從外部引用它


Layer 順序宣告的實務模式

方法一:先宣告順序,再寫樣式(推薦)

/* 第一步:宣告所有 layer 的順序(沒有內容)*/
@layer reset, base, framework, components, utilities;

/* 第二步:在各 layer 裡寫內容 */
@layer reset {
  * { box-sizing: border-box; }
}

@layer base {
  body { font-family: system-ui; }
}

@layer framework {
  /* Bootstrap / Tailwind / Vuetify 的 CSS */
}

@layer components {
  .button { padding: 0.5rem 1rem; }
  .card { border-radius: 8px; }
}

@layer utilities {
  .text-center { text-align: center; }
  .hidden { display: none; }
}

先宣告順序的優點:一目了然。任何人都能在第一行看到整個專案的 CSS 層級架構,不需要翻到檔案各處去找 @layer 定義。

方法二:每個 layer 分別定義

/* reset.css */
@layer reset {
  * { box-sizing: border-box; }
}

/* base.css */
@layer base {
  body { font-family: system-ui; }
}

/* 最後在某個 entry file 宣告順序 */
@layer reset, base, components, utilities;

這種模式適合大型 Codebase,每個 layer 是獨立的 CSS 檔案,最後統一管理順序。


Design System 中的 @layer 應用

典型 Design System 的 CSS 層級

一個良好的 Design System,通常有以下幾層(由底到高):

/* 順序:foundation < tokens < base < components < patterns < utilities */
@layer foundation, tokens, base, components, patterns, utilities;

@layer foundation {
  /* CSS Reset */
  * { margin: 0; padding: 0; }
}

@layer tokens {
  /* Design tokens(設計變數)*/
  :root {
    --color-primary: #3b82f6;
    --color-secondary: #64748b;
    --space-1: 0.25rem;
    --space-2: 0.5rem;
  }
}

@layer base {
  /* HTML 元素預設樣式 */
  body { color: var(--color-text); }
  a { color: var(--color-primary); }
}

@layer components {
  /* UI 元件 */
  .btn { padding: var(--space-2) var(--space-4); }
  .input { border: 1px solid var(--color-secondary); }
}

@layer patterns {
  /* 多個元件組成的 Pattern */
  .search-bar { display: flex; }
  .card-grid { display: grid; gap: var(--space-4); }
}

@layer utilities {
  /* 工具類 */
  .hidden { display: none; }
  .text-center { text-align: center; }
}

這個結構的價值:不管你的 component CSS 裡怎麼折騰,utilities layer 的 .hidden 永遠能覆蓋任何東西

第三方 Framework 整合

如果你的 Design System 要整合 Tailwind CSS 或 Bootstrap:

/* 在你自己的 layer 之前引入 framework */
@layer framework {
  @import 'tailwindcss/base';
  @import 'tailwindcss/components';
  @import 'tailwindcss/utilities';
}

/* 你的自定義樣式,永遠優先於 framework */
@layer components {
  .btn-primary {
    background: var(--color-primary);
    /* 這個會勝過 Tailwind 的 .btn */
  }
}

在 JavaScript 中控制 Layer 順序

// 動態改變 layer 順序
const stylesheet = document.styleSheets[0];

// layer 順序可以通過 insertRule() 來調整
// 這在需要根據使用者設定動態切換主題時很有用
// 實際應用:Dark Mode 切換
function switchTheme(theme) {
  const style = document.getElementById('theme-layer');

  // 動態調整 theme layer 的位置
  if (theme === 'dark') {
    // 把 theme layer 移到最高優先級
    document.styleSheets[0].insertRule(
      '@layer theme, reset, base, components, utilities;',
      0
    );
  }
}

瀏覽器支援

@layer 在所有主流瀏覽器都已支援(Chrome 99+、Safari 15.4+、Firefox 97+),沒有任何理由不使用它。

/* Progressive Enhancement:如果瀏覽器不支援 @layer,就按正常 cascade 處理 */
@layer utilities {
  .hidden {
    display: none;
  }
}

結語:什麼時候用 @layer

適合用 @layer 的場景

  • 大型前端專案,多人協作 CSS
  • 需要整合多個第三方 CSS framework
  • Design System 的 CSS 架構
  • 任何你在考慮用 !important 的時候

不需要 @layer 的場景

  • 極小的專案或一次性頁面
  • CSS 架構已經有其他方案(如 CSS Modules、Styled Components)

@layer 不是要取代好的 CSS 架構,但它是在原生 CSS 範圍內管理 specificity 和 cascade 的最佳工具。當你發現你的 CSS 檔案裡開始出現 !important,這就是考慮用 @layer 重構的訊號。


延伸閱讀

本文是「2026 CSS 實用技術」系列文章之一。