最適化と注意点
SVG ファイルサイズの圧縮、アクセシビリティ、CSS 連携、セキュリティ、 アイコン管理、パフォーマンスまで運用観点。
SVG の最適化
1. SVGO / SVGOMG
- 不要な空白 / コメント削除
- 小数の桁丸め
- 不要な属性 / メタデータ削除
- path の最適化
npm install -g svgo
svgo input.svg -o output.svg
svgo --pretty -i ./icons -o ./optimized
ブラウザ版: SVGOMG
2. 不要 ID / class を消す
Figma / Illustrator から書き出すと id="Layer_1" などが大量に。SVGO で自動削除。
3. viewBox を正規化
24x24 や 16x16 などキレイな数字に揃える(アイコン)。
4. fill/stroke を継承
fill="currentColor" にすると CSS で色変えできる。
サイズ感の目安
| 用途 | 典型サイズ |
|---|---|
| アイコン | 0.3〜2 KB |
| ロゴ | 1〜5 KB |
| イラスト | 5〜30 KB |
| これ以上 | PNG / WebP / 別フォーマット検討 |
アクセシビリティ
装飾的 SVG
<svg aria-hidden="true" focusable="false">...</svg>
意味のある SVG
<svg role="img" aria-labelledby="title desc">
<title id="title">Bar chart</title>
<desc id="desc">月別売上の推移</desc>
...
</svg>
img として使う SVG
<img src="logo.svg" alt="Acme Inc" />
インタラクティブ SVG
- クリック可能な要素にはrole="button"とtabindex="0"
- キーボード操作(Enter / Space)の対応
- fillin / outline で focus 可視化
セキュリティ
- 外部 SVG は信用しない — <script> が動く可能性
- ユーザがアップロードした SVG はサニタイズ(DOMPurify)
<img src="...">経由なら script は実行されない(安全寄り)<use href="...">も外部参照に注意
import DOMPurify from "dompurify"
const clean = DOMPurify.sanitize(userSvg, { USE_PROFILES: { svg: true, svgFilters: true } })
CSS との連携
- fill / stroke / opacity は CSS で書ける
- アニメは CSS の方が GPU 加速で速い
currentColorでテーマ連動- data-* で状態管理(
data-state="open"等)
アイコン管理
1. インライン JSX(React)
- vite-plugin-svgr / @svgr/webpack で
import Icon from "./icon.svg?react" - props で fill / size を渡せる
- tree-shaking で未使用は削れる
2. SVG スプライト
<svg width="0" height="0" style="position:absolute">
<defs>
<symbol id="home" viewBox="0 0 24 24">...</symbol>
<symbol id="user" viewBox="0 0 24 24">...</symbol>
</defs>
</svg>
<svg width="24" height="24"><use href="#home" /></svg>
3. アイコンライブラリ
- Lucide / Heroicons / Tabler / Phosphor
- Iconify — 200+ アイコンセットを 1 API で
- 使う分だけ tree-shake / オンデマンド
レスポンシブ SVG
方法 1: width / height を 100% に
<svg viewBox="0 0 800 400" style="width: 100%; height: auto">...</svg>
方法 2: aspect-ratio
.svg-wrap { aspect-ratio: 2 / 1; width: 100%; }
パフォーマンス
- 要素数が1000 を超えると DOM 更新が重い
- 画面外は表示しない(仮想スクロール / display:none)
- アニメは CSS transform / opacity に
- filter / mask は重いので限定的に
- 大量描画は Canvas / WebGL に切替
サニタイズ vs use の罠
<use href="https://other.com/x.svg#foo"> はクロスオリジンで弾かれる場合あり。
スプライトは同一オリジンに置く。
SVG vs Lottie の使い分け
| 選択 | 条件 |
|---|---|
| SVG(CSS / JS) | シンプルなアニメ、UI |
| Lottie | After Effects 由来、複雑な演出 |
| Rive | 状態遷移、ゲーム的 |
| 動画 (MP4 / WebM) | 実写素材 |
| Canvas / WebGL | 大量要素・パーティクル |
ダークモード対応
<svg fill="currentColor" stroke="currentColor">...</svg>
<style>
body { color: #111; background: #fff; }
@media (prefers-color-scheme: dark) {
body { color: #eee; background: #111; }
}
</style>
SVG を使うべきでない時
- 写真(→ JPEG / WebP / AVIF)
- 非常に複雑なグラデで小さい変化(→ ラスター)
- 10,000 要素級のシーン(→ Canvas / WebGL)
- 動画的な長い時系列素材(→ MP4)
失敗パターン
| 症状 | 対処 |
|---|---|
| ファイルが大きい | SVGO / 不要 metadata 削除 |
| 色が変えられない | currentColor / インライン化 |
| img で SMIL が動かない | 仕様。インライン or object |
| 外部 use がブロック | 同一オリジン化 / CORS 設定 |
| ロード後の表示が崩れる | preserveAspectRatio / 明示サイズ |
| SVG アップロードで XSS | DOMPurify でサニタイズ |
関連リソース
本セクションの総まとめ
- viewBox / preserveAspectRatio で座標系を理解
- 基本図形 + path で何でも描ける
- fill / stroke / グラデ / フィルタ で見た目を制御
- transform で位置と回転
- text / textPath でテキストも自由に
- CSS / JS でアニメーション
- SVGO で最適化、A11y も忘れずに
最後に
SVG は「画像」と「コード」の中間。コードとして扱うほど可能性が広がる。 まずアイコンを 1 個自作してみるところから始めると感覚が掴める。