SCSS 教學:安裝、變數、Mixin 與模組化實作
SCSS 入門教學完整整理:CSS 預處理器 Sass 的主流語法,從安裝、編譯、變數、巢狀與 & 選擇器,到 @use、@mixin、模組化架構與除錯對照表,帶你寫出可長期維護的樣式表。
作者:褚崇名(Sliven)
本頁目錄
- Sass 跟 SCSS 到底是什麼?先別急著背語法
- 2026 年還有必要學 Sass 嗎?原生 CSS 巢狀出現之後的重新定位
- SCSS、Sass、純 CSS 三種寫法的差異,一張表看懂
- Less、Stylus 跟 Sass 比,為什麼生態始終站 Sass 這邊
- 十分鐘安裝 Dart Sass:避開 LibSass 這個已淘汰的地雷
- 方法一:用 Node 的 sass 套件(最推薦給前端專案)
- 方法二:全域安裝(適合快速試玩)
- 方法三:獨立執行檔(不想裝 Node 的人)
- 變數與巢狀:把雜亂 CSS 收斂成可維護的系統
- 變數
- 巢狀
- @use、@forward 與被淘汰的 @import:模組化怎麼做才對
- Mixin、函式與運算子:Sass 真正能幫你省時間的地方
- Mixin:可重複、可帶參數的樣式區塊
- 函式:計算出回傳值
- 運算子
- 用 Sass 打造斷點與設計 Token:響應式系統的實戰打法
- 第一步:定義 token 檔(_tokens.scss)
- 第二步:封裝斷點 mixin(_mixins.scss)
- 第三步:實際寫元件
- Sass 跟現代建置工具串接:Vite、webpack、gulp 各自怎麼接
- Vite(目前新專案的首選)
- webpack
- gulp(維護舊專案常見)
- CSS 體積、壓縮與 Core Web Vitals:先看實際載入成本
- 六個 Sass 常見陷阱:@extend、深層巢狀與其他地雷
- 坑一:@extend 產生失控的選擇器群組
- 坑二:深層巢狀
- 坑三:變數命名沒紀律
- 坑四:把全部框架都 import 進來
- 坑五:混用 @import 跟 @use
- 坑六(補充):過度迷信 @extend 的「乾淨」
- Source Map 與瀏覽器除錯:壓縮過的 CSS 裡還能找到出處
- 一套你會真的重複用的 Sass 工具箱:五個萬年 mixin
- 第一個:截斷單行文字(text truncate)
- 第二個:多行截斷(line clamp)
- 第三個:置中(flex center)
- 第四個:視覺隱藏(screen reader only)
- 第五個:自訂捲軸(custom scrollbar)
- 學完之後做什麼?一張從入門到上線的行動清單
當你寫 CSS 寫到第三年,那份 style.css 往往已經膨脹成兩三千行,改一個主色要全檔搜尋替換四十七個地方,改完還怕漏掉半個頁面。這種「歷史共業」的網站在實務上相當常見,一份累積多年的 CSS 往往就是這副難以維護的模樣。問題的根源僅有一個:樣式沒有被當成一個可維護的系統來管理。
換句話說,Sass(Syntactically Awesome Style Sheets)是一套 CSS 預處理器(preprocessor),讓你用變數、巢狀、模組與函式來寫樣式,再編譯成瀏覽器可讀的 CSS。SCSS 是 Sass 的主流語法格式,與 CSS 語法接近。官方文件整理了兩種語法與完整功能。
這篇會從「為什麼要用」一路講到「怎麼裝、怎麼寫、怎麼編譯、怎麼避坑」,並附上從入門到上線的行動清單。如果你完全沒碰過 CSS,建議先讀 CSS 入門指南,再回來看會順很多。
快速總覽
- Sass 是工具的名字,SCSS 是你該學的那套語法(跟 CSS 幾乎相容)。
- 2026 年原生 CSS 變強了,但 Sass 在設計系統、格線產生、編譯期運算上仍領先。
- 裝 Dart Sass,別碰已被淘汰的 LibSass/node-sass。
- 用 @use 取代 @import,用變數與 mixin 把重複樣式收斂。
- 正式輸出記得壓縮,並實測 CSS 對載入與 Core Web Vitals 的影響。
有人會問:Sass 跟 Tailwind 這類 utility-first 框架是互斥的嗎?其實不是。Tailwind 處理的是「快速組出樣式」,Sass 處理的是「把樣式邏輯結構化」。很多團隊兩者並用:Tailwind 負責日常元件,Sass 負責設計 token、複雜的響應式邏輯、跟那些 Tailwind utilities 表達不了的運算。把它們想成不同層的工具,會比「選一個用到底」更貼近真實工作。
Sass 跟 SCSS 到底是什麼?先別急著背語法
很多人把 Sass 跟 SCSS 搞混,其實它們不是對立的東西。
Sass 是這整個工具的名字,也是一種以縮排為主的語法(副檔名 .sass,不寫分號、不寫大括號)。SCSS(Sassy CSS)則是 Sass 後來推出的另一種語法(副檔名 .scss),語法跟 CSS 完全相容,你可以把一份現成的 .css 直接改名 .scss,它就是合法的 SCSS。
換個比喻:Sass 是品牌,SCSS 是旗下最熱賣的車款。官方文件的語法說明也說得很清楚,這兩種語法編譯出來的結果完全一樣,差別僅在於你喜歡哪種寫法。
實務上,我強烈建議新手從 SCSS 開始。原因很單純:
- 它跟你已經會的 CSS 相容,學習阻力最小
- 團隊協作時,設計師或後端工程師都看得懂
- 主流主題與框架的文件幾乎都用 SCSS 範例
縮排式 .sass 雖然看起來乾淨,但少一個縮排就出錯,除錯成本太高,我不建議在正式專案使用。
一句話總結:Sass 是工具,SCSS 是你該學的那套語法。
理解預處理器這個概念,還有一個好處:它幫你建立「原始碼跟產出可以分開」的觀念。你寫的 SCSS 是給人看的,編譯出來的 CSS 是給機器看的。這個觀念一旦內化,你之後接觸 TypeScript、PostCSS、Babel 這類工具,都會是同一套邏輯:開發時用更強的語言,最後轉成瀏覽器或執行環境能消化的標準格式。Sass 是很多人踏入「前端工程化」的第一塊敲門磚。
還有一點值得先講清楚:Sass 編譯出來的 CSS 跟你手寫的 CSS,對瀏覽器來說完全沒有差別。瀏覽器看不懂 SCSS,它僅認 CSS。Sass 做的事,全在「交付給瀏覽器之前」發生。所以你不用擔心「用 Sass 會不會讓網站變慢」這種問題,因為使用者拿到的永遠是編譯後的純 CSS,跟手寫的一樣輕。會不會慢,取決於你編譯出來的 CSS 寫得好不好,跟用不用 Sass 沒有直接關係。
2026 年還有必要學 Sass 嗎?原生 CSS 巢狀出現之後的重新定位
這是我最常被問的問題,也是網路上很多文章給得最含糊的地方。
2023 年開始,主流瀏覽器陸續支援「原生 CSS 巢狀(CSS Nesting)」,你不用 Sass 也能在 CSS 裡寫出這樣的結構:
.card {
padding: 1rem;
& h2 { font-size: 1.5rem; }
& .body { color: #333; }
}
MDN 的文件說明,CSS Nesting 讓開發者可以用更貼近 HTML 結構的方式組織樣式,不必再倚賴預處理器。
那 Sass 是不是就過時了?我的答案是:還沒有,但它的角色變了。
原生 CSS 現在能做的事變多了。CSS 自訂屬性(custom properties,就是 --var)能取代一部分變數需求,@layer 能管理樣式優先權,:has() 選擇器讓很多以前要靠 JavaScript 的互動變成純 CSS。這些都壓縮了 Sass 的「必要性」。
舉個具體例子:以前你要做「卡片裡有圖片時,文字要往左推」,得靠 JavaScript 判斷子元素再加 class。現在 .card:has(img) 一行選擇器就解決了。再舉一個:以前要管理「reset 樣式、框架樣式、自訂樣式」誰蓋過誰,得靠 source 順序跟 specificity 計算,現在三個 @layer 宣告就釘死優先權。原生 CSS 這幾年的進步是真的快。
但 Sass 變數跟 CSS 自訂屬性,骨子裡是兩種不同的東西,很多新手會以為「有了 CSS 變數就不用 Sass 了」。我用一張表把差異講清楚:
| 面向 | Sass 變數($var) | CSS 自訂屬性(--var) |
|---|---|---|
| 運作時機 | 編譯期(產出前就定值) | 執行期(瀏覽器裡即時計算) |
| 能不能做顏色運算 | 能(lighten、darken) | 要靠 color-mix 等新函式 |
| 能不能跑迴圈產生 | 能(@for、@each) | 不能 |
| 能不能被 JavaScript 改 | 不能(編譯完就固定) | 能(這是它最大的優勢) |
| 主題切換(深色模式) | 要編譯多份 CSS | 改一個屬性就切換 |
| 最終 CSS 體積 | 小(變數編譯完消失) | 大一點(變數留在 CSS 裡) |
看出來了嗎?這兩者其實是分工關係,各管不同的階段。需要執行期動態切換的(深色模式、使用者自訂主題、從 API 拿顏色),交給 CSS 自訂屬性;需要編譯期算好、產生結構化樣式的(格線系統、斷點、間距 scale),交給 Sass 變數。成熟的大型專案往往是兩者並用,各取所長。
但 Sass 仍有幾件事原生 CSS 做不到,或是做得很彆扭:
- 條件與迴圈:根據內容動態產生一堆 class(例如產生
.col-1到.col-12的格線系統),Sass 的@for、@each、@if是真正的程式邏輯,CSS 做不到。 - 函式與運算:顏色運算(把一個藍色調亮 10%)、單位換算、字串處理,Sass 內建一整套(見 官方運算子說明)。
- 編譯期變數:Sass 變數在編譯時就定值,最終 CSS 裡不會帶一堆
--var(),檔案更小、執行期零成本。 - Mixin:一段可重用的樣式邏輯(含參數),原生 CSS 沒有等價物。
換句話說,如果你做的是中型以上的專案、有設計系統要維護、需要產生結構化的樣式工具庫,Sass 仍然是最成熟的選擇。如果僅是寫個一兩頁的活動頁,原生 CSS 真的就夠了。
我給你一個判斷基準:
| 你的情況 | 建議 |
|---|---|
| 單頁活動網站、CSS 少於 500 行 | 原生 CSS 就好,別多裝一層編譯 |
| 多頁形象站、有主題切換需求 | 用 SCSS 加變數管理 token |
| 設計系統、UI 元件庫、需要產生格線 | Sass 是目前最順手的工具 |
| 接手既有 Sass 專案 | 一定要會,不然連樣式都改不動 |
問題的核心在於一件事:你的規模需要哪一層抽象。
SCSS、Sass、純 CSS 三種寫法的差異,一張表看懂
雖然前面提過,這裡用一張表把三種寫法釘清楚,之後你看任何文件都不會再混淆。
| 項目 | SCSS(.scss) | Sass(.sass) | 純 CSS(.css) |
|---|---|---|---|
| 大括號 | 要寫 | 不寫 | 要寫 |
| 分號 | 要寫 | 不寫 | 要寫 |
| 縮排是否強制 | 否 | 是(靠縮排判斷層級) | 否 |
| 支援變數 | 是($var) | 是($var) | 否(僅有 --var) |
| 能用 Sass 全部功能 | 是 | 是 | 否 |
| 與 CSS 相容 | 完全相容 | 不相容 | 就是自己 |
| 適合誰 | 新手、團隊、正式專案 | 喜歡極簡的人 | 僅寫少量樣式 |
同一份樣式,三種寫法編譯出來的 CSS 完全相同。選哪個,是寫作體驗問題,不是功能問題。
在正式專案裡,SCSS 仍是較穩當的選擇。一旦 .sass 遇上原作者縮排風格不固定的情況,往往得耗掉大量時間抓「這個選擇器到底屬於哪一層」的問題,這也是多數團隊對縮排式保持距離的主因。
Less、Stylus 跟 Sass 比,為什麼生態始終站 Sass 這邊
CSS 預處理器不僅 Sass 一家。歷史上還有兩個名字會跟它擺在一起:Less 跟 Stylus。如果你在選工具,先認識它們的定位。
Less 大約在 2009 年出現,最大的特色是用 JavaScript 寫的、能在瀏覽器端直接跑(靠 less.js)。早期 Bootstrap 採用 Less,所以它跟著 Bootstrap 紅過一陣子。Less 的變數用 @ 開頭,語法比 Sass 保守,功能也比較少。
Stylus 則走另一個極端,它的口號是「什麼都可以省略」:分號可省、大括號可省、冒號可省,連變數前綴都能省。靈活到一個程度,但可讀性也崩到一個程度。一個團隊裡每個人寫出來的 Stylus 長得都不一樣,協作成本很高。
我把三者擺在一起給你看:
| 項目 | Sass/SCSS | Less | Stylus |
|---|---|---|---|
| 底層語言 | Dart(編譯期) | JavaScript | JavaScript |
| 變數符號 | $ | @ | 可省略 |
| 巢狀 | 是 | 是 | 是 |
| Mixin 能力 | 強 | 中等 | 很靈活 |
| 迴圈與條件 | 完整 | 有限 | 完整 |
| 主流框架採用 | Bootstrap 4 起、Bulma | Bootstrap 3 以前 | 少數 |
| 社群活躍度 | 最高 | 下滑 | 低 |
為什麼我會一直留在 Sass?原因有三個。
第一,Dart Sass 的編譯期運算最完整。顏色函式、map、字串處理、條件分支,這些在 Stylus 雖然也有,但 Sass 的官方文件跟穩定性最好,踩坑時查得到答案。
第二,主流設計系統跟框架幾乎都站 Sass 這邊。Bootstrap 從第四版開始整個改寫成 SCSS,Bulma 也是 SCSS,連很多 WordPress 主題的原始碼都是 SCSS。這代表你學一套,就能直接讀懂全業界的樣式碼。
第三,Sass 的語法在「自由」跟「紀律」之間拿捏得最好。它不像 Stylus 那樣什麼都能省到難以閱讀,也不像 Less 那樣功能綁手綁腳。SCSS 那種「跟 CSS 幾乎一樣、僅是多了能力」的設計,讓團隊新人的上手時間最短。
老實說,預處理器這個賽道現在已經定於一尊,Sass 一家獨大。如果你要選一個投資學習時間,選 Sass 的投資報酬率最高,沒有第二句話。
十分鐘安裝 Dart Sass:避開 LibSass 這個已淘汰的地雷
安裝這一步,很多人會卡在最古老的坑上:LibSass。
Sass 歷史上有過幾個實作,其中 LibSass(C++ 版本,過去常透過 node-sass 使用)曾經是主流。官方已在棄用公告中將 LibSass 標示為棄用,Node Sass 也已停止維護,不支援新的 Sass 功能。
如果你現在還看到教學叫你 npm install node-sass,那是一篇過時的教學,請直接關掉。現在唯一官方建議的實作是 Dart Sass。
安裝 Dart Sass 最常見的幾條路:
方法一:用 Node 的 sass 套件(最推薦給前端專案)
在專案資料夾執行:
npm install --save-dev sass
接著在 package.json 加一條 script:
"scripts": {
"build:css": "sass src/scss:dist/css --style=compressed"
}
這會把 src/scss 裡的所有 .scss 編譯成 dist/css 的 .css,並壓縮輸出。
方法二:全域安裝(適合快速試玩)
npm install -g sass
sass input.scss output.css
方法三:獨立執行檔(不想裝 Node 的人)
官方安裝頁提供各平台的可執行檔,直接下載就能用。
裝好之後,先在終端機執行 sass --version,能印出版號就代表安裝成功,接著寫一支最簡單的測試:
// style.scss
$primary: #2563eb;
.button {
background: $primary;
color: #fff;
padding: 0.75rem 1.5rem;
&:hover {
background: darken($primary, 10%);
}
}
編譯:
sass style.scss style.css
打開產出的 style.css,你會看到 $primary 已經被替換成實際的色值,hover 那層也自動變成 .button:hover。這就是 Sass 在做的事:你寫邏輯,它幫你產出機器吃的 CSS。
如果你是 WordPress 使用者,很多主題(像 Astra 主題)內部本來就吃 SCSS,學會之後你能直接改主題的設計 token,不必再被佈景主題的設定面板綁死。
變數與巢狀:把雜亂 CSS 收斂成可維護的系統
這兩個是 Sass 最常用、也最快看到效果的功能。
變數
用 $ 開頭定義,任何地方都能引用。
$brand: #2563eb;
$text: #1f2937;
$radius: 8px;
$max-width: 1200px;
.container {
max-width: $max-width;
color: $text;
}
.btn-primary {
background: $brand;
border-radius: $radius;
}
換主色的時候,你只要改 $brand 這一個地方。以常見的客戶網站為例,主色 hardcode 往往散落在數十個檔案裡,改成變數之後,品牌改色就能從「半天的工程」變成「改一行、編譯、收工」。
給你看同一段樣式在 Sass 前後的差別,感受會更具體。
純 CSS 的世界裡,你僅能這樣寫:
.btn-primary { background: #2563eb; border: 1px solid #2563eb; }
.card-header { border-bottom: 2px solid #2563eb; }
.link-active { color: #2563eb; }
主色 #2563eb 重複出現三次,改色要替換三個地方,而且你永遠不知道下一個 #2563eb 躲在哪個檔案。換成 Sass 之後,這三個地方全部引用同一個 $brand,改色僅需要動那一行定義。這就是「可維護性」三個字在現場長什麼樣子。
變數也能做顏色運算:
$primary: #2563eb;
.btn-primary { background: $primary; }
.btn-primary:hover { background: lighten($primary, 8%); }
這比你自己開色票工具去挑一個「大概亮一點的藍」要精準太多。
巢狀
讓你的選擇器結構跟 HTML 一致。
.navbar {
background: #fff;
padding: 1rem;
.logo {
font-size: 1.25rem;
font-weight: 700;
}
.menu li {
display: inline-block;
margin-left: 1.5rem;
a {
text-decoration: none;
color: $text;
&:hover { color: $brand; }
}
}
}
& 代表「目前這一層選擇器」,所以 &:hover 會編譯成 .navbar .menu li a:hover。
巢狀的價值在於:可讀性大幅提升,相關的樣式聚在一起,不用在整份檔案裡跳來跳去找。但要注意一個陷阱:巢狀不要超過三層。深層巢狀會編譯出又長又 specific 的選擇器(例如 .navbar .menu .item .link span.icon),不但效能差,之後要覆寫也很痛苦。我後面踩坑那段會再細講。
如果你想從更根本的地方建立 CSS 觀念,CSS Box Model 指南 值得先讀一遍,因為 Sass 改變的是「怎麼寫」,不是「CSS 怎麼算」。
@use、@forward 與被淘汰的 @import:模組化怎麼做才對
這是 2026 年學 Sass 最容易踩到「過時教學」的地方。
舊版 Sass 用 @import 來拆分檔案:
@import "variables";
@import "buttons";
@import "forms";
看起來直覺,但 @import 有幾個嚴重問題:每個被 import 的檔案裡的變數和 mixin 都是全域的,會互相覆蓋;同一個檔案被 import 兩次會編譯兩次,CSS 重複輸出;而且它跟原生 CSS 的 @import 語意混淆。官方文件明確標示 @import 將在未來版本移除。
現在正確的做法是用 @use。
舊的 @import 寫法:
@import "variables";
$brand; // 直接全域可用
新的 @use 寫法:
@use "variables" as v;
v.$brand; // 透過命名空間引用
@use 的設計是「一個檔案就是一個模組」,變數、mixin、函式都屬於那個模組的命名空間,不會汙染全域。同一個檔案就算被 @use 很多次,也僅會編譯一次。
如果你要寫一個「匯出給別人用」的入口檔案,用 @forward:
// _index.scss
@forward "variables";
@forward "buttons";
@forward "forms";
習慣上,僅被引用、不直接編譯成 CSS 的檔案,檔名前面加底線(例如 _variables.scss),這叫 partial,Sass 不會把它單獨編譯出來。
我的資料夾結構通常是這樣:
scss/
abstracts/
_variables.scss
_mixins.scss
_functions.scss
base/
_reset.scss
_typography.scss
components/
_buttons.scss
_cards.scss
layout/
_header.scss
_footer.scss
main.scss // 唯一的入口
main.scss 裡僅寫 @use:
@use "abstracts/variables";
@use "base/reset";
@use "base/typography";
@use "components/buttons";
@use "components/cards";
@use "layout/header";
@use "layout/footer";
編譯時若編 main.scss 一個檔案,全部樣式會合併輸出成一支 CSS。這套分層源自社群流傳的 7-1 架構:七個各司其職的資料夾,搭配一支入口檔統一輸出;上面這份是精簡版,中小型專案夠用,規模變大再照同樣邏輯往上加。這就是 Sass 做大型樣式架構的基本功。
Mixin、函式與運算子:Sass 真正能幫你省時間的地方
變數跟巢狀是入門,Mixin 跟函式才是 Sass 值得學的深層理由。
Mixin:可重複、可帶參數的樣式區塊
一個經典用途是「停用文字選取」這類會重複出現的小片段:
@mixin no-select {
user-select: none;
-webkit-user-select: none;
}
.price-tag { @include no-select; }
.badge { @include no-select; }
帶參數的 mixin 更有用,例如統一響應式斷點:
@mixin breakpoint($size) {
@if $size == sm {
@media (max-width: 640px) { @content; }
} @else if $size == md {
@media (max-width: 1024px) { @content; }
} @else if $size == lg {
@media (max-width: 1280px) { @content; }
}
}
.hero {
font-size: 3rem;
@include breakpoint(sm) {
font-size: 1.75rem;
}
@include breakpoint(md) {
font-size: 2.25rem;
}
}
@content 讓你在 @include 時塞入一段樣式進去。這種寫法讓你的響應式斷點集中管理,改一個數字全部連動。
函式:計算出回傳值
@function spacing($multiplier) {
@return $multiplier * 0.25rem;
}
.card { padding: spacing(2); } // 0.5rem
.section { padding: spacing(4); } // 1rem
運算子
Sass 支援加減乘除、比較、顏色運算(見 官方運算子說明)。
$base: 16px;
.container { max-width: $base * 75; } // 1200px
把這三件武器組起來,你能做到一件事:把整個設計系統的決策(間距、色階、字級)在編譯期就算好,最終 CSS 乾乾淨淨僅有結果,沒有運算痕跡。
這跟設計端的設計 token(design token)觀念完全對齊。你在 Figma 裡定義好的色票、字級、間距,可以一對一對映成 Sass 變數,設計師改 Figma,你改變數,兩邊不會脫鉤。正因如此,我說 Sass 在「設計系統」這個層級,原生 CSS 還追不上。
用 Sass 打造斷點與設計 Token:響應式系統的實戰打法
這一節我把前面學的東西串成一個能實際用的模式。
實務上做響應式,最大的痛是「斷點寫到處都是、數值每個設計師都不一樣」。Sass 能把這件事一次收斂。
第一步:定義 token 檔(_tokens.scss)
// 色彩
$color-brand: #2563eb;
$color-text: #1f2937;
$color-bg: #ffffff;
$color-border: #e5e7eb;
// 字級(用 scale 而非隨意給值)
$fs-xs: 0.75rem;
$fs-sm: 0.875rem;
$fs-base: 1rem;
$fs-lg: 1.25rem;
$fs-xl: 1.5rem;
$fs-2xl: 2rem;
$fs-3xl: 3rem;
// 間距(8px 系統)
$space-1: 0.25rem;
$space-2: 0.5rem;
$space-3: 0.75rem;
$space-4: 1rem;
$space-6: 1.5rem;
$space-8: 2rem;
$space-12: 3rem;
第二步:封裝斷點 mixin(_mixins.scss)
@use "sass:map";
$breakpoints: (
sm: 640px,
md: 768px,
lg: 1024px,
xl: 1280px,
);
@mixin up($name) {
$size: map.get($breakpoints, $name);
@media (min-width: $size) { @content; }
}
@mixin down($name) {
$size: map.get($breakpoints, $name);
@media (max-width: $size) { @content; }
}
這裡用了 Sass 的 map(類似字典)跟 map.get 來查表,斷點集中在一個地方管。
第三步:實際寫元件
@use "./mixins" as *;
.card {
padding: $space-4;
border: 1px solid $color-border;
border-radius: 8px;
h3 {
font-size: $fs-lg;
margin-bottom: $space-2;
}
@include down(sm) {
padding: $space-3;
h3 { font-size: $fs-base; }
}
}
注意一個重點:這個 .card 元件裡沒有出現任何裸數值。所有的 padding、font-size、color 都來自 token 檔。這表示什麼?整個元件的視覺決策都是可追溯的,你一眼就知道 $space-4 是間距系統裡的哪一階。
這個模式的好處是:所有設計決策都有名字、有出處。哪天設計師說「間距系統改成 4px 基底」,你改 token 檔就好,不用一個元件一個元件翻。
再往前一步,你還能用 Sass 的 @each 迴圈批次產生語意化 class,例如一套配色工具:
$themes: (
brand: #2563eb,
success: #16a34a,
warning: #d97706,
danger: #dc2626,
);
@each $name, $color in $themes {
.text-#{$name} { color: $color; }
.bg-#{$name} { background: $color; }
.border-#{$name} { border-color: $color; }
}
這段會一口氣產出十二個 class(四個主題各三種用途),全部來自同一份 map。新增一個主題時,在 map 裡加一行,Sass 就能依迴圈產生對應 class。這是 Sass 適合集中管理重複樣式的一個例子。
想做更完整的響應式觀念,Responsive Web Design 指南 把行動優先、流體版面這些底層邏輯講得比我這裡更深,推薦接著讀。
Sass 跟現代建置工具串接:Vite、webpack、gulp 各自怎麼接
到目前為止我都在講 Sass 本身。但在真實專案裡,你幾乎不會在終端機手動敲 sass 指令,而是把它掛進建置流程(build pipeline)。這一節把三個最常見的接法講清楚。
Vite(目前新專案的首選)
Vite 對 Sass 的支援是開箱即用的。你若裝好 Dart Sass,Vite 看到副檔名 .scss 就會自動編譯:
npm install --save-dev sass
然後在 JavaScript 或任何元件裡直接 import:
import './scss/main.scss';
開發時 Vite 會用 Hot Module Replacement 即時更新樣式,你改完 SCSS 存檔,瀏覽器不用重整就看到結果。正式打包時,Vite 自動把 CSS 抽出來、壓縮、加上 hash 檔名,完全不用你管。這是2026年我最推薦的組合。
如果你的 SCSS 用到了 @use 搭配 alias 路徑(例如 @use "@/abstracts/variables"),Vite 的 resolve.alias 設定也能直接套用,跟 JavaScript 的路徑解析完全一致,這點對從 webpack 搬家過來的人特別友善。
webpack
webpack 需要手動設定 loader 鏈。基本組合是 sass-loader 加上 css-loader 加上 MiniCssExtractPlugin:
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
}
loader 的順序很關鍵,webpack 是從右到左執行:先由 sass-loader 把 SCSS 編譯成 CSS,再由 css-loader 處理 import 與 url(),接著用 MiniCssExtractPlugin 把 CSS 抽成獨立檔案。順序寫反,打包就會出錯。除非已有現成的 webpack 專案,新專案可先評估設定較少的建置工具。
gulp(維護舊專案常見)
gulp 是任務流(task runner)時代的產物,很多五六年以上的網站還在用。接法是寫一個 gulpfile,用 gulp-sass 這個套件:
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
gulp.task('sass', () => {
return gulp.src('src/scss/**/*.scss')
.pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
.pipe(gulp.dest('dist/css'));
});
這裡有一個關鍵細節:gulp-sass 本身僅是介面,背後的編譯引擎要你明確指定。上面那行 require('sass') 就是叫它用 Dart Sass。很多舊教學會寫 require('node-sass'),那是 LibSass,前面講過已經淘汰,別再裝。
| 建置工具 | 適合誰 | Sass 接法難度 | 我的評價 |
|---|---|---|---|
| Vite | 新專案、SPA、元件化開發 | 極低(裝套件即可) | 2026 年首選 |
| webpack | 大型既有專案、需要極致控制 | 中高 | 彈性大但設定繁 |
| gulp | 傳統多頁網站、舊專案維護 | 中 | 穩定,新專案不必再選它 |
不管你用哪一套,背後編譯 SCSS 的都是同一個 Dart Sass,差別僅在「怎麼把編譯這步接進你的工作流」。把這個觀念建立起來,你換一套建置工具,Sass 的知識可以無縫搬過去。也因此,我建議你把力氣投資在 Sass 語法本身,別把心力耗在某一套特定工具的操作細節上。工具會換,語法跟背後的設計觀念不會。
CSS 體積、壓縮與 Core Web Vitals:先看實際載入成本
這一節是很多 Sass 教學不會講、但對做網站的人很關鍵的事:CSS 體積與載入方式會影響網頁效能,但不能直接換算成 SEO 名次。
依 Google Search Central Blog 在 2020 年 5 月的頁面體驗評估說明,Core Web Vitals(LCP、INP、CLS)是 Google 頁面體驗的一部分,也是小幅排名訊號,相關性遠小於內容是否符合查詢需求。載入速度更直接影響的是使用者體驗與轉換(見 web.dev 的說明)。
放在頁首載入的樣式表通常會阻塞轉譯。檔案大小、網路傳輸、未使用規則與樣式計算都可能影響首次呈現;是否真的拖慢 LCP,要用瀏覽器效能工具與實際使用者資料確認。
Sass 在這件事上是雙面刃:
好的一面:Sass 讓你用 partial 拆檔、模組化管理,最後可輸出壓縮檔。用 --style=compressed 會移除多餘空白等內容,但檔案是否比手寫 CSS 小,仍取決於原始規則與編譯結果。
壞的一面:如果你亂用 @extend 或深層巢狀,編譯出來的選擇器會又臭又長,檔案反而變大。或者你把一整套 UI 框架(像 Bootstrap 的全部 source)都 @use 進來卻僅用三個元件,等於把幾十 KB 的死程式碼一起上線。
幾個我實際會做的優化:
- 僅引入用到的模組:沒有被入口
@use的 partial 不會進入最終 CSS。若整包引入第三方框架,優先使用框架提供的模組化入口;Sass 本身不會自動移除已產生但頁面未使用的選擇器。 - 輸出壓縮模式:正式環境一律用
sass --style=compressed。 - 批判性地用 @extend:後面會講為什麼
@extend常常讓 CSS 反而變肥。 - 需要時使用未用 CSS 清理工具:清理前要把動態產生的 class、狀態與第三方元件加入保留清單,否則可能誤刪正式環境才會出現的樣式。
Sass 幫你組織樣式,但不會自動保證產物精簡。可搭配 Core Web Vitals 與 SEO 與 網站速度優化指南,從實際傳輸量、未使用 CSS 與關鍵轉譯路徑判斷優先順序。
實務上,整包引入 UI 框架與僅引入必要元件,產物大小可能相差很多,但沒有通用的 KB 或秒數。每次打包後記錄壓縮與傳輸後大小,再用 Coverage、Lighthouse 或真實使用者資料確認哪些規則值得移除,會比套用固定門檻可靠。
所以,我一直強調:Sass 不是寫完就沒事,編譯產物要拿放大鏡看。 你養成每次打包後瞄一眼 CSS 體積的習慣,很多效能問題在它變成使用者抱怨之前,就會被你先發現。
六個 Sass 常見陷阱:@extend、深層巢狀與其他地雷
每個工具都有它甜蜜的地雷區。以下是六個最常在實務中出現的坑,逐個拆解給你看。
坑一:@extend 產生失控的選擇器群組
@extend 看起來很方便,讓一個選擇器「繼承」另一個的樣式:
.btn { padding: 1rem; background: #2563eb; }
.btn-primary { @extend .btn; font-weight: 700; }
.btn-large { @extend .btn; font-size: 1.25rem; }
問題在於,@extend 是把選擇器「插入」所有用到 .btn 的地方。如果你的 .btn 出現在十個複合選擇器裡(.sidebar .btn、.card .btn),Sass 會把 .btn-primary 也插到那十個地方。最終 CSS 膨脹、選擇器串成一長串。
選擇 @extend 或 mixin 時,要直接檢查編譯結果。前者適合語意上確實屬於同一類、且選擇器關係單純的情境;後者會複製宣告,較容易預測,但大量重複也可能增加檔案大小,沒有「共用幾次」就一律切換的固定門檻。
坑二:深層巢狀
前面提過,巢狀超過三層就會產生又長又 specific 的選擇器。深層巢狀還有另一個問題:你的 SCSS 看起來很整齊,但實際上選擇器耦合太深,之後想覆寫某一層的樣式,要寫一串一樣深的選擇器才壓得過去。能拍平就拍平,能用單層 class 解決就不要靠後代選擇器。我後來養成一個習慣:寫完一段巢狀,回頭數一下層級,超過三層就重構,這個小動作長期省下來的除錯時間非常可觀。
坑三:變數命名沒紀律
$blue、$blue2、$dark-blue、$darker-blue、$brand-color-old,這種命名最後一定變成一團漿糊。建議用語意命名($color-brand、$color-text、$color-border)而非視覺命名($light-blue),這樣改色票時不會出現「$blue 其實是紅色」的尷尬。顏色怎麼選才專業,色彩理論指南 有完整的討論。
坑四:把全部框架都 import 進來
很常見的新手錯誤:npm 裝了整套 UI 框架,然後在 main.scss 寫 @use "bootstrap"。結果上線的 CSS 裡有 80% 你根本沒用到的元件。正確做法是僅 @use 你真的會用到的那些元件 source,或在框架支援的情況下用其模組化入口逐項引入。
坑五:混用 @import 跟 @use
同一個專案裡有人寫 @import、有人寫 @use,變數會一下出現在全域、一下又不出現,除錯到崩潰。遷移的時候建議一次全部換成 @use,別半套。
坑六(補充):過度迷信 @extend 的「乾淨」
這個值得單獨再講一次,因為它太常見。很多教學會告訴你 @extend 比 mixin「更乾淨」,因為它不會複製樣式。這句話理論上對,實務上常常錯。@extend 會把選擇器往所有出現過的地方塞,當你的繼承鏈一長、加上那些選擇器又出現在巢狀結構裡,編譯出來的選擇器清單會爆炸性成長。一個 .button 被 @extend 之後,最終 CSS 裡可能出現一條三十幾個選擇器串起來的規則,光那一條就佔了三行。記住一個判斷:重複的樣式值(padding、color、font-size)用 mixin 複製,代價遠低於 @extend 串出一條失控的選擇器鏈。
這幾個坑其實指向同一個來源:便利跟紀律的拉扯。 Sass 給你很強的抽象能力,但抽象用過頭,最後就是你跟未來的自己都看不懂。
Source Map 與瀏覽器除錯:壓縮過的 CSS 裡還能找到出處
很多人怕「編譯之後就找不到原始出處」,這在十年前是真的痛點。現在已經有完整的解法:Source Map。
Source Map(副檔名 .css.map)是一個對照表,記錄「壓縮 CSS 的第 N 行,對應到原始 SCSS 的哪個檔案、第幾行」。瀏覽器開發者工具會自動讀取它,讓你在除錯時直接看到 SCSS 原始碼,跳過壓縮後那一坨難讀的東西。
Dart Sass 編譯時加上一個參數就會產生:
sass src/scss:dist/css --style=compressed --source-map
產出會多一個 style.css.map。若這個檔案跟 CSS 放在一起,Chrome 開發者工具點開樣式,就會顯示它來自 _buttons.scss 的第 12 行。
幾個實務注意事項:
- 正式上線要不要放
.map檔,看團隊。放著,等於對外公布你的原始 SCSS 結構;不放,除錯就要靠本機。我的習慣是正式環境不放,但 CI 產物裡保留一份給內部用。 - Source Map 不會影響一般使用者下載的 CSS 大小,瀏覽器僅在開發者工具打開時才去抓
.map,一般訪客不會碰到。 - 如果你用 PostCSS 或其他後處理工具串接,記得讓 Source Map 在整條 pipeline 裡一路傳遞,不然對照會斷在中間。
學會用 Source Map,你才敢放心用 compressed 模式上線。沒有它,壓縮 CSS 一出問題就是大海撈針;有它,點一下就知道是哪一行 SCSS 寫錯了。這是把「開發體驗」跟「上線效能」同時顧好的關鍵一小步。
一套你會真的重複用的 Sass 工具箱:五個萬年 mixin
Sass 教學最常見的問題是「範例都看得懂,但上線不知道該包哪些東西」。這一節我把我每個專案都會帶上的五個 mixin 給你,這些是寫一次、用十年的東西。
第一個:截斷單行文字(text truncate)
@mixin truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-title { @include truncate; }
卡片標題太長要截斷成省略號,這個需求幾乎每個專案都有。包成 mixin 之後一行呼叫搞定。
第二個:多行截斷(line clamp)
@mixin clamp($lines: 2) {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
.excerpt { @include clamp(3); }
文章摘要限制三行顯示。這個跨瀏覽器寫法自己每次重背很煩,包起來最省事。
第三個:置中(flex center)
@mixin center {
display: flex;
align-items: center;
justify-content: center;
}
.hero { @include center; }
Flexbox 置中三行寫到爛,但你不會想每次都手敲。呼叫 @include center 比較快,而且語意更清楚,一眼就看出這個區塊要置中。
第四個:視覺隱藏(screen reader only)
@mixin sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.skip-link { @include sr-only; }
這個 mixin 把元素在視覺上隱藏,但螢幕閱讀器(screen reader)還是讀得到。做無障礙(accessibility)必備。很多人不知道,這組屬性是業界公認最穩定的寫法,比 display:none 更適合用來放「跳到主內容」這種輔助連結。把它收進你的工具箱,遇到要做無障礙檢測的客戶,你會慶幸自己早就準備好了。
第五個:自訂捲軸(custom scrollbar)
@mixin custom-scroll($track: #f1f1f1, $thumb: #c1c1c1) {
&::-webkit-scrollbar { width: 8px; }
&::-webkit-scrollbar-track { background: $track; }
&::-webkit-scrollbar-thumb {
background: $thumb;
border-radius: 4px;
}
scrollbar-width: thin;
scrollbar-color: $thumb $track;
}
.code-block { @include custom-scroll($thumb: $color-brand); }
程式碼區塊、側邊欄的捲軸想跟主題配色一致,這個 mixin 幫你一次處理 WebKit 跟 Firefox 兩套語法,不用自己記兩組屬性的差異。參數化之後,每個區塊還能指定不同的配色。
這五個 mixin 我會放進一個 _mixins.scss,每開新專案就 @use 進來。累積下來,你等於手上隨時有一組經過實戰驗證的工具,寫樣式的速度會明顯比從零開始快一截。這才是 Sass 真正的長期紅利:你把解過的問題包成可重用的資產,不必每次都重新發明輪子。
學完之後做什麼?一張從入門到上線的行動清單
讀到這裡,你腦裡應該對 Sass 有一張完整的地圖了。知識不等於會用,我給你一張把它變成肌肉記憶的步驟。
- 裝好 Dart Sass,跑出第一支編譯。今天之內,用 npm 在一個練習資料夾裝
sass,寫一支style.scss,編譯出style.css。這一步跨過去,你就不再是「僅會讀不會做」。 - 拿一個你現有的 CSS 檔案來重構。挑你自己網站裡一份兩三百行的 CSS,把顏色、字級、間距抽成變數,把相關選擇器巢狀起來。親自感受「改一個變數,全站連動」的爽感。
- 建立你的 token 檔跟斷點 mixin。照我前面給的
_tokens.scss結構,把你的設計系統寫下來。這個檔案會是你未來所有專案的起點。 - 把 @import 全部換成 @use。如果你手上有舊專案還在用
@import,排個時間一次遷移完。未來官方移除@import時你才不會措手不及。 - 上線前用壓縮模式編譯,檢查檔案大小。正式環境可用
--style=compressed,再以瀏覽器開發者工具檢查傳輸量、快取與未使用規則;不要把單一固定 KB 當成所有網站的及格線。 - 把速度觀念接上來。Sass 處理的是「怎麼寫 CSS」,網站真正的成績單是 Core Web Vitals。把設計、效能、SEO 三件事串起來看,你才看得懂自己的 CSS 到底寫得好不好。
這六步我建議你排進行事曆,一週走一步,別想一天搞定。 Sass 的學習曲線很特別:前兩步(裝好、改一個現有檔案)三十分鐘就能跨過去,但第四步的模組化遷移、第五步的體積優化,需要你對整個專案結構有足夠理解才做得穩。給自己時間消化,比硬背語法有用太多。
學 Sass 是為了讓你的樣式碼像一棟有結構圖的房子,避免變成一團每年都要重拉的電線。把這六步走完,你手上的 CSS 會從「每次改都怕」變成「改了放心」。
如果你是 WordPress 站長,想從更大的視角看自己有哪些架站選擇,網頁設計自學路線圖 能幫你把 Sass 放回整個技能地圖裡對的位置。Sass 是工具,不是信仰。你會不會用它,決定的是你維護樣式的效率,跟你網站能不能成功是兩回事。但當你的網站規模走到一定程度,這份效率,就是你跟半夜還在改 CSS 的自己之間,最大的差別。
對 WordPress 使用者來說還有一個實際的好處:很多主流佈景主題的子主題(child theme)都會附一支 style.scss 或開放 SCSS source。你看懂 Sass,就等於拿到改主題的萬能鑰匙,能做的事遠比在後台設定面板裡拉拉桿子多得多。從「被主題綁著走」到「主題聽你的」,中間往往就差這一座 Sass 的橋。
常見問題
SASS 跟 SCSS 有什麼不同?要學哪一個?
SCSS 要怎麼安裝與編譯出 CSS?
@mixin 和 @extend 到底該用哪一個?
SCSS 會被原生 CSS 巢狀或 Tailwind 取代嗎?
操作步驟
- 安裝 Dart Sass:跨平台用 npm install -g sass,或從官方下載獨立執行檔;務必確認裝的是 Dart Sass 而非已停止維護的 node-sass
- 用 sass -v 或 sass --version 驗證安裝成功,能印出版號即代表沒問題
- 做單次編譯測試 sass sass/style.scss:css/style.css,會在 css 資料夾產出 style.css 與除錯用的 source map
- 正式編譯用 sass src/scss:dist/css --style=compressed --source-map 一次輸出壓縮 CSS 與除錯對照檔;也可把這行寫進 package.json 的 build script,讓打包流程自動執行。