🧊

SVGOのv3は、デフォルトで`viewBox`を削除してしまう

とあるサイトで、「狭い画面で表示するとSVG画像のせいで横スクロールバーが出てしまう」というよくあるアレがあって。

すぐ直せると思ってたら、意外と根が深かったという話。

あらすじ

そのサイトではVitePressを使ってて、このようにSVGが利用されてた。

<script setup>
import MySVG from "./path/to/my.svg?component";
</script>

<div class="Container">
  <MySVG />
</div>

セオリー通りなら、svg要素にwidth: 100%; height: auto;するだけで終わりなはず。

だがそうしてみても、意図通りに表示されない。スクロールバーは消えたけど、中身が見切れてる。なんで?というところから調査スタート。

?componentとは

まず、?componentって何やねん!っていうのが最初に思ったこと。

どうやらこれは、vite-svg-loaderというVue用のViteのプラグインが暗躍してるものだった。

jpkleemans/vite-svg-loader: Vite plugin to load SVG files as Vue components https://github.com/jpkleemans/vite-svg-loader

このプラグインを使うと、SVGをそのままコンポーネントとして展開できつつ、SVGOで最適化もあわせて行われるとのこと。

<script setup>
import MySVG from "./path/to/my.svg?raw";
</script>

<div class="Container">
  <div v-html="MySVG"></div>
</div>

つまりこれに似た雰囲気のことができつつ、各種Props(といってもclassとかstyleとかrefとか?)を流し込めるって感じ。 (ちなみに、この?rawはVite本体の機能)

犯人はSVGO

で、このとき内部的にSVGOを使ってSVGの最適化をやるらしいが、そこでなんとviewBoxが消されてた。

なのでその結果、SVGの枠の中の表示だけが見切れるという結果になってたというわけ。

仕組みとしては単純明快ではあるものの、これはどうやらかなり根が深い問題だったらしく・・。

Remove the removeViewBox plugin from the default plugins list by JohnAlbin · Pull Request #1461 · svg/svgo https://github.com/svg/svgo/pull/1461

なんとこのPRは2021年のもの!

そこから時が流れ・・・2024年になってやっとマージされたけど、メジャーバージョンで反映するわってことで未だにRCなステータスという。

Release v4.0.0-rc.0 · svg/svgo https://github.com/svg/svgo/releases/tag/v4.0.0-rc.0

ワークアラウンド

まず考えたは、先述のとおりv-htmlでお茶を濁す方法。 SVGOで最適化したい内容にもよるとは思うけど、事前にSVGを用意する時点で最適化しておけばいい。

もしくは、SVGOのオプションを上書きする方法。

svgLoader({
  svgoConfig: {
    plugins: [
      {
        name: "preset-default",
        params: {
          overrides: {
            removeViewBox: false,
          },
        },
      },
    ],
  }
})

いっそのこと使わないという手もあるか。

svgLoader({
  svgo: false
})

まあ、個人的にはこの手のプラグイン自体あまり好きじゃないし、なくても特に困ることはなさそうだったので、v-htmlで済ませた。

余計なレイヤーは無いにこしたことはない。