このページで分かること

  • EmDashのコンポーネントが従う配色の決め方(<html>darklight クラス、クラスがなければ prefers-color-scheme
  • 画像フィールドでダークモード用の画像を有効にする方法(管理画面、またはシードファイルの darkVariant)と、編集画面での選び方
  • Image コンポーネントが2つの画像を出し分ける仕組み、読み込みの動作、別の配色の決め方を使う場合のCSS
難易度
実践
読む時間
4分
このページの目次

サイトがライトとダークのどちらで表示するかは、2つの方法のどちらかで決まります。訪問者のシステムの設定か、サイトがその訪問者ごとに保存した明示的な選択です。EmDashのコンポーネントは、<html> 要素に対する1つの決まりを通じて、この両方の情報を読み取ります。このページでは、その決まり、画像フィールドにダークモード用の画像を持たせる方法、emdash/uiImage コンポーネントでそれを描画する方法を説明します。

テーマの決まり

コンポーネントとテンプレートは、次の2つの情報をこの順番で使います。

  1. <html>dark または light クラスがあれば、そのクラスで配色が固定されます。クラスはシステムの設定より優先されます。
  2. クラスがなければ、配色は prefers-color-scheme メディアクエリに従います。

同梱のテンプレートは、明示的な選択を theme Cookieに保存し、<head> 内のインラインスクリプトで最初の描画より前に適用します。次のスクリプトはCookieを読み取ってクラスを設定し、選択が保存されていない場合は何もしません。

src/layouts/Base.astro
<script is:inline>
	(function () {
		var c = document.cookie;
		var i = c.indexOf("theme=");
		var theme = i >= 0 ? c.slice(i + 6).split(";")[0] : null;
		if (theme === "dark" || theme === "light") {
			document.documentElement.classList.add(theme);
		}
	})();
</script>

色は light-dark() で1回だけ定義し、配色の固定はクラスに任せます。

src/styles/global.css
:root {
	color-scheme: light dark;
	--color-bg: light-dark(#ffffff, #0d0d0d);
	--color-text: light-dark(#1a1a1a, #ededed);
}
:root.light {
	color-scheme: light;
}
:root.dark {
	color-scheme: dark;
}

テーマの切り替えを持たないサイトでは、スクリプトは必要ありません。<html> にクラスを付けなければ、システムの設定が適用されます。

本サイトの補足 やさしい解説

WordPressではテーマごとにダークモードの作り方が異なりますが、EmDashでは決まりが1つにまとまっています。<html>darklight のクラスが付いていればそれに従い、付いていなければ訪問者のパソコンやスマートフォンの設定(prefers-color-scheme)に従います。訪問者が画面上で切り替えられるようにしたい場合だけ、選択をCookieに保存して、ページの表示前にクラスを付けるスクリプトを使います。切り替えボタンを置かないサイトなら、スクリプトは不要です。

ダークモード用の画像

画像フィールドには、ダークな配色用に2枚目の画像を持たせられます。編集者はメインの画像の隣でそれを選び、Image コンポーネントが訪問者の配色に合うほうを表示します。

フィールドで画像の枠を有効にする

この枠は初期状態では無効です。フィールドごとに、管理画面またはシードファイルで有効にします。

管理画面では、「コンテンツタイプ」を開き、画像フィールドを編集して、「Dark mode variant」をオンにします。

シードファイルでは、フィールドに darkVariant ウィジェットオプションを設定します。

.emdash/seed.json
{
	"slug": "featured_image",
	"label": "Featured Image",
	"type": "image",
	"options": { "darkVariant": true }
}

編集画面で画像を選ぶ

  1. エントリーを開き、いつもどおりメインの画像を選択します。

  2. 画像の下にある「Add dark mode variant」をクリックし、メディアライブラリからダークモード用の画像を選びます。

  3. エントリーを保存します。

ダークモード用の画像は、フィールドの値の中に darkVariant として保存されます。メインの画像を削除すると、ダークモード用の画像も一緒に削除されます。メインの画像を差し替えた場合は、ダークモード用の画像を差し替えるか削除するまで、そのまま残ります。

ダークモード用の画像を描画する

値に darkVariant が含まれている場合、Image コンポーネントは両方の画像を描画し、CSSで合うほうを表示します。テンプレートを変更する必要はありません。

src/pages/posts/[slug].astro
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Image } from "emdash/ui";

const slug = decodeSlug(Astro.params.slug);

if (!slug) {
  return Astro.redirect("/404");
}

const { entry: post } = await getEmDashEntry("posts", slug);

if (!post) {
  return Astro.redirect("/404");
}
---

{post.data.featured_image && <Image image={post.data.featured_image} priority />}

出力には2つの <img> 要素が含まれます。メインの画像には emdash-image--light クラスが、ダークモード用の画像には emdash-image--dark クラスが付きます。どちらも、メインの画像の alt テキスト、幅と高さの上書き、読み込みに関する属性を使います。プレースホルダーの色は、それぞれの画像のものが使われます。

渡した id はメインの画像に付きます。ダークモード用の画像には、同じ id--dark を付けたものが付きます。つまり id="hero" を渡すと、herohero--dark になります。

ダークモード用の画像が別の場所(2つ目の画像フィールドなど)にある場合は、明示的に渡します。

<Image image={post.data.hero} darkVariant={post.data.hero_dark} />

読み込みの動作

初期状態では、両方の画像が遅延読み込み(lazy)になります。ブラウザーは display: none で非表示になっている遅延読み込みの画像を取得しません。そのため、訪問者がダウンロードするのは自分の配色に合う画像だけで、もう一方は配色が変わったときに読み込まれます。

priority を指定すると、両方の画像に loading="eager"fetchpriority="high" が付き、どの配色でも両方がダウンロードされます。テーマはブラウザー側で決まるため、訪問者がどちらの画像を見るかをサーバーは判断できません。priority は、スクロールせずに見える位置にある1枚の画像にだけ使い、ほかの画像は遅延読み込みのままにします。

本サイトの補足 やさしい解説

ダークモード用の画像は、WordPressの画像フィールドにはない、EmDashの画像フィールドの機能です。管理画面でフィールドの設定をオンにすると、記事の編集画面でメインの画像の下にダークモード用の画像を選ぶボタンが表示されます。テンプレート側は、これまでどおり Image コンポーネントに画像を渡すだけで、2枚の出し分けは自動で処理されます。priority を付けると2枚とも必ずダウンロードされるため、ページ上部の1枚だけに使います。

別のテーマの決まりを使う

同梱のCSSは、配色に合わないほうの画像を非表示にします。そのセレクターは <html> の部分に :where() を使っているため、クラスや属性で <html> を指定した独自のルールがあれば、そちらが優先されます。

切り替えの仕組みが data-theme のような属性を設定する場合、最も手短な対処は、同じ処理の中で darklight のクラスも設定することです。そうしない場合は、自分のスタイルシートで4つのケースを上書きします。

src/styles/global.css
:root[data-theme="dark"] .emdash-image--light,
:root[data-theme="light"] .emdash-image--dark {
	display: none;
}
:root[data-theme="dark"] .emdash-image--dark,
:root[data-theme="light"] .emdash-image--light {
	display: block;
}

display の値は、スタイルシートのほかの場所で画像に指定している値に合わせます。たとえば、imgblock にリセットしていない場合は inline にします。