跳到主要內容
前端開發

用 SCSS 的 Map 與 @each 統一管理背景圖示

整理用 SCSS 的 map 資料結構搭配 @each 迴圈,批次產生一組背景圖示 class 的做法,同一份清單同時管理 hover/normal 兩種狀態各自對應的圖檔路徑。這個做法現在多半被 SVG 圖示元件取代,整理在新的一篇。

W

網站圖示如果是一批各自獨立的 PNG 檔案(而不是圖示字型或 SVG),每個圖示又要處理 hover/normal 兩種狀態,手動幫每一個圖示各寫一段 CSS 很容易漏東漏西。 這篇整理當時用 SCSS 的 map 加 @each 迴圈批次產生的做法。

用 map 存圖示路徑

$icon-name: 'bookmark', 'avatar', 'chat', 'page', 'stars', 'focus';

$iconImageGroup: (
  'bookmark-hover': '../../assets/_img/_icon/icon-bookmark-hover.png',
  'bookmark-normal': '../../assets/_img/_icon/icon-bookmark-normal.png',
  'stars-hover': '../../assets/_img/_icon/icon-stars-hover.png',
  'stars-normal': '../../assets/_img/_icon/icon-stars-normal.png',
  'focus-hover': '/_img/_icon/icon_focus_hover.png',
  'focus-normal': '/_img/_icon/icon_focus_normal.png',
);

$icon-name 是一份圖示名稱清單,$iconImageGroup 是一個 map(SCSS 的鍵值對結構),把每個圖示 hover/normal 兩種狀態各自對應到一個圖檔路徑。

用 @each 批次產生 class

@each $name in $icon-name {
  &.icon-#{$name} {
    width: 26px;
    height: 24px;
    padding: 0px !important;
    background: url(map-get($iconImageGroup, $name + '-normal'));
    background-repeat: no-repeat;
    background-position: center center;
    transition: 0.3s;
    margin-right: 2px;
    display: inline-block;
  }
}

@each $name in $icon-name 會把清單裡的每個名稱輪流帶入迴圈,#{$name} 是字串插值,讓 class 名稱依名稱動態產生(.icon-bookmark.icon-avatar……),map-get() 則依名稱去 map 裡查對應的圖檔路徑。 六個圖示的樣式規則就這樣一次產生完,之後要加第七個圖示,只需要在兩份清單裡各加一筆。

依名稱做例外處理

不是每個圖示都遵循同一套規則,某些圖示需要額外的狀態 class(例如 bookmarkunbookmark 兩種狀態各自的樣式),可以用 @if 針對特定名稱加規則:

@each $name in $icon-name {
  &.icon-#{$name} {
    // 通用規則...

    @if ($name == 'bookmark') {
      &.bookmark {
        cursor: pointer;
        background: url(map-get($iconImageGroup, 'bookmark-hover'));
        width: 22px;
        height: 32px;
      }
      &.unbookmark {
        width: 22px;
        height: 32px;
        background: url(map-get($iconImageGroup, 'bookmark-normal'));
      }
    }
  }
}

@if 在迴圈內部依當下的 $name 值判斷,只有名稱符合條件時才會加上額外的巢狀規則,其餘圖示不受影響。

這個做法的代價

每個圖示、每種狀態都是一個獨立的圖檔請求。hover 狀態要切換圖片,代表瀏覽器要先把 hover 版本的圖也下載下來(或是遇到滑鼠移上去才臨時載入,反而有延遲閃爍的問題),圖示數量一多,請求數跟著往上疊。

顏色調整要重新出圖。網站要換色系、或同一個圖示要在不同背景色上呈現不同顏色,PNG 圖示沒辦法用 CSS 調色,只能請設計重新出一版圖檔。

2026 年後記

這套「用資料結構驅動批次產生樣式」的思路本身沒有過時,SCSS 的 map/@each 現在同樣好用。真正被取代的是用一堆獨立 PNG 檔案管理圖示這件事——現在多半用 SVG 圖示元件,一份圖示可以用 currentColor 直接吃 CSS 的文字顏色,不需要為了換色重新出圖,也不用為了 hover 狀態多準備一張圖。整理在《SVG 圖示元件:取代背景圖示與圖示字型》。

參考資料

分享這篇