用 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(例如 bookmark/unbookmark 兩種狀態各自的樣式),可以用 @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 圖示元件:取代背景圖示與圖示字型》。