URLエンコードを利用したData URIなSVGでSassのインターポレーションを利用する

Base 64ではなくURLエンコードを利用したData URIなSVGは記号類以外は普通のテキスト。なので簡単なものならばSassのインターポレーション機能(#{$foo})を使いダイナミックにデータを弄ることができる。一年半前くらいにヨモツネットで書かれてたグラデーション・ミックスインとかで使われている。それのもっと単純で即戦力な利用例。

例えばSVGのfillで使う色をSassで定義したカラースキームに従ったものに変えるようにできる。通常はカラースキームからカラーコードをコピーし、それを使ってSVGを編集し保存、更にData URIに変換した後でSassにペースト、という手順を踏むことになるが、その手順のほとんどが必要なくなる。

$color: #369;
$image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22112%22%20height%3D%22112%22%3E%3Cpath%20d%3D%22M63.314%2022.059l11.314%2011.314-22.627%2022.627%2022.627%2022.627-11.314%2011.314-33.941-33.941z%22%20fill%3D%22#{$color}%22%2F%3E%3C%2Fsvg%3E")

.test {
  background-image: $image;
}

とすると、

.test {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22112%22%20height%3D%22112%22%3E%3Cpath%20d%3D%22M63.314%2022.059l11.314%2011.314-22.627%2022.627%2022.627%2022.627-11.314%2011.314-33.941-33.941z%22%20fill%3D%22#369%22%2F%3E%3C%2Fsvg%3E")
}

とCSSが出力され、ちゃんとfill#369に設定される。#がURLエンコードされないけど、それに対応するのは非常に面倒くさい。URLエンコードするCustom Functionを作るか、そうでないなら#抜きと有りの変数を作るか。いずれにせよ導入かメンテナンスが面倒になるのでこのままの方が良さそう。


今はSVGの編集からData URIに変換して埋め込むまでを完全に自動化する手軽な手段がそこそこある。しかしカラースキームの管理という面でも作業の一元化という面でも、Sass内で完結するこの手法の方がローコストかつ効率的なんじゃないかなーと使い始めた

SVGで使えるstyle要素を単なる文字列として定義しておけば、もっと複雑なことも可能ではありそう。ただ、URLエンコードしなくてはならない文字との兼ね合いがあるので、実際にはこのような色の変更くらいにしか使えない気もする。しかしそれだけでも十二分に有用性があるので、このSassのインターポレーションの利用を考慮に入れてData URIなSVGはURLエンコードで埋め込むというのは良い選択なのではないか。

追記

色を扱う時に#がURLエンコードされない問題は、Sass 3.3で入るstr-slice()関数でどうにか出来そうかなーと思ったら出来た。そのまま色は型の都合で投げられず、色から文字列への型変換は特に用意されていないけど、空文字足せば文字列になる。

%23#{str-slice(""+$color, 2)}

#をURLエンコードした%23はハード・コーディングして、str-slice()で2文字目以降を切り出しインターポレーション。ただこれはこれで色でblackとかが来ると壊れるのでまぁまぁ頑張る関数作らないと安定しなそう。