SVGの一部をCSSで操作してもっと効率よく使い回ししたい…そんなときは、ちょっとした発想の転換が必要です。ヒントはMediumのロゴにありました。
2016年、SVGは、ファイルサイズ、スケーラビリティ、CSSのおかげで、Webページで広く使われるようになりました。
場合によってはアイコンフォントのほうが好まれることもありますが(「Build Your Own SVG Icons」参照)、アイコンシステムとして使われることもあります(「The Great Icon Debate: Fonts Vs SVG」参照)。
それだけではありません。SVGはロゴやグラフィック要素にも使えます(少なくとも、過度に複雑なものでなければ)。また、もともと柔軟性に富んでいるので、レスポンシブなWebサイトを作るには完璧な味方になってくれます(Sara Soueidan「Making SVGs Responsive with CSS」参照)。
SVGを使えば、CSSを通じてすべての要素のサイズや色の変更ができます。しかし、SVGのコードがHTMLページに埋め込まれていない限りは、要素の一部を変更することはできません。
どこが問題なのか?
シンプルな例を見てみましょう。以下のように、いろいろな色で表示したい画像があるとします。
もちろん、従来通りに3つの画像をそれぞれ別の色で作る方法もあります。しかし、1つのSVGファイルを使ってレンダリング時にデザインしたい場合はどうでしょうか。
さらに、画像を「SVG symbol」とし、ブラウザーキャッシュを活用する方法はないでしょうか。
ここでは「スキン変更可能なSVG symbol」と呼ぶことにします。SVG画像の「骨組み」は変わりませんが、表面的な見た目なら簡単に変えられるからです。
完璧な方法は、CSSセレクター経由でsymbol要素にアクセスし、なんらかのルールを(埋め込みSVGで使っているのと同じように)を加えることです。
次のサンプルでは、各三角形にclass(top、right、bottom、left)を追加し、画像をsymbolとして配置し、CSS経由で修正を試みました。それがこちらの方法です。
.top { fill: #356BA5; }
.right { fill: #357FD9; }
/* and so on... */
残念なことに、次のCodePenの例からも分かるように、この方法は現段階でFirefoxでしか使えません。2番目の画像が青色で表示されるのはFirefoxだけです(便宜上、symbolのコードを埋め込みましたが、外部SVGファイルと同じ結果になりました)。
このように要素がたくさんある大規模なプロジェクトでは、メンテナンス性が重要です。そこで、プロジェクト全体のアセットをもっと効率的に管理する方法をいつも探すようにしています。
めざすは、純粋なSVGの解決策——前のサンプルなら、たった1つのSVGの三角形を使い、CSSで回転や移動、色付けをすること——を見つけることです。
しかし、個人的にはこの方法は気に入りませんでした。問題をただ別の場所に移しているだけで、解決していないように思えるからです。まったく同じ形のコンポーネントを持つロゴなんて、現実にどれだけあるというのでしょうか?
これについては、Sara SoueidanがCSS変数を使った賢い解決法を私よりもはるかに上手に説明しています。CSS変数がまだ実験段階の技術であり、Microsoftのブラウザーがサポートしていない点が残念なところですが……。
Mediumのロゴに見る解決策
よくあることですが、解決策はいままで気づかなかった自分が間抜けに思えるくらい簡単なものです。
数か月前、Mediumの新しいロゴを見ているときに私は思いつきました。
Mediumのロゴは、4つの「形」からできています。それぞれが別々の、グラデーションのないフラットカラーに塗りつぶされています。白黒のロゴと緑色のロゴは、色を除けばまったく同じものです。
両方のバージョンを1つのファイルに保存するには、4つの形をそれぞれsymbolにして、すべてviewBox内で作ります。
では、Mediumの例を応用し、先ほどの画像に使われているそれぞれの形のシンボルを作ります。すべての画像全体で(0 0 54 54)と同じviewBoxを使っているので、余計な説明をしなくても正しい位置に自動的に配置されます。ただ、symbolのコード内でfill、stroke、styleなどの属性を避けるように注意してください。
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="top" viewBox="0 0 54 54">
<polygon points="54 0 0 0 27 27 54 0"></polygon>
</symbol>
<symbol id="right" viewBox="0 0 54 54">
<polygon points="54 54 54 0 27 27 54 54"></polygon>
</symbol>
<symbol id="bottom" viewBox="0 0 54 54">
<polygon points="0 54 54 54 27 27 0 54"></polygon>
</symbol>
<symbol id="left" viewBox="0 0 54 54">
<polygon points="0 0 0 54 27 27 0 0"></polygon>
</symbol>
</svg>
これによって、1つのSVGコンテナに集められるようになります。
<svg>
<use class="top" xlink:href="#top"></use>
<use class="right" xlink:href="#right"></use>
<use class="bottom" xlink:href="#bottom"></use>
<use class="left" xlink:href="#left"></use>
</svg>
各use要素は、お好みでスタイリングできます。また、もっとも大事なことですが、各use要素は最近のすべてのブラウザーとの互換性があります。
この方法でSVGファイルを配置すれば完了です。もちろん手動でもできますが、たくさんのグラフィックを管理したり、パパッと編集してより多くのプロジェクトで再利用しなければならない場合、もっと賢くてスピーディーなワークフローが必要になります。私自身はAdobe Illustrator、そしてGulpを少し使いながら解決策を見つけました。
SVGシンボル作りのワークフロー
テクニックの基本は、「Build Your Own SVG Icons」「Create an Icon Font Using Illustrator & IcoMoon」で以前、取り上げたものと同じですので、この2つの記事をチェックしてください。
まず、下のような2つの例を用意しました。それぞれ別のアートボードに配置されています。
色の塗りつぶしはCSSで編集できることは分かっていますが(ストロークやストロークサイズなどについても同様)、便宜上、画像に色を付けてあります。
symbolそれぞれに独自のアートボードが必要なので、色付きの部分が増えるたびに各画像のアートボードを分割して作らなければなりません。
Illustratorを使えば、各要素を切り取り、ターゲットアートボードを選択して同じ位置にペーストするまで、とてもスピーディーに作業できます。
それぞれのアートボードに別々の名前がつけられていることに注目してください。のちにsymbolのIDに使われるものです。
ここで、「ファイル」→「書き出し」→「スクリーン用に書き出し」という新しいコマンドを使って、アートボードをSVGに書き出します。
Illustratorの最新バージョンでは、各アートボードまたはユーザー定義のアセットを、たった1つのコマンドを使うだけでいろいろなフォーマットで保存できるので、とても便利です。
書き出しパネルから「アートボード」を選択し、書き出し先フォルダーを指定し、書き出しフォーマットを「SVG」に設定します。
アートボードはそれぞれ1つのSVGファイルとして書き出されます。
すべてのファイルをSVG symbolとしてまとめ、不要なSVG属性を削除しなければなりません。ここではGulpのスクリプトを少し使うだけで、スムーズかつスピーディーに作業を進められます。
Gulpでsymbolを一気に整える
ここからは少しばかり技術的は話になりますが、やる気さえあれば、汎用性の高いSVGを素早くクリーンに作れます。
GulpについてはすでにSitePointで記事を書いています。また、Gulpのインストール方法や関連情報についてはWebにたくさんの情報があります。そのため、ここではすでにGulpをインストールしていて、Gulpについても分かっているものとして話を進めていきます。
とはいえ、Gulpがあまり好みでないという場合、次に紹介するステップを手動で実行することもできます。私自身もGulpを使い始める前は手動で操作していました。なぜなら、学習するには間違いなく素晴らしい方法であり、また小規模なプロジェクトや常に編集やメンテナンスをする必要のないプロジェクトでは手動で十分すぎるほどだからです。
それぞれが下の例のように配置されたSVGファイルをいくつか用意しました(便宜上、d属性は短縮してあります)。
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 54 54">
<title>umbrella-handle</title>
<path d="..." fill="#603813"></path>
</svg>
目的は、不要な属性を省き、すべての画像を独自ファイルのSVG symbolとして配置することです。
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="umbrella_handle" viewBox="0 0 54 54"> ... </symbol>
<symbol id="umbrella_top" viewBox="0 0 54 54"> ... </symbol>
<!-- and so on ... -->
</svg>
Gulp上の作業以外にも、いろいろな拡張が必要です。
- gulp-svgstoreとgulp-svgminを結合させてSVGファイルを最小化する
- gulp-renameでid名を変え、保存先ファイルに特定の名前を付ける。以前のIllustratorのSVG書き出しコマンドを使う場合は特に必要なモジュールなので、あとで説明する
これで、Gulpファイルを配置できるようになります(コードは公開Gistとしても使えます)。
var gulp = require('gulp')
,rename = require('gulp-rename')
,svgstore = require('gulp-svgstore')
,svgmin = require('gulp-svgmin')
;
gulp.task('default', function() {
gulp.src(['svg_files/*.svg'])
.pipe(rename(function (path) {
path.basename = path.basename.replace(/__icon_prefix__/, '');
return path;
}))
.pipe(svgmin(function (file) {
return {
// https://github.com/svg/svgo/tree/master/plugins
plugins: [
{ cleanupIDs: { remove: true, minify: true } }
, { removeDoctype: true }
, { removeComments: true }
, { removeStyleElement: true }
, { removeDimensions: true }
, { cleanupNumericValues: { floatPrecision: 2 } }
, { removeAttrs: { attrs: ['(fill|stroke|class|style)', 'svg:(width|height)'] } }
]
//,js2svg: { pretty: true } // uncomment for readability
};
}))
.pipe(svgstore())
.pipe( rename('my-icons.svg') )
.pipe(gulp.dest('./'));
});
モジュールの搭載が終わったら、構文解析したいファイルを指示します (svg_files/*.svg)。
SVGstoreは各ファイル名を使ってsymbolのid属性を設定しています(例:ファイル名がumbrella.svgの場合、id="umbrella"というsymbolになる)。ファイル名はアートボードとまったく同じものになるので、新しいバージョンのIllustratorで「スクリーン用に書き出し」を使用する場合、最初の「rename」コマンドは省けます。
しかし、もっと古いバージョンのIllustratorの場合、Illustratorのファイル名をアートボード名と連結させることによってファイル名を生成しています。そのため、Illustratorのファイル名の接頭辞を除去し、ファイル名を変更しなければなりません。
path.basename = path.basename.replace(/__icon_prefix__/, '')
これでファイルをきれいにできます。gulp-svgminはSVGOのGulpバージョンで、「SVGベクター・グラフィックファイルのNodejsをベースとした最適化ツール」です(Jake Archibaldが、ファイルを手動で管理したいときに本当に使えるSVGOオンライン版をリリースしています)。
SVGOにはたくさんの構造化可能なオプションがありますが(プロジェクトページからすべてチェックできます)、必要なのはほんの数点だけです(もちろんスクリプトを好みに合わせてカスタマイズできます)。
- cleanupIDs:ファイルからすべてのIDを削除する
- removeDoctype、removeComments、removeStyleElement:doctypeやコメント、要素をすべて削除する
- removeDimensions:viewboxが表示されているとき、widthとheightsの属性すべてを削除する
- cleanupNumericValues:数値を正確に四捨五入計算する
- removeAttrs:特定の属性をすべて削除する
次に、ファイルがsvgstoreに移動され、独自ファイルにまとめられます。そのあと名前が変更され、保存されます。
これを何回も繰り返せば、各プロジェクトあたり数分で配置したり、必要な時にSVG symbolを素早く作ったりできるようになります。
これがその結果例です(このCodePenにも便宜上SVGファイルを埋め込んでいますが、外部ファイルとして安全にリンクできます)。
注意点は?
svg4everybodyで分かるように、この方法はuse要素のスタイリングに依存しているので、polyfillによって要素が削除されると問題が発生します。
外部symbolリンクをサポートしてないブラウザー(IEのすべてのバージョン)では、svg4everybodyによってすべてのuse要素が一致するsymbolコンテンツに書き換えられてしまいます。そのため、useに適用されるCSSルールはすべて効かなくなってしまうのです。
これはCSSセレクターを内部記号要素(path、circleなど)に適用することによって解決できますが、少々大変かもしれません。
おまけ
このワークフローには無数のパターンがあり、ストロークやテキストなどいろいろ操作できます。
ほかにも、Illustratorのシンボルもおもしろい機能なので、ぜひ探ってみたいところです。SVG symbolとして書き出しできて、たくさんの可能性を秘めています。
(原文:‘Reskinnable’ SVG Symbols: How to Make Them (..and Why))
[翻訳:加藤由佳/編集:Livit]