Skip to content

Sass 對照表 map:主題、斷點與刻度的資料結構

Sass map 的建立、取值與合併

map 是鍵值對的集合,Sass 裡最重要的資料結構。主題色、斷點、間距刻度、字級表,這些「一組有名字的設定」都該收進 map,而不是散落成十幾個 變數

建立

scss
$theme: (
  primary: #2965f1,
  danger: #e03131,
  success: #2f9e44,
);

$breakpoints: (
  sm: 576px,
  md: 768px,
  lg: 992px,
);

最後一項後面留逗號

success: #2f9e44, 結尾的逗號是合法的,之後新增一行時 diff 只會有一行變動。

取值

scss
@use "sass:map";

.btn {
  background: map.get($theme, primary);
}

取不到時回傳 null

scss
color: map.get($theme, primry);  // 打錯字 → null → 這一行整個不輸出

這是 map 最危險的地方

沒有錯誤、沒有警告,樣式就是不見了。建議一律包一層取值函式

scss
@use "sass:map";

@function color-of($name) {
  @if not map.has-key($theme, $name) {
    @error "色彩 #{$name} 不存在,可用的有:#{map.keys($theme)}。";
  }
  @return map.get($theme, $name);
}

打錯字就在編譯期得到一句清楚的訊息,見 自訂函式除錯

常用操作

scss
@use "sass:map";

map.get($theme, primary)        // 取值
map.has-key($theme, primary)    // 有沒有這個鍵 → true / false
map.keys($theme)                // 所有鍵 → primary, danger, success
map.values($theme)              // 所有值
map.set($theme, info, #1c7ed6)  // 回傳「加了一項」的新 map
map.merge($a, $b)               // 回傳合併後的新 map
map.remove($theme, danger)      // 回傳「移除一項」的新 map

map 是不可變的

map.set()map.merge() 都不會改到原本的 map,它們回傳新的。要留下結果得指派回去:

scss
$theme: map.set($theme, info, #1c7ed6);

完整函式表見 sass:map

搭配迴圈:map 真正的價值

一張表加一個 @each,換來一整批樣式:

scss
@each $name, $color in $theme {
  .text-#{$name} { color: $color; }
  .bg-#{$name}   { background: $color; }
}

之後設計師要加一個 warning,你只要在 map 加一行,所有相關的類別自動生出來。這就是「資料與樣式分離」。

巢狀 map

一個項目要記好幾個屬性時:

scss
$buttons: (
  primary: (bg: #2965f1, text: #fff, border: transparent),
  ghost:   (bg: transparent, text: #2965f1, border: currentColor),
);

@each $name, $conf in $buttons {
  .btn-#{$name} {
    background: map.get($conf, bg);
    color: map.get($conf, text);
    border: 1px solid map.get($conf, border);
  }
}

map.get() 也可以一次鑽好幾層:

scss
map.get($buttons, primary, bg)   // #2965f1

合併巢狀 map 要用 deep-merge

scss
$base: (btn: (bg: #2965f1, radius: 4px));
$over: (btn: (bg: #e03131));

map.merge($base, $over);
// → (btn: (bg: #e03131))          ← radius 不見了!

map.deep-merge($base, $over);
// → (btn: (bg: #e03131, radius: 4px))  ✅

map.merge() 只看第一層,第二層是整個換掉。做主題覆寫時幾乎都該用 deep-merge

鍵的型別

任何型別都能當鍵,但實務上一律用 不帶引號的識別字

scss
$a: (primary: red);
map.get($a, primary);     // ✅ red
map.get($a, "primary");   // ❌ null:帶引號是不同的鍵

不帶引號的 primary 與帶引號的 "primary" 在 Sass 裡是兩個不同的鍵,混用就取不到值。細節見 資料型別

一個完整的設定檔

scss
// abstracts/_tokens.scss
$breakpoints: (sm: 576px, md: 768px, lg: 992px) !default;

$spacing: (0: 0, 1: 4px, 2: 8px, 3: 16px, 4: 24px, 5: 32px) !default;

$colors: (
  primary: #2965f1,
  danger:  #e03131,
  text:    #333333,
  surface: #ffffff,
) !default;

加上 !default 就能讓使用這個模組的人用 @use ... with 覆寫。這是 abstracts 目錄 的標準寫法。

常見問題

map 的鍵取不到值時會怎樣?

回傳 null,不會報錯。而值為 null 的宣告整行都不會輸出,所以打錯鍵名的結果就是那條樣式安靜地消失。建議包一層取值函式,取不到就用 @error 明確報錯。

map 可以修改嗎?

map 本身不可變更,map.set()map.merge() 都是回傳一個新的 map,原本那個不受影響。要留下結果就得把回傳值指派回變數。

巢狀 map 怎麼取值?

map.get() 可以接多個鍵,依序往下鑽。合併巢狀 map 時要用 map.deep-merge(),一般的 map.merge() 只會整個換掉第二層,不會逐層合併。

map 的鍵要用什麼型別?

任何型別都可以,但實務上一律用不帶引號的識別字,例如 primarymd。要注意帶引號與不帶引號的字串是不同的鍵,混用會取不到值。

延伸閱讀