ウェブサイトのグラフィック表現において SVG(Scalable Vector Graphics)は欠かせません。

この記事では SVG を使用するのに適したケースやメリットおよび、SVG の実装方法のパターン、SVG コードの最適化についてまとめます。

SVG はシンプルな構成で、境界線が明確な以下のタイプのグラフィックに適しています。

  • ロゴ
  • アイコンやピクトグラム
  • イラスト
  • チャート

かつては、イメージマップ(<map>)と呼ばれる、画像上の特定のエリアにリンクを付与する実装が多く見られましたが、現在は SVG で実装できます。

以下は、よくある日本地図から各地域のページにリンクを付与する機能を SVG で実装したデモです。

リンクを無効にしているため、地域を選択してもページ遷移は発生しません。

SVG の仕様は膨大なためこの記事ではとても紹介しきれませんが、グラデーション、回転、フィルタ効果、クリッピング、マスキング、アニメーションなどさまざまな機能があり、表現の可能性はいくらでも広がります。

SVG を使用するメリットとしては、以下が考えられます。

  • 拡大しても劣化しない
  • ホバー効果やダークモードに対応しやすい
  • 再利用しやすい

なお、ファイルサイズについては、次世代画像フォーマット(WebP や AVIF)と比較するとサイズが大きくなってしまう場合が多いです。

ただ、パフォーマンスの観点から考えると、SVG はインラインで表示することができるので、HTTP リクエストを減らせるというメリットも踏まえて評価するのがよいかもしれません。

拡大しても劣化しない

見出し「拡大しても劣化しない」

SVG は文字どおりスケールできるベクター形式の図形なので、いくら拡大しても品質を維持することができます。

そのため、特にウェブサイトのロゴに使用するのに適しています。

SVG 形式とビットマップ形式で書き出したロゴを拡大したときの比較の図。SVG 形式はオブジェクトの境界線がシャープだが、ビットマップ形式では全体的に品質が劣化し、オブジェクトの境界線がぼやけている
SVG 形式とビットマップ形式で書き出したロゴを拡大したときの品質の比較

ホバー効果やダークモードに対応しやすい

見出し「ホバー効果やダークモードに対応しやすい」

インライン SVG で実装する場合、CSS から fill の値を変更してカラーを変更したり、transform で一部のパーツを動かしたり変形することができます。また、<use> 要素で参照する方法でも、全体のカラー(fill)を変更することができます。

これにより、ホバー効果を付けたり、ダークモードを導入するのが容易になります。

なお、「リニューアル振り返り - CSS 編」でも言及しましたが、このとき currentColor キーワードを使用することで柔軟性が高くなります。

以下の例では currentColor によって、ボタンに指定してる color: #cfdddd が、SVG アイコンにも反映されますが、ホバーで変更したカラーもそのまま反映されます。

currentColor の使用例
---
// Astro コンポーネントの例
---

<button type="button" class="btn">
  ボタン
  <svg width="27" height="27" viewbox="0 0 27 27" class="btn-icon">
    <path d="M20.035 13.5 13.5 19.802l-1.307-1.26 4.37-4.214H6.965v-1.782h9.466l-4.238-4.087 1.307-1.26 6.535 6.301Z"/>
  </svg>
</button>

<style>
.btn {
  /* 親要素にカラーを指定 */
  color: #cfdddd;
}
.btn-icon {
  /* このとき SVG の `fill` が `#cfdddd` になる */
  fill: currentColor;
}
.btn:hover {
  background-color: #cfdddd;
  /* ホバーで変更したカラーは `currentColor` にも反映される */
  color: #042020;
}
</style>

JPEG などのビットマップ形式の場合には、一度書き出した後に調整できることは限られますが、ベクター形式の SVG であれば、書き出した後のファイルをそのままデザインツールで開いて、品質を損なうことなく調整することができます。

また、SVG はテキストベースのため、カラー変更レベルであれば、デザインツールからファイルを開かなくても、直接エディタからコードを調整することもできます。

SVG をウェブサイトで表示させるには、大別すると以下の実装方法が考えられます。

  1. インライン SVG
  2. <img>
  3. <use>
  4. 背景画像(background-image

実装方法によって、CSS や JS を使用して SVG を操作できるか、外部ファイルを参照できるかどうかが異なるので、用途や制約によって使い分けます。

実装方法CSS 操作JS 操作参照
1. インライン✅ 対応✅ 対応内部
2. <img>--外部
3. <use>一部対応-内部 / 外部
4. 背景画像--内部 / 外部

HTML に直接 SVG のコードを指定する方法で、もっとも一般的です。

インライン SVG の例
<svg width="27" height="27">
  <path d="M20.035 13.5 13.5 19.802l-1.307-1.26 4.37-4.214H6.965v-1.782h9.466l-4.238-4.087 1.307-1.26 6.535 6.301Z"/>
</svg>

CSS、JS での操作ができ、パフォーマンスも高いため、コンポーネントとして一元管理できるといった場合など、メンテナンス性に問題がなければ、まずはこの方法を採用します。

SVG 内の各要素にアニメーションをつけることもできるため、イラスト内のパーツを個別に動かしたい場合にも適しています。

クリックや Enter キーでアニメーションするインライン SVG の例

通常の画像と同じように、SVG 形式で書き出した画像ファイルを <img src="..."> で読み込む方法です。

<img> の例
<img src="icon-arrow.svg" alt="" width="27" height="27">

他の画像ファイルと同じく外部ファイルとして管理できるので、メンテナンス性は高いですが、CSS や JS で直接操作ができないため、そのようなニーズがあるときには他のアプローチを取る必要があります。

また、この方法では HTTP リクエストが発生するため、インライン SVG などと比較するとパフォーマンスコストがかかります。

<use> 要素を使用することで、ファイルの内部または外部から SVG を参照することができます。

以下は外部の SVG ファイル(icon-arrow.svg)に id 属性を付与しておき、<use> 要素で参照する方法です。

icon-arrow.svg
<!-- SVG ファイルに `id="target"` を付与 -->
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27">
  <path d="M20.035 13.5 13.5 19.802l-1.307-1.26 4.37-4.214H6.965v-1.782h9.466l-4.238-4.087 1.307-1.26 6.535 6.301Z"/>
</svg>
<use> の例
<svg width="27" height="27">
  <!-- ファイルパスの後ろにフラグメント `#target` を指定 -->
  <use href="icon-arrow.svg#target"/>
</svg>

外部の SVG ファイルで管理したいが、ホバー効果などで CSS の操作を加えたいときに使える手法です。

ただ、CSS で変更できるのは SVG 全体のスタイルになるので、パーツごとに変化を加えたい場合には、インライン SVG で実装する必要があります。

複数のアイコンを一箇所にまとめておき、使いたいアイコンを抜き出す方法として知られる SVG スプライトも、<use> 要素を使用した手法です。

以下は HTML 内に <symbol> を記述しておき、<use> で参照する例です。

ベースとなる SVG を HTML に記述
<!-- ベースとなる SVG は非表示にしておく -->
<svg aria-hidden="true" style="display: none;">
  <defs>
    <!-- アイコンごとに `<symbol>` を列挙 -->
    <symbol id="svg-icon-arrow" viewBox="0 0 27 27">
      <path d="M20.071 13.5 13 20.57v-1.414L18.157 14H7v-1h11.157L13 7.843V6.43l7.071 7.07Z"/>
    </symbol>

    <symbol id="svg-icon-arrow-bold" viewBox="0 0 27 27">
      <path d="M20.035 13.5 13.5 19.802l-1.307-1.26 4.37-4.214H6.965v-1.782h9.466l-4.238-4.087 1.307-1.26 6.535 6.301Z"/>
    </symbol>
  </defs>
</svg>
<use> で参照する
<svg width="27" height="27">
  <!-- 使用するアイコンのフラグメントを指定 -->
  <use href="#svg-icon-arrow">
</svg>

CSS の background-image で指定する方法で、HTML に SVG のコードを記述できない制約があるときに用います。

外部ファイルを参照するのが一般的ですが、Data URL で直接コードを指定することもでき、この場合には HTTP リクエストが発生しないので、パフォーマンスに優れています。

背景画像の例
/* 外部ファイルを参照する例 */
.icon-arrow {
  background-image: url('icon-arrow.svg');
}

/* Data URL で指定する例 */
.icon-arrow {
  background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27"><path d="M20.035 13.5 13.5 19.802l-1.307-1.26 4.37-4.214H6.965v-1.782h9.466l-4.238-4.087 1.307-1.26 6.535 6.301Z"/></svg>');
}

デザインデータの調整

見出し「デザインデータの調整」

SVG を画像として用いる場合には、オブジェクトの線やテキスト要素をあらかじめアウトライン化しておき、ブラウザで表示したときに見た目が変わらないようにします。

次に、Illustrator のパスファインダの「合体」「型抜き」「交差」といった機能を使用して、オブジェクトの形状をなるべくシンプルにして、コードの量を抑えます。

Figma のパスファインダに相当する機能(「Union」「Subtract」など)でも調整できますが、Illustrator を使ったほうが無駄のないシンプルなパスに仕上がります。

デザインツールで作成した同じ家の形をしたオブジェクト 2 つが横に並んだ図
デザインツールで作成したホームアイコンのオブジェクト
アウトラインモードでの最適化前の状態と最適化した状態を比較した図。最適化した状態ではパスやアンカーポイントの数が少なくなっている
アウトラインモードでの最適化前の状態(左)と最適化した状態(右)

加えて、可能であればパス上の不要なアンカーポイントを削除しておきます。

デザインツールで書き出した SVG のコードには不要なコードが含まれることがあるので、「SVGO」のようなツールで取り除くか、直接コードから削除します。

  • XML 宣言(<?xml version="1.0" encoding="utf-8"?>
  • ジェネレータのコメント(<!-- Generator: Adobe Illusrator... -->
  • <svg>versionxmlns:xlinkxml:space といった属性
  • 不要な <g> 要素
  • 不要な id 属性や data-name 属性
  • 別途 CSS でスタイルを指定するときの <style> 要素や classfill の属性
  • インライン SVG の場合には xmlns="http://www.w3.org/2000/svg" の指定も省略できます。
  • <svg>viewBox 属性や、width height 属性を省略できる場合もありますが、ケースバイケースで慎重さが求められるので、無理に省略しなくてもよいでしょう。

以下は Illustrator の「別名で保存」で書き出したコードを最適化する例ですが、ファイルサイズを 735 byte から 344 byte まで削減することができます。

Before: 735 byte
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 27 27" style="enable-background:new 0 0 27 27;" xml:space="preserve">
<style type="text/css">
  .st0{fill:#FFFFFF;}
</style>
<g>
  <rect x="13" y="17" class="st0" width="1" height="1"/>
  <rect x="13" y="11" class="st0" width="1" height="5"/>
</g>
<path class="st0" d="M21.9,19.5l-7.5-13C14.2..."/>
</svg>
After: 344 byte
<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27" viewBox="0 0 27 27">
<rect x="13" y="17" width="1" height="1"/>
<rect x="13" y="11" width="1" height="5"/>
<path d="M21.9,19.5l-7.5-13C14.2..."/>
</svg>

単体で見ると削減できるサイズは小さいですが、SVG はロゴやアイコンのようにサイト共通の要素として使用することが多いので、これらの積み重ねによってパフォーマンス改善に貢献することができます。

SVG は XML ベースの言語のため、HTML と比較すると厳格な文法が求められます。

例えば、要素の終了タグは必須で、<path> 要素などの空要素に対しても必ずスラッシュ(/)で閉じる必要があり、これらを遵守しないとエラーが発生して正しく表示されなくなります。

また、<svg> 要素内で使用できる要素も限定されているので、使用できない要素で囲うとその要素は表示されなくなります。

そのほかにも、大文字と小文字を区別する、属性値の引用符を省略できないといった構文規則があります。

直接コードを調整する際には、これらの点に注意する必要があります。

アクセシビリティの確保

見出し「アクセシビリティの確保」

SVG の暗黙のロールは role="graphics-document" です。

インライン SVG で画像として実装する場合には、アクセシビリティを確保するために role="img"<title> 要素(もしくは aria-label 属性)を指定します。

ロゴ画像の例
<svg xmlns="http://www.w3.org/2000/svg" width="174" height="30" viewbox="0 0 174 30" role="img">
  <title>grip on minds</title>
  <path d="M60.819 20.836c0..."/>
</svg>

SVGO のような圧縮ツールを使用する場合、これらの必要な指定が削除されることがありますので、適宜ツールの設定を調整する必要があります。
(SVGO のデフォルトは removeTitle: true なので <title> が削除されます)

アイコンやピクトグラムのようなシンボルには role="img" のサブクラスである role="graphics-symbol" を指定するのがより適切ですが、その判断が難しい場合もあるので、メンテナンス性や作業コストを考慮して採用するかを判断するとよいでしょう。

指定する際には、支援技術が role="graphics-symbol" を認識しないケースを考慮して、role="graphics-symbol img" とフォールバックの指定を加えるのがよいかもしれません。

graphics-symbol の例
<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27" viewbox="0 0 27 27" role="graphics-symbol img">
  <title>タイトル</title>
  <path d="M13.5 21a7.5 7.5..."/>
</svg>

そのほかにも、<svg>aria-labelledby を付与して <title> と紐づけたり、<desc> 要素で概要テキストを指定したりといった最適化が考えられますが、id はページ内で一意でなければならないので、メンテナンスコストも作業コストも高くなります。

加えて、値が正しく設定されているかを検証するコストも高くなるので、ここまで対応できるケースは限られるかもしれません。

aria-labelledby と <desc> の例
<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27" role="img" viewbox="0 0 27 27" aria-labelledby="svg-title" aria-describedby="svg-desc">
  <title id="svg-title">タイトル</title>
  <desc id="svg-desc">概要テキスト</desc>
  <path d="M13.5 21a7.5 7.5..."/>
</svg>

なお、SVG のアクセシビリティについては、以下の記事やドキュメントを参考にしました。