こんにちは!
意匠部のおばらです。
今回はファビコンに関する記事です。
「チャレンジ石垣島」ウェブサイトではファビコンを OS やブラウザの外観モード(ライトモード/ダークモード)に応じて(一部の環境を除き)切り替えています。
今回はそのロジックをご紹介します。
(ロゴの制作秘話に関しては前回の記事を御覧ください)
目次
- 目次
- ファビコンを設定する
- ライトモード向けのファビコンがダークモードで見づらい
- メディアクエリでファビコンの見た目を切り替える
- 方法 ①「SVG 内に記述した CSS で切り替える」
- 方法 ② 「link タグの meadia 属性で切り替える」
- 方法 ③ 「JavaScript で切り替える」
- まとめ
- 参考文献
ファビコンを設定する
なにはともあれまずはファビコンを設定してみましょう。
<link rel="icon" href="favicon.png" />
PNG をファビコンにできるなんて。。良い時代になったものです。Adobe Photoshop にプラグインを入れて ICO ファイルを書き出していた頃が懐かしいです。
はい、これでおしまい!
・
・
・
と思いきや。。
ライトモード向けのファビコンがダークモードで見づらい
大変です!!ダークモードで見づらい!
最近の OS では外観モード(ライトモード/ダークモード)が選べてしまうのでした。ユーザによってファビコンの背景色が明るかったり暗かったり。。難儀な時代になったものです。とはいえ私も何を隠そうダークモード派。職業柄いつも酷使している目を少しでもいたわりたい。。文句は言えません。
というわけで外観モード(ライトモード/ダークモード)でファビコンを出し分けてみることにします。
メディアクエリでファビコンの見た目を切り替える
本記事でご紹介する方法はいずれもメディアクエリ(のメディア特性) prefers-color-scheme
を利用します。
メディアクエリ prefers-color-scheme
でユーザが選択した外観モード(ライトモード/ダークモード)に応じ処理を分岐できます。prefers-color-scheme
に指定できる値は以下の 2 つです。
light
dark
注)no-preference
は廃止されました
CSS でメディアクエリ prefers-color-scheme
を用い処理を分岐するロジックは例えば以下です。
セレクタ { ライトモード時(or 特にモードが指定されていない場合)の記述 } @media (prefers-color-scheme: dark) { セレクタ { ダークモードの時の記述 } }
メディアクエリ prefers-color-scheme
をどこでどう使うかによってアプローチが異なります。本記事では私が試した 3 つのアプローチをご紹介します。
方法 ①「SVG 内に記述した CSS で切り替える」
SVG をファビコンにし、その SVG 内に<style>
タグで CSS を記述する方法です。なお一部ブラウザは SVG のファビコンに対応していません。とはいえ SVG もファビコンにできるなんて。。良い時代になったものです。
対応状況は以下を御覧ください。
SVG を作成する
任意のツールで SVG を作成します。今回は Adobe Illsutrator でロゴを作成しているため Illustrator を使用しました。
書き出す際 <svg>
タグの width
と height
属性が省略されていると表示崩れを起こす環境があるため注意してください。気まぐれで WebGL で SVG をテクスチャに使ってみようと思い試したところ Safari で width
と height
属性が省略されている SVG を正しく表示できませんでした。他にも様々な環境・状況で表示崩れが発生します。
ちなみに Illustrator では SVG を書き出す際に「レスポンシブ(Responsive)」のチェックを入れると width
と height
属性が省略されます。
SVG をテキストエディタで開く
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <path d="M25.55821,28.8322a21.54656,..." /> <path d="M16,1.88235A14.11561,..." /> <path d="M29.30388,20.73146a14.12322,..." /> <path d="M6.58824,8.47059c-.07681,..." style="fill:#26bfbf" /> <path d="M24.91254,10.25761A29.04449,..." style="fill:#ff4d4d" /> <path d="M18.20327,22.00412q.152.96771.37326,..." style="fill:#e0b550" /> </svg>
VS Code など任意のテキストエディタで SVG を開きます。SVG をプレビューするプラグインを入れておくと便利です。
<style> タグで CSS を追加する
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <style> .main { fill: #fff; } @media (prefers-color-scheme: dark) { .main { fill: #000; } } </style> <path class="main" d="M25.55821,28.8322a21.54656,..." /> <path class="main" d="M16,1.88235A14.11561,..." /> <path class="main" d="M29.30388,20.73146a14.12322,..." /> <path d="M6.58824,8.47059c-.07681,..." style="fill:#26bfbf" /> <path d="M24.91254,10.25761A29.04449,..." style="fill:#ff4d4d" /> <path d="M18.20327,22.00412q.152.96771.37326,..." style="fill:#e0b550" /> </svg>
SVG 内に追加した<style>
タグに、メディアクエリで条件分岐する CSS を記述しました。main
というクラス名が付与された <path>
タグの fill
属性の値を
- ライトモードでは白
#fff
- ダークモードでは黒
#000
としています。
SVG をファビコンに設定する
早速設定してみましょう。
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link>
タグにsizes="any"
を指定している例も見られますが、2023年2月現在対応している環境がないため、sizes="any"
は省いています。
これで(対応している環境では)ライトモード/ダークモードで色が切り替わります。
ブラウザごとの比較
環境依存があるため手元の環境で検証しました。
- ページ読み込み時に切り替わるか?(リロードすると切り替わるか?)
- ページ表示完了後も動的に切り替わるか?(OS の外観モードの変更と同期し、リロードしなくても動的に切り替わるか?)
注)Edge は Mac 用の Edge です
Mac | Firefoxv110.0 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|---|
Chromev109.0.5414.87 | ページ読み込み時のみ切り替わる | |
Edgev110.0.1587.50 | ||
Safariv15.6.1 | SVG ファビコン非対応 |
参考までに<body>
内に <img>
タグで配置した場合の挙動も併せて検証しました。
Mac | Chromev109.0.5414.87 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|---|
Edgev110.0.1587.50 | ||
Firefoxv110.0 | ||
Safariv15.6.1 | いずれも切り替わらない |
うーん。。 Mac だけを見てもブラウザによって挙動が異なります。。
<image>
タグでの画像埋め込み
チャレンジのロゴはベクタデータのみで表現できるため CSS で見た目を簡単に変えることが簡単でした。
しかしベクタデータでは表現できない(ラスタデータでしか表現できない)デザインのファビコンもあります。
試しに <path>
タグの代わりに <image>
タグを用い SVG 内に画像を埋め込んでみましょう。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> <style> .light { display: block; } .dark { display: none; } @media (prefers-color-scheme: dark) { .light { display: none; } .dark { display: block; } } </style> <image class="light" width="32" height="32" href="data:image/png;..." /> <image class="dark" width="32" height="32" href="data:image/png;..." /> </svg>
SVG を(<svg>
タグを <body>
内にインラインで置くのではなく)<img>
タグの src
属性や <link>
タグの href
属性を経由して SVG を画像として読み込む場合、セキュリティ上の理由から <image>
タグの href
属性で外部(別ファイル)の画像を読み込むことが出来ません。よってその制限を回避するため Data URI で埋め込みます。
developer.mozilla.org developer.mozilla.org
ブラウザごとの比較
Mac | Firefoxv110.0 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|---|
Chromev109.0.5414.87 | ページ読み込み時のみ切り替わる | |
Edgev110.0.1587.50 | ||
Safariv15.6.1 | SVG ファビコン非対応 |
Mac | Chromev109.0.5414.87 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|---|
Firefoxv110.0 | ||
Edgev110.0.1587.50 | ||
Safariv15.6.1 | いずれも切り替わらない |
こちらもブラウザによって挙動が異なります。。
方法 ② 「link タグの meadia 属性で切り替える」
ファビコンを指定している<link>
タグのmedia
属性にメディアクエリを記述することも出来ます。
<link rel="icon" href="favicon-light.png" media="(prefers-color-scheme: light)" /> <link rel="icon" href="favicon-dark.png" media="(prefers-color-scheme: dark)" />
この方法なら ファビコンは PNG でも SVG でも何でも良いですね! そして(SVG 内に記述した CSS メディアクエリでは動的に切り替わらなかった)Chrome と Edge でも動的に切り替わります。。やった!!
と思ったら今度は Firefox で切り替わりません。。
ブラウザごとの比較
Mac | Chromev109.0.5414.87 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|---|
Edgev110.0.1587.50 | ||
Firefoxv110.0 | そもそも切り替わらない | |
Safariv15.6.1 | そもそも切り替わらない |
こちらも Mac だけを見てもブラウザによって挙動が異なります。。
方法 ③ 「JavaScript で切り替える」
JavaScript でライトモード/ダークモードを判別し、<link>
タグの href
属性を書き換えてしまいましょう。
ビルトイン関数 matchMedia()
を使用します。
const link = document.querySelector('link[rel="icon"]'); // 何らかの方法で DOM 要素を取得 const mediaQueryList = matchMedia('(prefers-color-scheme:dark)'); const handleChange = () => { if (mediaQueryList.matches) { link.setAttribute('href', 'favicon-dark.png'); console.log('ダークモードだよ!'); } else { link.setAttribute('href', 'favicon-light.png'); console.log('ダークモードじゃないよ!'); } }; mediaQueryList.addEventListener('change', handleChange); handleChange();
注 1)古い実装では addEventListener
ではなく addListener
となる場合があるので注意してください。引数も異なります
注 2)setAttribute('href', '...')
を通して設定するのではなく href
属性に直接代入してもよいかと思います。ただ取得の際は getAttribute('href')
で取得する場合と href
を直接参照する場合とで結果が異なるので注意してください。後者はパス解決後の絶対パスになります。昔 IE6 と IE7 で getAttribute('href')
でも絶対パスになってしまうバグがあったような。。やっぱり良い時代になったものです。
ブラウザごとの比較
Mac | Chromev109.0.5414.87 | ページ読み込み時に切り替わる ページ表示完了後も動的に切り替わる |
---|---|
Firefoxv110.0 | |
Edgev110.0.1587.50 | |
Safariv15.6.1 | いずれも切り替わらない (JSでモードの変更は検知できるが href の書き換えが反映されない) |
相変わらずの Safari 。。
しかし、この方法が現状一番広い環境に対応できそうです。 「チャレンジ石垣島」ウェブサイトでもこの方法を採用しています。
ちなみに、2023年2月現在 GitHub も同じ方法でファビコンを切り替えていますね!
<!-- ライトモード時 --> <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg">
<!-- ダークモード時 --> <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon-dark.svg">
まとめ
長い戦いでした。。
OS やブラウザの外観モード(ライトモード/ダークモード)を判別し、ファビコンを切り替える方法をいくつかご紹介しました。しかし環境依存が激しいです。またライトモードだからファビコンの背景色が明るいとも限らないですし、ダークモードだからといって暗いとも限りません。
そしてブラウザにはプライベートモード(Incognito Mode)があります。
JavaScript で localStorage
や indexedDB
などにアクセスしてみて検知するか。。とも考えましたが、そもそもプライベートモードで UI が暗くなるかどうかもブラウザ依存。。
ほんと難儀な時代になったものです。
参考文献
- The External Resource Link element | MDN
- HTML attribute: rel | MDN
- Using media queries | MDN
- prefers-color-scheme | MDN
- <image> | MDN
- SVG as an Image | MDN
- Window.matchMedia() | MDN
- MediaQueryList | MDN
- Favicon That Works For Light And Dark Mode | JOY OF CODE
- Detecting if a browser is using Private Browsing mode | Stack Overflow