从 15 版开始,Safari 在 macOS 和 iOS 上都支持theme-color
<meta>
标签。这是一个令人兴奋的消息,因为现在第一个桌面浏览器支持此<meta>
标签,并且它也支持media
属性和prefers-color-scheme
媒体特性。
我从未真正注意过theme-color
元标签,但现在是了解其功能和局限性并尝试发现一些有趣用例的好时机。
注意!Safari 在 Safari Technology Preview (127) 中删除了对theme-color
元标签的支持。这只是暂时的,从 128 版开始,它再次支持它。
功能和限制
过去几年我一直这样使用theme-color
元标签:只是content
属性的一个普通的十六进制代码。
<meta name="theme-color" content="#319197">

根据我今年早些时候进行的测试,这在 Chrome、Brave 和三星互联网(Android 上)、Chrome 中安装的 PWA 以及现在 Safari Technology Preview 中均有效。

CSS 颜色支持
我想到的第一个问题之一是“我们也可以使用颜色关键字、hsl()
、rgb()
吗?”根据 HTML 规范,属性的值可以是任何 CSS 颜色。我创建了这个 theme-color
测试 CodePen 来验证这一点。
<meta name="theme-color" content="hsl(24.3, 97.4%, 54.3%)">

theme-color
元标签支持任何形式的 CSS 颜色:关键字、rgb()
、hsl()
或十六进制代码。
所有支持的浏览器也支持hsl()
和rgb()
。这很棒,因为它允许我们使用 JavaScript 做一些很酷的事情。我们稍后会讨论这个问题,但首先让我们看看一些限制。
透明度
十六进制代码、rbg()
、hsl()
和关键字都得到了良好且一致的支持,但包含透明度的颜色:则没有那么好。实际上,它们在大多数浏览器中都受支持,但结果并不一致,有时甚至出乎意料。
transparent
是一个 CSS 颜色,在大多数浏览器中,它在theme-color
元标签中使用时会按预期工作。所有常规移动浏览器都不会更改颜色并显示默认标签栏,但 macOS 上的 Safari 和 macOS 上的 Chrome Canary PWA 会将标签栏变为黑色。Android 上的 PWA 回退到manifest.json
中定义的theme-color
,我们稍后会讨论。

theme-color
元标签的浏览器所有浏览器都解释hsla()
和rgba()
,但它们将 alpha 值设置为 1。唯一的例外是 macOS 上的 Safari;它解释了透明度,但透明颜色似乎有一个黑色基线。这会导致浅橙色看起来像深橙色。

hsla()
应用于theme-color
元标签新的颜色函数
Safari 15 是第一个支持lab()
、lch()
和hwb()
颜色函数的浏览器。如果您在 CSS 中使用这些函数,它们可以工作,但如果您在theme-color
元标签中使用它们,则不行。
所有三个声明在 Safari 15 中都运行良好
body {
background-color: hwb(27 10% 28%);
background-color: lch(67.5345% 42.5 258.2);
background-color: lab(62.2345% -34.9638 47.7721);
}
如果您在theme-color
元标签中使用任何新的颜色函数,Safari 不会解释它们,而是回退到它自己的颜色选择算法。Safari 可能会使用您的<body>
的背景颜色作为theme-color
,这意味着您可能在没有显式定义theme-color
的情况下获得预期结果。
<meta name="theme-color" content="lab(29.2345% 39.3825 20.0664)">

请注意,在撰写本文时 Safari 15 是唯一支持这些新颜色函数的浏览器。
currentColor
如果支持 CSS 颜色,那么currentColor
也应该可以工作,对吧?不,不幸的是,在任何浏览器中都不行。这可能是一个不常见的用例,但我希望我们可以将theme-color
设置为<body>
或<html>
元素的当前颜色。
<style>
body {
color: blue;
}
</style>
<meta name="theme-color" content="currentColor">
我在 WebKit 错误跟踪器中找到了一张标题为 “<meta name="theme-color" content="...">
也应支持 CSS currentcolor
。” 的工单。如果有人处理这张工单,支持可能会在将来发生变化。
禁止的颜色
当我测试 CSS 颜色关键字时,我使用了颜色red
,但它不起作用。首先,我认为不支持关键字,但blue
、hotpink
和green
运行良好。事实证明,Safari 不支持 一小部分颜色,这些颜色会妨碍使用界面。red
不起作用,因为它在视觉上与标签栏中关闭按钮的背景颜色过于接近。此限制特定于 Safari,在所有其他支持的浏览器中,任何颜色似乎都可以正常工作。

theme-color
设置为red
,Safari 会使用它认为合适的任何颜色。自定义属性
我不太了解浏览器和自定义属性的内部机制,以及是否可以在<head>
中访问自定义属性,但我还是试了一下。不幸的是,它在任何浏览器中都不起作用。
<style>
:root {
--theme: blue;
}
</style>
<meta name="theme-color" content="var(--theme)">
这几乎是我想知道的关于theme-color
元标签基本支持的所有内容。接下来,让我们看看如何以及如何不为标签栏实现暗模式。
暗模式
Safari 15 是第一个支持theme-color
元标签上的media
属性和prefers-color-scheme
媒体特性的桌面浏览器。从 93 版开始,Chrome 也支持它,但仅限于已安装的渐进式 Web 应用。
根据 web.dev 上的 Web 应用清单页面,如果您定义了多个theme-color
元标签,浏览器会选择第一个匹配的标签。
<meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">
我渴望了解在不支持media
属性的浏览器中会发生什么。我创建了一个 演示页面来测试暗模式,其中包含上述元标签,还允许您将站点安装为 PWA。webmanifest.json
包含另一个主题颜色的颜色定义。
{
"name": "My PWA",
"icons": [
{
"src": "https://via.placeholder.com/144/00ff00",
"sizes": "144x144",
"type": "image/png"
}
],
"start_url": "/theme-color-darkmode.html",
"display": "standalone",
"background_color": "hsl(24.3, 97.4%, 54.3%)",
"theme_color": "hsl(24.3, 97.4%, 54.3%)"
}
以下是支持的浏览器在亮模式下显示标签栏的方式。无论浏览器是否支持媒体属性,它都会解释第一个元标签,这一点无关紧要。

以下是同一页面上的标签栏在深色模式下的外观。这些结果比较有趣,因为它们略有差异。Canary PWA 和 Safari 支持并显示深色。所有移动浏览器都使用其默认的深色标签栏样式,除了三星互联网,因为它不支持prefers-color-scheme
媒体特性,所以使用浅色样式。(TIL:这应该在不久的将来发生变化。)

我做了一个最后的测试。我想看看如果我只为深色模式定义主题颜色,但在浅色模式下访问页面会发生什么。
<meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">

这些结果让我最惊讶,因为我预计所有移动浏览器都会忽略媒体属性,而不管怎样都只使用元标签中的深色,但普通 Chrome Canary 完全忽略了整个元标签,即使它不支持media
属性。正如预期的那样,两个 Canary PWA 都回退到清单文件中定义的颜色。
另一件有趣的事情是,Safari 即使我没有为浅色模式定义主题颜色,也会显示一个theme-color
。这是因为,如果你没有提供theme-color
,Safari 会自己选择一个颜色。在这种情况下,它使用页面的背景颜色,但它也可能使用<header>
元素的背景颜色,例如。
如果你想为浅色和深色模式定义主题颜色,最好的方法是定义这两种颜色,并使用第一个元标签作为不支持媒体特性的浏览器的后备。
<meta name="theme-color" content="#319197" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">
Safari 已经证明theme-color
在桌面浏览器上也能很好地工作。我相信设计师和开发人员会找到许多创造性的方法来使用这个元标签,特别是考虑到可以通过 JavaScript 更改其值。我收集并创建了一些有趣的演示,以激发你的灵感。
演示和用例
渐变
如果你在页面上使用渐变,可以通过使渐变跨越整个浏览器来突出你的样式。theme-color
元标签不支持渐变,但你可以对元标签和页面背景渐变的起始颜色使用相同的颜色。
<meta name="theme-color" content="rgb(0, 235, 255)">
<style>
body {
background: linear-gradient(rgb(0, 235, 255), #08124a);
}
</style>

表单验证
我构建了这个表单概念验证,它在表单验证时更改theme-color
。它以蓝色标签栏开始,如果提交的数据无效则变为红色,如果有效则变为绿色。

const email = document.querySelector('input')
const themeColor = document.querySelector('meta[name="theme-color"]')
const msg = document.querySelector('[aria-live]')
let color = '#FA0000'
let message = 'Error message'
document.querySelector('button').addEventListener('click', (e) => {
e.preventDefault()
email.reportValidity()
email.setAttribute('aria-invalid', true)
if (email.validity.valid) {
color = '#00FF00'
message = "Success message!"
email.setAttribute('aria-invalid', false)
}
msg.textContent = message
themeColor.setAttribute('content', color)
});
迪斯科模式
我并不是说你应该这样做,但你可以通过结合setInterval
和 hsl()
颜色将你的网站置于💃迪斯科模式🕺。
/*
Inspired by https://twitter.com/argyleink/status/1408184587885309952
*/
const motion = window.matchMedia("(prefers-reduced-motion: no-preference)");
// Check if users don't have a preference for reduced motion
if (motion.matches) {
let scheme = document.querySelector('meta[name="theme-color"]')
let hue = 0
let color
setInterval(() => {
color = `hsl(${hue+=5} 50% 30%)`
document.body.style.background = color;
scheme.setAttribute('content', color)
}, 50)
滚动
Stuart 有一个好主意,他建议在滚动时更改主题颜色。我构建了这个快速原型,同样使用hsl()
颜色。
请仅在不会对性能产生负面影响的情况下执行此操作。
Max 构建了一个演示,其中他根据视口中当前部分的背景颜色使用 Intersection Observer 更改theme-color
。
const setThemeColor = (color) => {
const meta = document.querySelector('meta[name="theme-color"]')
if (meta) {
meta.setAttribute('content', color)
}
}
if ("IntersectionObserver" in window) {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const { isIntersecting, target } = entry
if (isIntersecting) {
const color = window.getComputedStyle(target).getPropertyValue("background-color");
setThemeColor(color)
}
})
}, {
root: document.getElementById('viewport'),
rootMargin: "1px 0px -100% 0px",
treshold: 0.1
})
document.querySelectorAll('.section').forEach(section => {
observer.observe(section)
})
}
提取颜色
另一个有趣的想法是从你的标题图像中自动提取主导颜色或平均颜色,并将其用作theme-color
。

<script type="module">
import fastAverageColor from "https://cdn.skypack.dev/[email protected]";
const fac = new fastAverageColor();
fac.getColorAsync(document.querySelector('img'))
.then(color => {
document.querySelector('meta[name="theme-color"]').setAttribute('content', color.rgba)
})
.catch(e => {
console.log(e);
});
</script>
<img src="/amy-humphries-2M_sDJ_agvs-unsplash.jpg" alt="A sea star on blue sand." />
这只是一些想法,但我已经喜欢上它的发展方向,我相信你会想出更多创造性的方法来使用theme-color
元标签。
在 Big Sur 上的 Safari 15 中,theme-color 对我无效。其他人也遇到这种情况吗?
正如 Miguel 已经提到的,他们在当前版本的 Safari TP(127)中删除了支持。
https://developer.apple.com/safari/technology-preview/release-notes/
奇怪的是,在我编写这段文字时,它似乎只对某些颜色有效。“#eac8df”不起作用,但“#9a702a”起作用。同样,“blue”也起作用。而且这似乎只发生在桌面端,在 iOS 15 上工作正常。我有点困惑。
在
head
中添加这样的内容还有意义吗?到
head
?好问题!我还没有机会在移动 Safari 上测试,但我将在获得结果后更新这篇文章。
这在最新的 Safari 技术预览更新(版本 127)中被删除了,但根据他们关于主题颜色的说明,这似乎是暂时的。
https://developer.apple.com/safari/technology-preview/release-notes/
在 Safari 技术预览版 128(昨天)中,它再次起作用了。
感谢告知,Anders。我已经更新了文章!
我发现,在 iOS 的 PWA 模式下,如果更改主题颜色,会出现意想不到的过渡效果,但在移动 Safari 上没有这种效果。我在 CSS 中禁用了 html 和 body 标签的所有过渡,但过渡效果仍然存在。你知道如何禁用它吗?
更改主题颜色是什么意思?从浅色模式切换到深色模式?以及什么类型的意想不到的过渡?你是否有我可以用来自己测试的链接?
这是一个不错的演示页面:https://roger.pub/theme-color-preview/
它在 macOS 12.1 Safari 15.2 中不起作用。但在 iOS Safari 中可以正常工作。
你好!是否可以获得类似黑色半透明的效果?我的网站上基本上有一个动画背景,并且希望它“扩展”到缺口安全区域。
由于某种奇怪的原因,iOS 16.0 中的 Safari 不尊重 theme-color 值是否为 #ffffff。
例如,如果你有一个红色背景的标题,iOS 中的 Safari 会将 theme-color 替换为与顶部元素相同的颜色,而不是使用你在元标签中设置的颜色。
这可能是由于 Safari 中存在禁止的颜色。请参阅:https://css-tricks.cn/meta-theme-color-and-trickery/#aa-prohibited-colors
很棒的概述,Manuel!
是否有任何可行的(符合规范的)主题颜色方法,可以在浅色和深色模式下都工作?据我所知,CSS 解决方案还没有取得进展?
更新:似乎符合性问题是 HTML 验证程序中的误报。这并没有使浅色和深色模式支持从可维护性的角度来看更有吸引力,但至少它不是虚构的 HTML。
抱歉回复晚了,感谢你的回答。我之前不知道!:)