手把手教你用 SVG 符號和 CSS 變數做出彩色圖標
簡評:使用圖片和 CSS 樣式來製作網頁圖標的日子已經過去了,隨著網頁字體的爆發,圖標字體成為展示圖標的第一解決方案。
字體都是向量,所以你不必擔心解析度的問題。它們能作為文本來用 CSS 屬性修飾,所以你能夠完全掌控它們的大小,顏色以及樣式。還可以添加變換,效果以及裝飾比如旋轉,下劃線或陰影。
然而圖標字體還不夠完美,這就是為什麼越來越多的人使用內嵌 SVG 圖片的原因。CSS Tricks 寫了一個圖標字體和原生的 SVG 元素相比的諸多缺點:清晰度,位置,因為受到跨域載入,瀏覽器兼容,以及廣告攔截器等因素影響甚至稱得上失敗。現在通過一個安全的選擇來製作圖標字體完全可以避免大多數這種問題。
然而,還有一個圖標字體絕對不能做到的問題:多顏色支持。只有 SVG 能做到。
太長,不讀:這篇文章將深入探討怎麼做以及為什麼。如果你想理解整個過程,就繼續閱讀。如果想直接查看代碼,可以點擊 CodePen.
設置 SVG 符號圖標
內嵌的 SVG 的問題在於它們的冗餘。你肯定不想在每次使用同一個圖標時複製粘貼所有的坐標。這顯得笨拙,可讀性差,難以維護。
使用 SVG 符號圖標,你只要複製整個 SVG 元素一次,然後你就可以用一個引用在任何地方實例化它們。
從內嵌 SVG 開始,隱藏它,並用一個 <symbol> 包裹,然後加上 id 屬性:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none"> <symbol id="my-first-icon" viewBox="0 0 20 20"> <title>my-first-icon</title> <path d="..." /> </symbol></svg>
一次包含完整的 SVG 標記並隱藏在 HTML 里。
然後,只要用 <use> 元素來實例化這個圖標就行了:
<svg> <use xlink:href="#my-first-icon" /></svg>
這樣就可以展示你之前的 SVG 圖標了:
就是辣么簡單!對吧?
你可能注意到了有意思的 xlink:href
屬性:這是你的實例和原始的 SVG 之間的鏈接。
值得一提的是 xlink:href
是一個被棄用的 SVG 屬性。即使大多數瀏覽器仍然支持,你都應該使用 href
來代替它。問題是,有些瀏覽器比如 Safari 還不支持通過 href
屬性引用 SVG 資源,所以你還要提供 xlink:href
屬性。
為了安全起見,同時提供兩個屬性。
添加一些顏色
不像字體,color
屬性對於 SVG 圖標沒有任何作用:你必須使用 fill
屬性來定義一個顏色。這意味著它們不像圖標字體那樣繼承父類的文本顏色,但你仍然可以用 CSS 來為它們添加樣式。
// HTML<svg class="icon"> <use xlink:href="#my-first-icon" /></svg>// CSS.icon { width: 100px; height: 100px; fill: red;}
這裡,你可以用不同的填充顏色創建另一個同樣的圖標實例。
// HTML<svg class="icon icon-red"> <use xlink:href="#my-first-icon" /></svg><svg class="icon icon-blue"> <use xlink:href="#my-first-icon" /></svg>// CSS.icon { width: 100px; height: 100px;}.icon-red { fill: red;}.icon-blue { fill: blue;}
這會起作用,但不是我們確切想要的。到這裡,我們剛才完成的步驟可以得到一個常規的圖標字體。我們想要的是對於圖標的每個部分可以有不同的顏色。我們想要用不同的顏色填充每個 path 而不用更改其他實例,必要的時候可以重寫。
一開始,你可能會想到用特殊性:
// HTML<svg xmlns="http://www.w3.org/2000/svg" stylex="display: none"> <symbol id="my-first-icon" viewBox="0 0 20 20"> <title>my-first-icon</title> <path class="path1" d="..." /> <path class="path2" d="..." /> <path class="path3" d="..." /> </symbol></svg><svg class="icon icon-colors"> <use xlink:href="#my-first-icon" /></svg>// CSS.icon-colors .path1 { fill: red;}.icon-colors .path2 { fill: green;}.icon-colors .path3 { fill: blue;}
然而這並沒什麼用。
我們嘗試為 .path1
, .path2
和 .path3
添加樣式,就好像它們被 .icon-colors
嵌套一樣,但技術上來說並非如此。<use>
不是一個被你定義的 SVG 的佔位符。 它是一個引用,複製了指向暗處的 DOM 的內容。??
那我們該怎麼做?我們怎樣才能影響不在 DOM 中的子元素?
CSS 變數來幫忙
在 CSS 中,一些屬性從祖先到孩子都繼承了。如果你分配給 body
分配一個文本顏色,所有頁面中的文本都會繼承這個顏色,除非顏色被重寫。祖先不知道孩子,但可繼承的樣式被傳遞下來。
我們開始的示例中,繼承了 fill
屬性。再看一次,你會看到我們聲明的 fill
顏色被添加到實例中,而不是定義的 SVG。這就是我們能夠為每個實例添加不同顏色的原因。
現在問題來了:我們想要將不同的顏色傳遞給原始 SVG 的不同的路徑,但我們只能繼承一個 fill
屬性。
看看 CSS 變數。
CSS 變數可以像其他屬性那樣聲明在規則集里。你可以為它取任意的名字,並分配任意的有效的 CSS 值。然後你可以為它聲明一些值或者任意的孩子屬性,它能夠被繼承。
.parent { --custom-property: red; color: var(--custom-property);}
所有 .parent
的孩子都會有紅色的文本。
.parent { --custom-property: red;}.child { color: var(--custom-property);}
所有嵌套在 .parent
的 .child
元素也會有紅色的文本。
現在將這個概念應用到我們的 SVG 符號。我們將在 SVG 定義的每個路徑里用 fill
屬性,然後把它們的值設置為不同的 CSS 變數。這樣一來,我們就能分配不同的顏色了。
// HTML<svg xmlns="http://www.w3.org/2000/svg" stylex="display: none"> <symbol id="my-first-icon" viewBox="0 0 20 20"> <title>my-first-icon</title> <path fill="var(--color-1)" d="..." /> <path fill="var(--color-2)" d="..." /> <path fill="var(--color-3)" d="..." /> </symbol></svg><svg class="icon icon-colors"> <use xlink:href="#my-first-icon" /></svg>// CSS.icon-colors { --color-1: #c13127; --color-2: #ef5b49; --color-3: #cacaea;}
然後……大功告成!??
從現在開始,我們只需要用不同的顏色的 CSS 類創建不同的實例。
// HTML<svg class="icon icon-colors-alt"> <use xlink:href="#my-first-icon" /></svg>// CSS.icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink;}
如果你只想使用單色的圖標,你不用在每個 CSS 變數里重複相同的顏色。你可以使用單個 fill
來代替:因為 CSS 變數沒有被定義,所以將會回滾到你的 fill
聲明。
.icon-monochrome { fill: grey;}
你的 fill
聲明會起作用因為 fill
屬性的值在原始的 SVG 上是沒有定義的 CSS 變數。
我的 CSS 變數名應該叫什麼?
在 CSS 中命名通常有兩種方式:描述性的或者語義性的。描述性意味著什麼顏色就叫什麼:如果你存儲 #ff0000
,那就叫它 --red
。語義性意味著這種顏色是怎麼用的就叫什麼。比如你用 #ff0000
填充咖啡杯把手,你就叫它 --cup-handle-color
。
描述性的名字可能是你下意識的選擇。它看起來更清爽,因為 #ff0000
不僅可以用來填充咖啡杯把手,還可以為其他東西上色。當別的圖標需要上紅色時,一個 --red
的 CSS 變數是可以重用的。畢竟,這就是 CSS 實用至上原則的體現,它也的確是個好體制。
問題在於,在我們的案例中我們不能粒度類應用到我們想要的樣式。不能用實用至上原則,因為我們有每個圖標的引用,我們必須通過類變數來為它們添加樣式。
使用語義化的名字,比如 --cup-handle-color
在這種情況下這樣才比較有意義。當你想要改變某個圖標的部分顏色時,你瞬間就能知道它是什麼,應該重寫成什麼。類名和你分配的無論什麼顏色都能保持關聯。
默認或不默認
將你的圖標設置成多種顏色作為默認狀態是很吸引人的。用這種方式,你可以直接使用它們不需要添加額外的樣式,只有在需要改動時,才會添加你自己的類。
可以通過兩種方式實現: :root 以及 var() default.
:root
你可以用 :root
里定義你所有的 CSS 變數。這樣一來可以把所有的變數放在同一個地方,並允許你「共享「類似的顏色。 :root
有最低的優先權,所以很容易可以重寫它。
:root { --color-1: red; --color-2: green; --color-3: blue; --color-4: var(--color-1);}.icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink; --color-4: orange;}
然而,這種方法有一些主要的缺點。首先,讓顏色的定義和它們各自的圖標分離會變得混亂。當你想要重寫它們時,你必須在 :root
和當前的類之間往返。更重要的是,它不會限制你的 CSS 變數,從而避免你使用相同的名字。
大多數時候,當一個圖標僅使用一種顏色,我會用 --fill-color
。它簡單易懂,當你只想用一種顏色填充所有的圖標時,這種方式也比較行得通。如果我必須在 :root
中聲明所有的變數,我不能有多個 --fill-color
。我必須定義 --fill-color-1
, --fill-color-2
等等,或者使用命名空間比如 --star-fill-color
, --cup-fill-color
。
var() default
用來將 CSS 變數分配給某個屬性的 var()
函數可以將第二個參數作為默認值。
<svg xmlns="http://www.w3.org/2000/svg" stylex="display: none"> <symbol id="my-first-icon" viewBox="0 0 20 20"> <title>my-first-icon</title> <path fill="var(--color-1, red)" d="..." /> <path fill="var(--color-2, blue)" d="..." /> <path fill="var(--color-3, green)" d="..." /> </symbol></svg>
除非你定義 --color-1
,--color-2
和 --color-3
, 否則圖標會使用默認的值為每個 <path>
填充顏色。這解決了之前使用 :root
時全局範圍的問題,但是要注意:你現在使用了默認值,並且它正在生效。結果是,你不能夠使用單個 fill
聲明來定義單色的圖標了。你必須為每個 CSS 變數分配顏色,一個一個地。
設置默認值很有用,但它是種權衡。我建議你不要將這種做法成為一種習慣,只有當它在特定的項目有意義時才使用。
瀏覽器兼容
CSS 變數對於絕大多數現代瀏覽器都是兼容的,但正如你所想的那樣,IE 瀏覽器完全不支持。甚至 IE 11 也不支持,自從 Edge 的開發被擱置後,它再也沒機會趕上了。
現在,僅僅因為一個瀏覽器不支持這個功能,這並不意味著你要推翻一切來迎合它。這種情形有更加優雅的做法:為現代瀏覽器提供多彩圖標,為其他的老版本瀏覽器提供回滾的填充顏色。
你想要做的是當 CSS 變數不支持時設置一個會起作用的聲明,這可以通過為 fill
屬性設置回滾顏色來實現:當支持 CSS 變數時,它不會生效,如果不支持,你的 fill
聲明才會生效。如果你使用 Sass,可以抽象到@mixin
里:
@mixin icon-colors($fallback: black) { fill: $fallback; @content;}
現在我們可以定義我們的顏色方案而不用擔心瀏覽器兼容問題了。
.cup { @include icon-colors() { --cup-color: red; --smoke-color: grey; };}.cup-alt { @include icon-colors(green) { --cup-color: green; --smoke-color: grey; };}
如果你想要了解更多 SVG 的內容,我推薦閱讀 Sara Soueidan 的博客。
原文鏈接:Let』s make multi-colored icons with SVG symbols and CSS variables
推薦閱讀:
KenChoi:開源——設計塊:輕鬆創建簡潔,現代化的網站KenChoi:節約 Web 開發時間的 67 個工具、庫和資源極光日報,極光開發者旗下媒體。
每天導讀三篇英文技術文章。
推薦閱讀: