Sass abstracts 目錄:變數、mixin 與 function 的分層
7-1 架構 裡,abstracts 是唯一一個 不產生任何 CSS 的目錄。它裡面全是定義(變數、混入、函式),也就是整個專案共用的「樣式詞彙」。
檔案清單
abstracts/
├── _index.scss 入口,全是 @forward
├── _tokens.scss 設計代幣:色彩、間距、字級、圓角
├── _breakpoints.scss 斷點 map 與 respond-to
├── _functions.scss rem()、space()、色彩取值
├── _mixins.scss truncate、visually-hidden 等工具
└── _placeholders.scss %placeholder(用 @extend 才需要)一條規則都不要放
這裡放進 CSS 規則會出問題
// abstracts/_mixins.scss
@mixin center { display: flex; }
.debug-outline { outline: 1px solid red; } // ❌ 不該在這裡模組系統會保證每個模組只編譯一次,所以不會像 @import 時代那樣重複輸出十份。但這條規則的 輸出位置 會變成「第一個載入 abstracts 的檔案那裡」,完全不受你控制,也違反這個目錄的職責。
想要除錯用的樣式,放 base 或獨立的 _debug.scss。
相依順序
abstracts 內部有先後:
_tokens.scss ← 純資料,不依賴任何東西
↓
_functions.scss ← 用到 tokens 的 map
↓
_breakpoints.scss ← 用到 functions
↓
_mixins.scss ← 用到上面全部入口檔照這個順序轉發:
// abstracts/_index.scss
@forward "tokens";
@forward "functions";
@forward "breakpoints";
@forward "mixins";每個檔案還是要自己載入相依
// abstracts/_functions.scss
@use "sass:map";
@use "tokens"; // ← 不能省
@function space($key) {
@return map.get(tokens.$spacing, $key);
}不要依賴入口檔的順序,模組系統要求每個檔案明確宣告自己用到什麼,見 @use。
_tokens.scss:純資料
// abstracts/_tokens.scss
$spacing: (0: 0, 1: 4px, 2: 8px, 3: 16px, 4: 24px, 5: 32px) !default;
$radius: (sm: 4px, md: 8px, full: 999px) !default;
$colors: (
brand: #2965f1,
danger: #e03131,
text: #212529,
surface: #ffffff,
) !default;
$font-size: (sm: 14px, base: 16px, lg: 20px, xl: 24px) !default;全部加 !default,這樣使用這套樣式的專案就能用 @use ... with 換掉,見 變數旗標。
什麼該加、什麼不該加
預期會被調整的設定都加:斷點、間距、主色。純粹內部推導的中間值不要加,加了反而讓人以為那是對外的設定項。
_functions.scss:安全取值
// abstracts/_functions.scss
@use "sass:map";
@use "sass:math";
@use "tokens";
@function space($key) {
@if not map.has-key(tokens.$spacing, $key) {
@error "間距 #{$key} 不存在。可用的有:#{map.keys(tokens.$spacing)}。";
}
@return map.get(tokens.$spacing, $key);
}
@function color($key) {
@if not map.has-key(tokens.$colors, $key) {
@error "色彩 #{$key} 不存在。可用的有:#{map.keys(tokens.$colors)}。";
}
@return map.get(tokens.$colors, $key);
}
@function rem($px, $base: 16px) {
@return math.div($px, $base) * 1rem;
}每個 map 都配一個取值函式,打錯字就在編譯期爆炸,而不是安靜地少一行樣式。理由見 sass:map。
_mixins.scss:真正共用的才放
// abstracts/_mixins.scss
@mixin truncate($lines: 1) {
@if $lines == 1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}只有一個元件會用的混入不要放這裡
@mixin card-shadow 就放在 _card.scss 裡。abstracts 是 跨元件共用 的東西,什麼都往這裡塞會變成新的垃圾桶。
使用端
// components/_card.scss
@use "../abstracts" as a;
.card {
padding: a.space(3);
border-radius: a.radius(md);
background: a.color(surface);
&__title {
font-size: a.rem(20px);
@include a.truncate(2);
}
@include a.respond-to(md) {
padding: a.space(4);
}
}一行 @use 就有全部工具可用。
載入整個目錄,不要個別載入
個別載入雖然更精確,但 abstracts 不產生任何 CSS,省不下任何體積,只換來更多要維護的載入語句。
前綴用什麼
| 寫法 | 評價 |
|---|---|
@use "../abstracts" as a; | 最短,團隊統一即可 |
@use "../abstracts" as t; | t 代表 token,語意好一點 |
@use "../abstracts" as *; | 省事,但看不出來源 |
as * 在這裡風險最高
abstracts 的成員最多,全部倒進全域最容易撞名。保留一個字母的前綴,成本很低。 見 @use。
常見問題
為什麼 abstracts 裡不能寫 CSS 規則?
因為每個元件檔都會載入它。雖然模組系統保證每個模組只編譯一次,不會像舊寫法那樣重複輸出,但把會產生樣式的東西放在這裡會讓載入順序影響產出,違反這個目錄的單一職責。
abstracts 裡的檔案之間有順序嗎?
有相依順序。函式會用到變數、混入會用到函式,所以入口檔的轉發要照這個順序排。同時每個檔案也要自己載入它用到的模組,不能依賴入口檔的順序。
哪些變數該加 !default?
所有預期會被專案調整的設定都該加,例如斷點、間距刻度、主色。純粹內部推導出來的中間值則不必,加了反而讓人以為那是對外的設定項。
元件檔要載入整個 abstracts 還是個別檔案?
載入整個目錄比較實用,一行就有全部工具可用,也不必在新增相依時改載入語句。個別載入雖然更精確,但因為 abstracts 不產生 CSS,省不下任何體積。