完璧なロゴをアップロードし、OSをダークモードに切り替えてサイトを開いてみると、明るいオレンジ色のブランドマークが暗いタブバーからぎらぎらと目に飛び込んでくる——そんな経験はないでしょうか。フラットなPNG faviconはシステムテーマを認識できません。しかし、prefers-color-schemeを内蔵したSVG faviconであれば話は別です。

本ガイドで取り上げるのは、.svgファイル内にCSSのmedia queryを直接埋め込み、単一ファイルで自動的に配色を切り替えるという一つのテクニックです。HTML側で<link>タグを2つ使い分ける方法とは別のアプローチであり、多くのサイトにとっては最もクリーンな選択肢になります。

前提条件

作業を始める前に、次の3つを用意してください。

SVGをまだお持ちでない場合は、デザインソフトから書き出すか、Mzuのfavicondlを使って任意の画像からベクターを取得してください。

ステップ・バイ・ステップ:SVG favicon内にmedia queryを追加する

ステップ1:SVGファイルをテキストエディタで開く

SVGは単なるXMLです。.svgファイルを右クリックし、「プログラムから開く → TextEdit または VS Code」を選んでください。次のような内容が表示されます。

<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
  <rect width='64' height='64' rx='12' fill='#ff7a00'/>
  <path d='M20 22 L32 44 L44 22' stroke='#fff' stroke-width='6' fill='none'/>
</svg>

ステップ2:media queryを含む<style>ブロックを追加する

開始タグ<svg>の直後に<style>要素を挿入します。その中でライトテーマ用の配色を定義し、prefers-color-scheme: darkを使ってダークモード時の上書きを指定します。

<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
  <style>
    .bg { fill: #ff7a00; }
    .stroke { stroke: #ffffff; }
    @media (prefers-color-scheme: dark) {
      .bg { fill: #1a1a1a; }
      .stroke { stroke: #ffb347; }
    }
  </style>
  <rect class='bg' width='64' height='64' rx='12'/>
  <path class='stroke' d='M20 22 L32 44 L44 22' stroke-width='6' fill='none'/>
</svg>

ブラウザはOSのテーマが切り替わるたびにprefers-color-schemeを再評価します。多くのモダンブラウザでは、リロードなしでfaviconが更新されます。

ステップ3:HTMLの<head>からリンクする

WHATWG HTML Living Standardによれば、rel='icon'リンクタイプはtype='image/svg+xml'で正しいMIMEタイプを宣言すればSVGをサポートします。

<link rel='icon' href='/favicon.svg' type='image/svg+xml'>

このタグはPNGやICOのフォールバックより上に配置してください。Apple Touchやweb manifestを含む最新のスタック全体については、favicon HTMLチートシートを参照ください。

ステップ4:ラスターのフォールバックを用意する

すべてのブラウザがfavicon内のCSSを解釈できるわけではありません。特に古いバージョンのSafariや一部の組み込みWebビューはその対象となります。そうしたクライアント向けに32×32のPNG(またはマルチサイズのICO)を用意してください。

<link rel='icon' href='/favicon.svg' type='image/svg+xml'>
<link rel='icon' href='/favicon.ico' sizes='32x32'>

ブラウザは最初にマッチしたものを使うため、SVGが使える環境ではSVGが優先されます。

ステップ5:両カラースキームでテストする

ハードリロード(Ctrl/Cmd + Shift + R)を実行し、OSのテーマを切り替えてください。Chrome、Edge、Firefoxでは、タブアイコンがライブで切り替わるはずです。何も変わらない場合は、以下の落とし穴を確認してください。

よくある落とし穴

落とし穴1:viewBoxを忘れる

viewBoxがないと、SVGは本来のサイズで描画され、巨大サイズまたはゼロサイズになることがあります。必ずviewBox='0 0 64 64'のように宣言してください。

落とし穴2:インラインのfill属性がCSSを上書きしてしまう

図形にすでにfill='#ff7a00'が属性として設定されていると、CSSクラスがspecificityの勝負で負けてしまいます。配色はクラスに移すか、インラインの属性を削除してください。

落とし穴3:faviconのキャッシュが強すぎる

ブラウザは他のファイル以上にfaviconを積極的にキャッシュします。SVGを編集した後は、テスト時にクエリ文字列を付けて読み込んでください(例:/favicon.svg?v=2)。本番環境での戦略については、キャッシュバスティングのガイドを参照ください。

落とし穴4:Safariのピン留めタブはmedia queryを無視する

Safariのピン留めタブ機能は、別のモノクロ用safari-pinned-tab.svgを使います。このファイル内のmedia queryは効きません。単色のシルエットとしてデザインし、<style>ブロックは省いてください。

落とし穴5:ダークモードは「色の反転」ではない

ダークモード用の配色は、ブラウザの実際のタブ背景(Chromeでは多くの場合#2b2a33、Safariではニアブラック)に対して十分なコントラストを持つものを採用してください。アイコンをコントラストチェッカーに一度通しておくと、後々のサポート対応の手間を減らせます。

まとめ

SVGのmedia query方式は、1ファイル・2テーマ・JavaScript不要というクリーンな構成を実現します。すでにSVGロゴを持っているモダンなサイトにとってのデフォルトとして最適であり、旧ブラウザ向けに用意すべきPNGやICOのフォールバックとも(対立するのではなく)共存して動作します。

仕様上の要件を確認する

HTML Standardでは、現在のページを表すアイコンのリンク関係としてrel='icon'が定義されています。