favicon が存外に難しかった。 これまで favicon.ico ファイルを 1 個作れば済むと思っていたけど、そんな単純じゃない。 Favicon Generator (link) で一式作成してもらったファイルをお手本にして、Favicon checker (link) で怒られないようになるまで直した。

自動化のために手元の make で似たものを作成するようにした。 なお、手動の操作が気にならないなら、元画像を更新する度に Favicon Generator でファイルを再作成して差し替えていけばよいと思う。

favicon の自動作成

事前に必要なファイル

以下のファイルを用意しておく。 このうち favicons/favicon.svgfavicons/safari-pinned-tab.svg はサイトや個人のセンスを反映して作成しておく。 他のファイルの中身は使い回せると思うので後掲する。

  • Makefile
    • コンテンツのルートの Makefile。
  • _includes/custom-head.html
    • HTML ファイルの <head> タグにインクルードされる。 jekyll 4.2.0 ではデフォルトのテーマが minima-2.5.1 になっていたけど、これは favicon への配慮が無い。 より新しいバージョンの minima では favicon に関するタグを custom-head.html に記述できるようになっている。 custom-head.html の元の名前は favicons.html だったみたい。
  • favicons/
    • 各 favicon を格納するディレクトリ。 最初は名前を icons/ にして試したけど、Web サーバの設定によっては icons/ のアクセスが禁止されていることがあった。
  • favicons/Makefile
    • 各 favicon を作成する Makefile。
  • favicons/favicon.svg
    • 元画像。
  • favicons/safari-pinned-tab.svg
    • 元画像を単色にしたもの。 Safari (Mac の?) でタブを固定するときに使われるらしい。
  • favicons/browserconfig.xml
    • Windows でピン留めするときに使われるらしい。
  • favicons/site.webmanifest
    • Android で使われるらしい。

作成されるファイル

コンテンツのルートで make favicons を実行すると次のファイルが作成される。 これを .gitignore に追加しておくのがよい。

apple-touch-icon.png
favicon.ico
favicons/android-chrome-192x192.png
favicons/android-chrome-512x512.png
favicons/apple-touch-icon.png
favicons/favicon-16x16.png
favicons/favicon-32x32.png
favicons/favicon.ico
favicons/mstile-144x144.png
favicons/mstile-150x150.png
favicons/mstile-310x150.png
favicons/mstile-310x310.png
favicons/mstile-70x70.png

「事前に必要なファイル」の中身

Makefile

favicon の作成は favicons/Makefile でしていて、そのうち favicon.icoapple-touch-icon.png をルートにコピーしている。 これらをルートに配置しておかないと、Favicon checker から指摘を貰うため。

.PHONY: favicons clean-favicons

FAVICON_DIR	= favicons

clean-favicons:
    (cd $(FAVICON_DIR)/ && $(MAKE) clean)
    rm -f favicon.ico apple-touch-icon.png

favicons:
    (cd $(FAVICON_DIR)/ && $(MAKE))
    $(MAKE) favicon.ico apple-touch-icon.png

favicon.ico: $(FAVICON_DIR)/favicon.ico
    cp -a $? $@

apple-touch-icon.png: $(FAVICON_DIR)/apple-touch-icon.png
    cp -a $? $@

_includes/custom-head.html

Favicon Generator が作成してくれたものをほぼそのまま使っている。

ただし、 favicon.ico の記述はコメントアウトした。 favicon.ico をルートに配置してタグの記述までしていると、Favicon checker から指摘を貰うため。

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png">
<link rel="manifest" href="/favicons/site.webmanifest">
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#5bbad5">
<!-- <link rel="shortcut icon" href="/favicon.ico"> -->
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="/favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">

favicons/Makefile

Favicon Generator が作成してくれたものを見ながら、なるべく同じサイズや余白の PNG に変換できるように調整した。 なかには favicons/browserconfig.xmlfavicons/site.webmanifest から参照してないファイルもあるけど、それらも深く考えずに Favicon Generator を真似して作っている。

ここに載せるコマンドで SVG から PNG に変換すると滲みが大きくてボヤけてしまう。 Favicon Generator はもっと綺麗な PNG にしてくれる。 ImageMagick の convert で同じようなエッジの立った PNG にする方法を見付けられなかったので、ひとまずそのままにしてしまった。 Inkscape で PNG にエクスポートしたらどうなるか、などは気になるところ。

.PHONY: favicon clean-favicon

ICON_SRC	=	favicon.svg

ICONS		= \
    favicon.ico \
    android-chrome-192x192.png \
    android-chrome-512x512.png \
    apple-touch-icon.png \
    favicon-16x16.png \
    favicon-32x32.png \
    mstile-144x144.png \
    mstile-150x150.png \
    mstile-310x150.png \
    mstile-310x310.png \
    mstile-70x70.png

all: $(ICONS)

clean:
    rm -f $(ICONS)

favicon.ico: $(ICON_SRC)
    convert -define icon:auto-resize=48,32,16 -transparent '#ffffff' -fuzz 10% $? $@
    cp -a $@ ../

android-chrome-192x192.png: $(ICON_SRC)
    convert -resize 192x192 -transparent '#ffffff' -fuzz 10% $? $@

android-chrome-512x512.png: $(ICON_SRC)
    convert -resize 512x512 -transparent '#ffffff' -fuzz 10% $? $@

apple-touch-icon.png: $(ICON_SRC)
    convert -resize 180x180 -transparent '#ffffff' -fuzz 10% $? $@

favicon-16x16.png: $(ICON_SRC)
    convert -resize 16.x16 -transparent '#ffffff' -fuzz 10% $? $@

favicon-32x32.png: $(ICON_SRC)
    convert -resize 32x32 -transparent '#ffffff' -fuzz 10% $? $@

mstile-144x144.png: $(ICON_SRC)
    convert -resize 144x144 -transparent '#ffffff' -fuzz 10% $? $@

mstile-150x150.png: $(ICON_SRC)
    convert -resize 126x126 \
        -gravity west  -splice 72x0 \
        -gravity east  -splice 72x0 \
        -gravity north -splice 0x50 \
        -gravity south -splice 0x94 \
        -transparent '#ffffff' -fuzz 10% $? $@

mstile-310x150.png: $(ICON_SRC)
    convert -resize 126x126 \
        -gravity west  -splice 216x0 \
        -gravity east  -splice 216x0 \
        -gravity north -splice 0x50 \
        -gravity south -splice 0x94 \
        -transparent '#ffffff' -fuzz 10% $? $@

mstile-310x310.png: $(ICON_SRC)
    convert -resize 270x270 \
        -gravity west  -splice 144x0 \
        -gravity east  -splice 144x0 \
        -gravity north -splice 0x144 \
        -gravity south -splice 0x144 \
        -transparent '#ffffff' -fuzz 10% $? $@

mstile-70x70.png: $(ICON_SRC)
    convert -resize 96x96 \
        -gravity west  -splice 16x0 \
        -gravity east  -splice 16x0 \
        -gravity north -splice 0x16 \
        -gravity south -splice 0x16 \
        -transparent '#ffffff' -fuzz 10% $? $@

favicons/browserconfig.xml

Favicon Generator が作成してくれたものをそのまま使っている。

<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square150x150logo src="/favicons/mstile-150x150.png"/>
            <TileColor>#da532c</TileColor>
        </tile>
    </msapplication>
</browserconfig>

favicons/site.webmanifest

Favicon Generator が作成してくれたものをそのまま使っている。

{
    "name": "",
    "short_name": "",
    "icons": [
        {
            "src": "/favicons/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/favicons/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "theme_color": "#ffffff",
    "background_color": "#ffffff",
    "display": "standalone"
}