深色模式

本页内容

网站通过两种方式之一在浅色和深色之间做出选择:访客的系统偏好,或网站为该访客存储的显式选择。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 存储在字段值内部。移除主图片会同时移除变体;替换主图片会保留变体,直到你替换或移除它。

渲染变体

Image 组件在值携带 darkVariant 时渲染两张图片,并使用 CSS 显示匹配的那张。模板中无需更改:

---
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 />}

输出包含两个 <img> 元素。主图片获得类 emdash-image--light,变体获得 emdash-image--dark。两者都使用主图片的替代文本、宽高覆盖和加载属性。每个保留自己的占位符颜色。

你传递的 id 保留在主图片上;变体获得带 --dark 后缀的相同 id,因此 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。