深色模式

本頁內容

站台以兩種方式之一決定使用淺色還是深色:訪客的系統偏好,或站台為該訪客儲存的明確選擇。EmDash 元件透過 <html> 元素上的同一個慣例來讀取這兩種訊號。本頁說明這個慣例、如何為圖片欄位提供深色變體,以及如何使用 emdash/ui 中的 Image 元件算繪它。

主題慣例

元件和範本依下列順序使用這兩種訊號:

  1. <html> 上的 dark 或 light 類別會固定配色方案。該類別優先於系統偏好。
  2. 沒有類別時,配色方案會遵循 prefers-color-scheme 媒體查詢。

內建範本會把明確的選擇儲存在 theme cookie 中,並透過 <head> 中的內嵌指令碼在首次繪製之前套用它。下面的指令碼會讀取該 cookie 並設定類別;當沒有儲存任何選擇時,它什麼也不做:

<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() 只需定義一次顏色,並讓類別來固定配色方案:

: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> 不帶類別,系統偏好就會生效。

深色圖片變體

圖片欄位可以攜帶第二張用於深色配色方案的圖片。編輯者在主圖片旁邊選擇它,Image 元件會顯示與訪客配色方案相符的那一張。

在欄位上啟用插槽

插槽預設為關閉。你可以依欄位開啟,既可以在管理後台中,也可以在種子檔案中。

在管理後台中,開啟 Content Types,編輯圖片欄位,然後開啟 Dark mode variant。

在種子檔案中,在欄位上設定 darkVariant 小工具選項:

{
	"slug": "featured_image",
	"label": "Featured Image",
	"type": "image",
	"options": { "darkVariant": true }
}

在編輯器中選擇變體

  1. 開啟一個條目,像往常一樣選擇主圖片。

  2. 點擊圖片下方的 Add dark mode variant,然後從媒體庫中選擇對應的深色圖片。

  3. 儲存該條目。

變體會以 darkVariant 的形式儲存在欄位值中。移除主圖片會同時移除變體;替換主圖片則會保留變體,直到你替換或移除它。

算繪變體

當值帶有 darkVariant 時,Image 元件會算繪兩張圖片,並用 CSS 顯示相符的那一張。範本無需任何改動:

---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Image } from "emdash/ui";

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

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

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

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

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

輸出包含兩個 <img> 元素。主圖片帶有類別 emdash-image--light,變體帶有 emdash-image--dark。兩者都使用主圖片的 alt 文字、寬高覆寫值和載入屬性。每張圖片各自保留自己的預留位置顏色。

你傳入的 id 會保留在主圖片上;變體會得到相同的 id 並加上 --dark 後綴,因此 id="hero" 會產生 hero 和 hero--dark。

當深色圖片來自其他地方(例如第二個圖片欄位)時,請明確傳入:

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

載入行為

兩張圖片預設都是延遲載入。瀏覽器不會擷取被 display: none 隱藏的延遲載入圖片,因此訪客只會下載與其配色方案對應的變體,另一張會在配色方案改變時再載入。

使用 priority 時,兩張圖片都會得到 loading="eager" 和 fetchpriority="high",並且在每種配色方案下都會下載。主題是在瀏覽器中決定的,所以伺服器無法得知訪客會看到哪個變體。請只對首屏上方的那一張圖片使用 priority,其他圖片保持延遲載入。

使用不同的主題慣例

隨附的 CSS 會隱藏與配色方案不相符的變體。它的選擇器在 <html> 部分使用 :where(),因此你自己的任何以類別或屬性指向 <html> 的規則都會優先生效。

如果你的切換器設定的是 data-theme 之類的屬性,最簡單的辦法是同時在同一段程式碼路徑中設定 dark 和 light 類別。否則,請在你自己的樣式表中覆寫這四種情況:

: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 的值與你的樣式表在其他位置給圖片設定的值保持一致,例如當你沒有把 img 重設為 block 時使用 inline。