miyaoka.dev

外部読み込みSVGに色を付ける

SVG 画像を inline ではなく外部読み込みにしたくなっていろいろ試した。

Why?

このサイトでは Feather Icons という SVG アイコンを使っていて、react 用のライブラリがあるのでそれで描画している。これだと各アイコンが inline 化されるのだが、静的書き出ししているこのサイトだと各ページに同じ svg データが入ることになるのでちょっと無駄だなと思った。

なので、inline ではなく画像アセットとして読み込むのを試してみた。

(微々たる差でしかないし、パフォーマンス的には inline のほうが良さそうなのは承知の上で)

結果

上段: img、中段: svg use、下段: mask + 背景色
上段: img、中段: svg use、下段: mask + 背景色

いくつかやり方があり、うまくいくものといかないものがあったので書いておく。

img の場合

<img src={`/images/${src}.svg`} className="h-8 w-8 text-red-300" />

これが最もシンプルだしセマンティックな感じもするので良さそうなものだが、ここで style 指定しても色が適用されないという弱点がある。そのために inline 化しているわけだ。

svg use の場合こう

<svg className="h-8 w-8 text-red-300">
  <use href={`/images/${src}.svg`}></use>
</svg>

他にやり方がないかと調べたら、img ではなく svg の use で読み込めばいいらしい。が、試しても表示されなかった。なぜかと言うと .svg#id のように id 付きで読み込まないといけないらしい。元々 id 振っていないアイコンなのでこれも合わなかった。

mask + 背景色

<div
  className="h-8 w-8 bg-red-300"
  style={{
    WebkitMask: `url(/images/${src}.svg) no-repeat center / contain`,
  }}
/>

さらにやり方を探したところ、svg を mask 画像として使い、背景色で色を設定するというやり方があった。

color ではなく bg-color での指定になるのが少々トリッキーだが確かにいけた。stroke と fill を一緒にしてしまうが、二値で使いたいだけならこういうやり方もあるんだなという感じ。

image

Can I use 的にも IE, Opera 以外は OK なので OK だろう。

その他

あとは CSS 変数で渡すというのがあるが、それだと元の svg の色部分を変数にしないといけないので、これも見送った。ただ、stroke と fill をそれぞれ指定したい場合はそのへんをやっていったほうがよさそう。

追記:CSS 変数で指定できるけど、これも inline じゃないとダメなやつだった