💎

GitHubのREADMEの中の画像(SVG)をダークモードに対応させる

2021/02/01に公開
4

TL;DR

SVGの中のstyleタグの中では実はMedia Queryが使えるので、

@media (prefers-color-scheme: dark) {
	/* Dark Mode用の設定をここに書く */
}

とやると、かんたんにダークモードに対応した画像が作成できる。
もちろんCSS Variablesを使ったりもできる。
もしかするとdisplay: none;を使うこともできるかもしれないので、SVGの中に画像を埋め込み、ダークモードをライトモードで出し分ける、なんてこともできるかもしれない。

暇な人向け

事の発端は @treastrain が公開している、 Japan NFC Reader という各種ICカードの残高等データの読み取りがiPhoneでできるアプリの核に当たる部分のOSSがありまして、

https://github.com/treastrain/TRETJapanNFCReader

これに使用しているトップの画像がダークモードでうまいこと表示できないか、という話になったことからはじまりました。

要はGitHubでCSSが使えればいいのですががそうはいかないので、SVGの中に閉じさせて、それをimgで読み込めばいいじゃない、というのが今回の発想です。
短く書いたものの、まさかSVGの中でMedia Queryが使えるとはまっっっったく思わず時間がかかってしまいました……。

今回参考にさせていただいたのはこちら。ありがとうございます。

https://coliss.com/articles/build-websites/operation/work/svg-favicon-in-dark-mode.html

https://blog.tomayac.com/2019/09/21/prefers-color-scheme-in-svg-favicons-for-dark-mode-icons/

ですが調べてもなぜかGitHubのREADMEで使う、という話がなかったので、記事に起こしてみることにした、というのが今回のモチベーションです。
そして、これらを参考に作ったのがこのrepository

https://github.com/Qs-F/readme-darkmode-svg

うまくいっていれば、それぞれ、

ダークモード使用時:

ダークモード使用時 ロゴが白色 テキストが白地に黒文字で表示されている

ライトモード使用時:

ライトモード使用時 ロゴが黒色 テキストが黒地に白文字で表示されている

このように表示されているはずです。細かい部分はぜひRepositoryのほうを参考にしていただきたいですが、それぞれのコードを貼って少し解説しますと、

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 800 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
    <g transform="matrix(1,0,0,1,-7737.59,0)">
        <g id="b" transform="matrix(1,0,0,1,4849.31,-1866.02)">
            <rect x="2888.28" y="1866.02" width="800" height="800" style="fill:none;"/>
            <g transform="matrix(1.49842,0,0,1.49785,-1619.95,-1034.65)">
                <style>
                    path {
                        fill: rgb(0,20,51);
                    }
                    @media (prefers-color-scheme: dark) {
                        path {
                            fill: rgb(221, 235, 255);
                        }
                    }
                </style>
                <path d="M3277.17,2298.36C3277.03,2298.34 3276.25,2298.26 3276.1,2298.25C3275.06,2298.88 3274.69,2298.96 3273.58,2299.53C3269.77,2301.48 3261.81,2304.43 3253.71,2307.39C3244.82,2310.65 3236.62,2312.43 3229.34,2312.05C3216.68,2311.38 3208.82,2304.48 3208.82,2292.95C3208.82,2291.6 3209.1,2289.25 3209.68,2285.89C3210.26,2282.53 3211.03,2279.5 3211.99,2276.81L3211.41,2276.81C3208.72,2280.08 3205.64,2283.68 3202.19,2287.62C3198.73,2291.56 3194.84,2295.21 3190.51,2298.57C3186.19,2301.93 3181.43,2304.72 3176.25,2306.93C3171.06,2309.14 3165.29,2310.24 3158.95,2310.24C3154.92,2310.24 3151.03,2309.52 3147.28,2308.08C3143.53,2306.64 3140.22,2304.43 3137.34,2301.45C3134.45,2298.47 3132.2,2294.68 3130.56,2290.07C3128.93,2285.45 3128.11,2279.98 3128.11,2273.64C3128.11,2265.95 3129.65,2258.27 3132.72,2250.58C3135.8,2242.89 3139.78,2235.54 3144.68,2228.53C3149.59,2221.52 3155.25,2215.03 3161.69,2209.08C3168.13,2203.12 3174.66,2197.93 3181.29,2193.51C3187.92,2189.09 3194.45,2185.63 3200.89,2183.13C3207.33,2180.64 3213.04,2179.39 3218.04,2179.39C3223.8,2179.39 3228.7,2181.02 3232.74,2184.29C3236.77,2187.55 3238.79,2191.88 3238.79,2197.26L3239.37,2197.26L3276.84,2091.77L3232.73,2091.14L3234.27,2087.11L3298.45,2087.73L3233.03,2270.47C3231.3,2275.46 3229.81,2280.27 3228.56,2284.88C3227.31,2289.49 3226.75,2293.62 3226.69,2297.27C3226.61,2301.63 3227.41,2308.04 3233.29,2308.77C3239.27,2309.51 3247.2,2307.26 3253.24,2304.46C3261.48,2300.64 3266.08,2294.05 3269.14,2290.19C3275.5,2282.18 3281.88,2264.05 3285,2249.98C3287.2,2240.09 3301.19,2152.94 3301.19,2152.94L3356.21,2151.93L3351.3,2181.81L3352.93,2181.81C3362.35,2159.7 3386.72,2150.69 3403.48,2149.88C3412.43,2149.44 3435.13,2150.35 3448.45,2159.2L3408.41,2205.34C3402.25,2201.31 3388.58,2196.88 3379.14,2197.37C3362.78,2198.22 3348.43,2209.24 3345.15,2227.26L3331.64,2308.12L3275.14,2309.14L3277.17,2298.36ZM3230.72,2220.89L3215.45,2263.26C3214.48,2265.95 3212.27,2269.7 3208.82,2274.5C3205.36,2279.31 3201.22,2284.11 3196.42,2288.91C3191.62,2293.72 3186.33,2297.85 3180.57,2301.31C3174.81,2304.77 3169.14,2306.5 3163.56,2306.5C3157.99,2306.5 3154,2304.57 3151.6,2300.73C3149.2,2296.89 3148,2291.89 3148,2285.74C3148,2280.36 3148.86,2274.12 3150.59,2267.01C3152.32,2259.9 3154.77,2252.55 3157.94,2244.96C3161.11,2237.37 3164.91,2229.88 3169.33,2222.48C3173.75,2215.08 3178.6,2208.5 3183.88,2202.73C3189.17,2196.97 3194.84,2192.31 3200.89,2188.76C3206.94,2185.2 3213.14,2183.42 3219.48,2183.42C3222.94,2183.42 3225.72,2184.05 3227.84,2185.3C3229.95,2186.55 3231.63,2188.03 3232.88,2189.76C3234.13,2191.49 3235,2193.37 3235.48,2195.38C3235.96,2197.4 3236.2,2199.28 3236.2,2201C3236.2,2202.93 3235.86,2205.18 3235.19,2207.78C3234.52,2210.37 3233.03,2214.74 3230.72,2220.89Z" />
            </g>
        </g>
    </g>
</svg>

これの、

                <style>
                    path {
                        fill: rgb(0,20,51);
                    }
                    @media (prefers-color-scheme: dark) {
                        path {
                            fill: rgb(221, 235, 255);
                        }
                    }
                </style>

ここが重要です。

また、テキストの方は、

<svg fill="none" viewBox="0 0 800 400" width="800" height="400" xmlns="http://www.w3.org/2000/svg">
	<foreignObject width="100%" height="100%">
		<div xmlns="http://www.w3.org/1999/xhtml">
			<style>
				.root {
					--whitish: #fff;
					--blackish: #000;
					--bg: var(--blackish);
					--color: var(--whitish);
				}
				@media (prefers-color-scheme: dark) {
					.root {
						--bg: var(--whitish);
						--color: var(--blackish);
					}
				}

				p {
					color: var(--bg)
				}

				span {
					font-size: 32px;
				}

				.support-reverse-darkmode {
					background: var(--bg);
					color: var(--color);
				}

			</style>
			<div class="root">
				<p>Test</p>
				<p><a href="https://de-liker.com">Hello Link</a></p>
				<div class="support-reverse-darkmode">
					<span>This text should be rendered Black Text on White Background in dark mode.</span>
					<br />
					<span>This text should be rendered White Text on Black Background in light mode.</span>
				</div>
			</div>
		</div>
	</foreignObject>
</svg>

こんな感じで、CSS VariablesとDark Mode対応を組み合わせた、よくある感じのセットアップになってます(インデントが雑で申し訳ないです、これの実験中にDevToolsのemulationだとうまく切り替わらないことに偶然気がついた関係でデバッグの修正忘れて.rootにしてますが、たぶんふつうに:rootが使えると思います)。

あとはこれを <img src="hogehoge.svg" /> で普通に画像として読み込めば完了です。

ぜひ応用して、GitHubのREADMEをもっと素晴らしく、読みやすいものにしていきましょう!!
(くれぐれもSVGにテキストを入れるならaltの設定を忘れずに!!)

おわり

Discussion

Yuki HattoriYuki Hattori

初めまして。自身も OSS を運営していて、GitHub のダークモード発表直後に似た様な悩みを抱えた者です。

GitHub の README で Media query を使うのは懸念点があり、GitHub が OS の設定と連動した状態であればこの方法で問題ありませんが、ユーザー設定によって OS と異なる色に設定されている場合、逆の色が表示されるケースがあります(例えば『OS は白、GitHub は黒』など)。
https://github.com/settings/appearance

GitHub がダークモードで使っている CSS variables を引っ張ってくることができれば良いのですが、README で SVG を表示する方法が <img> タグしかない現状、その値を使うことはできないので、なんとも悩ましいところです。
https://stackoverflow.com/questions/52009759/use-a-css-variable-in-svg-via-img

GitHub も問題は認知しているようで、根本解決のために動いているようです。

https://github.community/t/support-theme-context-for-images-in-light-vs-dark-mode/147981

たふみたふみ

はじめまして, Hattoriさん。
すごく参考になるコメントをありがとうございます……!!!
それでOSのappearanceの設定を変えないと反映されなかったんですね。
私も記事中で書いたんですが,GitHub モバイルのアプリだとそもそもすべてライトモードとして扱われてしまうので,そういうところも困ったなと思ってます。

GitHubでもきちんと認知されてるんですね!知らなかったので助かります,貴重なご意見ありがとうございます。

たふみたふみ

わざわざありがとうございます…!!早速記事を更新させていただきました!