站点以两种方式之一决定使用浅色还是深色:访客的系统偏好,或站点为该访客保存的显式选择。EmDash 组件通过 <html> 元素上的同一个约定来读取这两种信号。本页介绍这个约定、如何为图片字段提供深色变体,以及如何使用 emdash/ui 中的 Image 组件渲染它。
主题约定
组件和模板按以下顺序使用这两种信号:
<html>上的dark或light类会固定配色方案。该类优先于系统偏好。- 没有类时,配色方案遵循
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 }
}
在编辑器中选择变体
-
打开一个条目,像往常一样选择主图片。
-
点击图片下方的 Add dark mode variant,然后从媒体库中选择对应的深色图片。
-
保存该条目。
变体以 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。