SVG 最常應用在 icon 上,不只因為它是向量圖,icon 也很常出現在按鈕等互動元素中,會隨著狀態改變樣式。試想一下,如果每一種狀態的 icon 都要從設計稿匯出一個圖檔,靜態檔案的數量膨脹好幾倍。光想像就讓人瑟瑟發抖。
今天會以 button 中的 icon 為例,講解如何用 fill 與 stroke 修改 inline SVG 的顏色。來更優雅地處理 icon 吧!
⚠️這些技巧只適用於直接寫在 HTML 裡的 inline SVG。如果用
<img src="star.svg">或 CSS 的background-image載入,SVG 會被當成獨立的圖片,外層的 CSS 與 currentColor 都無法影響它。
昨天的 SVG 介紹中,我們知道能用 fill 與 stroke 來設定圖形的顏色。除了直接修改 <svg> 的屬性外,還有更多應用場景。先來看看該如何讓按鈕中 SVG icon 的顏色與尺寸,跟著按鈕的文字顏色與字體大小同步。
先介紹今天的助教——星星。參考註解,可以清楚知道 <svg> 內的四個標籤分別繪製了哪部分。現在的星星是一個固定 200px × 200px 的黑框星星。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<!-- 星星:每個角都用 Q 曲線切成圓角 -->
<path
d="M96.01 23.07 ... Z"
fill="none"
stroke="#000"
stroke-width="4"
stroke-linejoin="round"
/>
<!-- 眼睛 -->
<circle cx="84" cy="96" r="6" fill="#000" />
<circle cx="116" cy="96" r="6" fill="#000" />
<!-- 嘴巴 -->
<path d="M80 113 Q100 136 120 113" fill="none" stroke="#000" stroke-width="4" stroke-linecap="round" />
</svg>

現在,要讓圖形的顏色能套用外層 <button> 的設定。只要把圖形的 fill / stroke 改成 currentColor,SVG 圖形就會吃到外層的文字顏色。
接著,順便讓圖形尺寸套用按鈕的文字設定,把 <svg> 上面指定 width 與 height 拿掉,改在 CSS 將寬高設為 1em。
<button type="button" class="star-btn">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<path
d="M96.01 23.07 ... Z"
fill="none"
stroke="currentColor"
stroke-width="4"
stroke-linejoin="round"
aria-hidden="true"
/>
<!-- 眼睛 -->
<circle cx="84" cy="96" r="6" fill="currentColor" />
<circle cx="116" cy="96" r="6" fill="currentColor" />
<!-- 嘴巴 -->
<path d="M80 113 Q100 136 120 113" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" />
</svg>
<span>star</span>
</button>
.star-btn {
color: #000;
font-size: 40px;
svg {
width: 1em;
height: 1em;
}
}
.star-btn:hover {
color: orange;
background: yellow;
border-color: orange;
font-size: 48px;
}

這樣一個附 icon 的按鈕就完成了。顏色與尺寸只需要設定在 <button> 這一層,icon 就會跟著按鈕文字一起變化,維護起來簡單不少。
如果顏色變化不只一種,可以用 CSS 分別設定不同標籤的 fill / stroke。先把剛剛寫死在 HTML 標籤上的屬性移除,改用 class 選取器來設定。例如,我想要分開設定星星的填色跟嘴巴顏色,就能在對應的 class 設定 fill 與 stroke。
原本嘴巴是一條線,不是封閉圖形,所以要寫 fill: none。若把 HTML 上的 fill="none" 移除後,圖形會回到預設的黑色填充。
<button type="button" class="star-btn">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<path class="star-shape" d="M96.01 23.07 ... Z" stroke-width="4" stroke-linejoin="round" aria-hidden="true" />
<!-- 眼睛 -->
<circle cx="84" cy="96" r="6" fill="currentColor" />
<circle cx="116" cy="96" r="6" fill="currentColor" />
<!-- 嘴巴 -->
<path class="star-mouth" d="M80 113 Q100 136 120 113" stroke-width="4" stroke-linecap="round" />
</svg>
<span>star</span>
</button>
.star-btn {
.star-shape {
fill: #808080;
stroke: currentColor;
}
.star-mouth {
fill: pink;
stroke: pink;
}
}
.star-btn:hover {
color: orange;
.star-shape {
fill: yellow;
}
}

外框跟隨文字顏色(currentColor),星星填色與嘴巴顏色則各自獨立,互不影響。另外,CSS 的優先權高於標籤上的屬性,所以即使 HTML 上還留著 fill,CSS 也能直接覆蓋它。
同理,stroke-width、stroke-linecap 等外觀相關的屬性,也都可以改用 CSS 來寫。
回到一開始的問題:以前 default、hover、disabled 每種狀態與顏色,都要一個圖檔;現在只要一份 SVG,狀態與顏色都交給 CSS 決定。蒸蚌!!
.
.
.
透過相對直觀的 fill / stroke,我們知道可以直接在 SVG 標籤上設定屬性,也能利用 CSS 與選擇器做更細緻的樣式控制。
明天,我想來一點漸層。漸層不能直接寫在 fill 裡,需要先用 <defs> 定義,再透過 url(#id) 引用。看看曾經讓我傷透腦筋的漸層按鈕吧!