SvelteKit のルーティングを整え、レイアウトを磨き、ビルドをデプロイした。しかしブラウザのタブを開いてみると、デフォルトの Svelte ロゴが表示されている — 最悪の場合は白い空白の四角だ。この問題を解決するのは、たいてい 1 つのファイルです:app.html。
app.html は、SvelteKit がすべてのページをサーバーとクライアントの両方でレンダリングするために使う HTML シェルです。SvelteKit のプロジェクト構造ドキュメントによると、このテンプレートは src/ の直下に置かれ、すべてのルートで hydration をまたいで生き残るマークアップを注入できる唯一の場です。つまり、ファビコンの <link> タグを置いておく場所として、これ以上ないほど適しています。
なぜ app.html で、レイアウトではないのか?
+layout.svelte の <svelte:head> ブロック内にファビコンタグを追加することは可能で、ほとんどのルートでは問題なく動作します。しかし app.html は、レイアウトやコンポーネントのコードが実行される前に、最初にレンダリングされます。これには次の 2 つの実用上の利点があります。
- ファビコンのリクエストは、ブラウザが受信する最初のバイトで発生するため、タブのレンダリングが高速化されます。
+layout.svelteから注入されたタグは、JavaScript を実行しないクローラーやボットには取りこぼされる可能性があります。app.htmlは常に静的な HTML を配信します。
前提条件
必要なものは次のとおりです。
- 動作する SvelteKit プロジェクト (2.x 以降)。
static/内に用意したファビコンファイル一式。完全な構成としては、32×32 のfavicon.ico、180×180 のapple-touch-icon.png、SVG、必要に応じてsite.webmanifestを用意してください。
これらのファイルがまだない場合は、Mzu favicondl を使ってソース画像から生成できます。ワンクリックで全サイズのパッケージが出力されます。
ステップバイステップ: app.html にファビコンの link タグを追加する
1. app.html ファイルを見つける
src/app.html を開いてください。デフォルトではおおよそ次のようになっています。
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<link rel='icon' href='%sveltekit.assets%/favicon.png' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data='hover'>
<div style='display: contents'>%sveltekit.body%</div>
</body>
</html>
%sveltekit.assets% と %sveltekit.head% のプレースホルダーに注意してください。これらはビルド時に置き換えられます。このプレースホルダーは残しつつ、その上下にタグを追加できます。
2. デフォルトのファビコン行を置き換える
スターターテンプレートには favicon.png の行が 1 行だけ含まれています。これを削除し、タグ一式に置き換えます。HTML 仕様では rel='icon' がアイコンを宣言する標準的な方法であると定義されているため、この属性を使い続けてください。
<link rel='icon' href='%sveltekit.assets%/favicon.svg' type='image/svg+xml' />
<link rel='icon' href='%sveltekit.assets%/favicon.ico' sizes='32x32' />
<link rel='apple-touch-icon' href='%sveltekit.assets%/apple-touch-icon.png' sizes='180x180' />
<link rel='manifest' href='%sveltekit.assets%/site.webmanifest' />
モダンブラウザが優先的に使うよう、SVG を先頭に置きます。ICO は古いブラウザ向けのフォールバックとして機能します。Apple Touch と manifest は特定のプラットフォーム向けなので、最後に配置します。
3. ダークモード対応を追加する
SVG アイコンがダークタブで異なる見た目になるべき場合 (昨今の環境でダークテーマを使っていない人がどれほどいるでしょうか)、media ごとに 2 つのソースを宣言してください。ファビコンのダークモードガイドで扱っているのと同じ手法です。
<link rel='icon' href='%sveltekit.assets%/favicon-light.svg' type='image/svg+xml' media='(prefers-color-scheme: light)' />
<link rel='icon' href='%sveltekit.assets%/favicon-dark.svg' type='image/svg+xml' media='(prefers-color-scheme: dark)' />
ブラウザは media 属性を評価し、一致するアイコンを選択します。
4. ファイルを static/ に配置する
SvelteKit は static/ の中身をサイトのルートから配信します。タグで参照されているファイル名と完全に一致させて、各ファビコンファイルを配置してください。整理された構成は次のようになります。
static/
├── favicon.svg
├── favicon.ico
├── favicon-light.svg
├── favicon-dark.svg
├── apple-touch-icon.png
└── site.webmanifest
5. 開発サーバーを再起動する
Vite は HTML シェル aggressively にキャッシュします。app.html を編集したあとは、Ctrl+C で開発サーバーを停止し、再度 npm run dev を実行してください。これを怠ると、古いマークアップが表示され続けることがあります。
よくある落とし穴
ファビコンタグを body 内に置くこと。ブラウザはアイコン宣言のために <head> のみを参照します。ファイル名が app.html であっても、<body> の中身は無視されます。アイコンが表示されない場合は、まずこの点を確認してください。
タグと static/ のファイル名が一致していない。Linux 環境へのデプロイでは大文字小文字が区別されます。favicon.ICO と favicon.ico は別ファイルです。名前を正確に合わせてください。
デプロイ後にアイコンが古いまま。ブラウザはファビコンを aggressively にキャッシュし、ときには数週間にわたって保持します。ファイルを差し替えても表示が更新されない場合は、クエリ文字列またはコンテンツハッシュを付与したうえで再デプロイしてください。static/ 内の他の静的アセットにも同じパターンが使えます。
SVG 属性のダブルエンコード。type="image/svg+xml" のようにダブルクォートを使ったスニペットを app.html シェルにコピーすると、Vite がそれを壊してしまうことがあります。ビルドを正常に保つため、HTML 属性内ではシングルクォートを使用してください。
まとめ
app.html テンプレートは、JavaScript よりも先に実行され、すべてのページレスポンスに同梱されるため、SvelteKit でファビコンを宣言する最もクリーンな場所です。SVG + ICO + Apple Touch の完全な構成を static/ に配置し、対応する <link> タグを <head> に配線すれば、最初のペイントからすべてのブラウザタブできれいにアイコンが表示されるようになります。
SvelteKit のファビコン選択肢 (+layout.svelte アプローチや動的アイコンを含む) をより広く見たい場合は、メインの SvelteKit ファビコンガイドを参照してください。実際に必要なアイコンファイルがある場合は、Mzu favicondl がソース画像から全サイズのパッケージを数秒で生成します。