html picture标签通过prefers-color-scheme实现主题切换无需js,但必须暗色source在前、media语法完整(含括号冒号)、img有src和alt且非自闭合,否则完全失效。

直接用 <picture></picture> + prefers-color-scheme 就能实现,不需要 JS,但顺序、语法、<img> fallback 三处写错就完全不生效。
media 属性必须写完整且顺序不能颠倒
浏览器从上到下匹配 <source></source>,第一个满足 media 条件的立即生效,其余跳过。暗色模式必须放前面,否则 Safari 会忽略它:
- ✅ 正确:
media="(prefers-color-scheme: dark)"(括号、冒号、空格缺一不可) - ❌ 错误:
media="prefers-color-scheme: dark"(少括号)、media="dark"(语法无效) - ❌ 错误:把 light 的
<source></source>写在 dark 前面——在系统切到暗色时,light 规则已命中,dark 规则根本不会被检查
不是可选,而是强制存在的渲染节点
<picture></picture> 自身不渲染任何内容,它只是个决策容器。真正显示图片的是内部的 <img> 标签。漏掉它,或 src 为空、alt 缺失,结果就是页面留白:
- ✅ 必须有
<img src="/images/logo-light.jpg" alt="品牌标识"> - ❌ 不能写成
<img src="">或<img src="#">—— 这些在部分浏览器里会触发 404 请求或直接不加载 - ❌ 不能省略
alt—— 不仅影响无障碍访问,某些浏览器加载失败时只显示alt文本,没它就啥也不见 - ?
<img>的src可复用某个<source></source>的图(比如 light 版),但绝不能“偷懒留空”
用户手动切换主题时,图片会自动更新吗?
会,但前提是浏览器支持且写法合规。Chrome、Firefox、Safari 15.4+、Edge 101+ 在监听到系统 prefers-color-scheme 变更后,会自动重选并替换已渲染的 <img>。这不是靠 JS 轮询,而是原生机制:
- ✅ 触发条件:用户在系统设置里开关深色模式,或浏览器地址栏点击切换(如 Safari 的深色/浅色按钮)
- ⚠️ 不触发:仅靠 JS 修改
document.documentElement.dataset.theme不会影响<picture></picture>—— 它只响应 CSS 媒体查询,不响应自定义属性 - ? 如果你做了手动主题切换按钮(比如 localStorage 控制),那图片切换就得另配逻辑:要么用 JS 替换
<img>.src,要么干脆不用<picture></picture>,改用 class 切换 + CSSbackground-image
旧浏览器 fallback 是怎么工作的?
不支持 prefers-color-scheme 的浏览器(比如旧版 Safari 或 IE)会跳过所有 <source></source>,直接渲染 <img>。所以它的 src 必须是个安全、可用的默认图:
- ✅ 推荐用 light 版作为 fallback —— 多数用户初始环境是浅色,也符合“未指定偏好”时的语义
- ✅ 可额外加一条
<source media="(prefers-color-scheme: no-preference)" srcset="logo-light.png"></source>显式覆盖“未指定”场景,但非必需 - ⚠️ 不要指望 polyfill —— 没必要,也不现实;
<picture></picture>的设计哲学就是优雅降级,<img>就是兜底
最容易被忽略的其实是 <img> 的存在感:它看着像后备,实则是唯一渲染出口。很多人调了半天 <source></source> 却忘了它,最后页面一片空白,查半天才发现是 <img> 少了 src 或写成了自闭合标签。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











