覆盖内部作用域中的 :root CSS 变量
Overriding :root CSS variables from inner scopes
在 Stack Overflow 的 design system 中,我们使用 Less 编译 CSS 颜色值。
我们有像 @orange-500
这样的全局 Less 变量,它们经常针对悬停状态、建筑边框样式、背景颜色等进行修改。
在Less中,这写成darken(@orange-500, 5%)
。我正在尝试使用本机 CSS 变量实现类似的效果。切换到 CSS 变量将使我们能够更快地发布依赖于主题的功能(Stack Exchange Network、暗模式等),CSS、while 在媒体查询(高对比度、暗模式等)上启用交换变量。
当变量范围限定为 CSS class:
时,这个覆盖 hsl
颜色亮度值的示例有效
.card {
--orange: hsl(255, 72%, var(--lightness, 68%));
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
然而,我们需要在一个单一的、可交换的地方全局指定我们的颜色变量以支持全局主题,但这并不像预期的那样工作:
:root {
--orange: hsl(255, 72%, var(--lightness, 68%));
}
.card {
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
我试过从 :root
切换到 html
或 body
,但没有成功。对此有任何解决方法吗?
我有兴趣了解是否有比此解决方案更理想的解决方案,但作为一种可能的解决方法,您可以进一步分解 CSS 变量并在元素样式定义中构建值,例如所以:
:root {
--orangeColor: 37,72%;
--redColor: 1,72%;
--blueColor: 215,72%;
--greenColor: 126,72%;
--LumDefault: 68%;
--LumDark: 45%;
--LumLight: 80%;
}
.card {
background: hsl(var(--orangeColor), var(--LumDefault));
}
.card:hover {
background: hsl(var(--orangeColor), var(--LumDark));
}
.card:active {
background: hsl(var(--redColor), var(--LumDark));
color: hsl(var(--greenColor), var(--LumLight));
}
<div class="card">
Hello world
</div>
我确实意识到这并没有像您想要完成的那样覆盖,但是从您陈述的业务案例来看,它将为您提供一种在全球范围内管理元素的方法...只是需要多做一些工作来定义您的CSS 在前端。
简单的解决方案是将 CSS 变量放入单独的 CSS 文件中,然后根据需要将其换出。例如,支持暗模式的媒体查询可以进行交换,或者您可以使用 JavaScript、预烘焙主题等。
这样做的好处是用您的变量定义交换 CSS 文件会实时更改 CSS 渲染。
假设您在 light/dark 模式下使用媒体查询。如果浏览器理解并请求 "dark mode" 则只加载第一个文件。但是,如果浏览器不理解这些媒体查询,您的 "default" 就是 light.css,因为两个 CSS 文件都已加载,但 后续规则会覆盖先前的规则.
<link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" href="/light.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)">
<!-- The main stylesheet -->
<link rel="stylesheet" href="/style.css">
在这些样式表中,您想使用 :root
伪 class,因为它与 HTML 基本相同,但在大多数浏览器中具有更高的特异性。
light.css
:root {
--text-color: #333;
--background-color: #fff;
}
dark.css
:root {
--text-color: #dadada;
--background-color: #333;
}
此外,请注意简化变量并在元素内构建完整规则是一个好主意,正如 Travis 在该答案中提到的那样。
style.css(主要样式文档)
body {
color: var(--text-color);
background-color: var(--background-color);
}
附带说明一下,您可能希望 read 在 CSS color-scheme
属性 上获得更好的本机浏览器元素支持。
这是一个范围界定问题。您这样做的方式是从 :root
继承 --orange
,而 :root
中的 --orange
的亮度为 68%。
为了更改它,您需要将 re-scope 变量 --orange
设置为将查找新 --lightness
值的元素。有几种方法可以做到这一点:
选项 1:复制元素上的 --orange
变量:
:root {
--lightness: 68%;
--orange: hsl(255, 72%, var(--lightness));
}
.card {
background: var(--orange);
--orange: hsl(255, 72%, var(--lightness));
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
显然这有点糟糕,因为您将不得不复制那个 --orange
变量。
选项 2:
您可以抽象 --orange
的其他参数,这样它就不会重复。我会喜欢这种方法,尽管它更多的是文本:
:root {
--lightness: 68%;
--orangeHue: 255;
--orangeSat: 72%;
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card {
background: var(--orange);
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
你可以也做的是将其具体限定在可能应用于HTML元素或.darkMode
class body。这也可能是有道理的,因为很清楚代码的意图是什么:
选项 3
:root {
--lightness: 68%;
--orangeHue: 255;
--orangeSat: 72%;
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card {
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
.darkMode .card {
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
<div class="darkMode">
<div class="card">
Hello world
</div>
</div>
不管你怎么走,问题是 --orange
变量继承自其设置 --lightness
的原始范围。将其视为 "inheriting a computed value".
为了让--orange
获得新的亮度,你需要在某个地方有一个新的--orange
。
选项 4
我不确定你的主题模式是什么,但我可以解释一下我是如何自己创建暗模式的 blog . If you look at the CSS你会看到我已经创建了两个完整的主题相同的命名约定:
--themeLightTextColor: rgb(55, 55, 55);
--themeLightBGColor: rgb(255, 255, 255);
--themeLightAccentColor: rgb(248, 248, 248);
--themeLightTrimColor: rgb(238, 238, 238);
--themeDarkTextColor: rgb(220, 220, 220);
--themeDarkBGColor: rgb(23, 23, 23);
--themeDarkAccentColor: rgb(55, 55, 55);
--themeDarkTrimColor: rgb(40, 40, 40);
然后我做的是创建 第三个 变量集,其工作是 "active" 经理:
--themeActiveLinkColor: var(--linkColor);
--themeActiveLinkColorHover: var(--linkColorHover);
--themeActiveTextColor: var(--themeLightTextColor);
--themeActiveEditorialTextColor: var(--themeLightPltNLow);
--themeActiveBGColor: var(--themeLightBGColor);
--themeActiveAccentColor: var(--themeLightAccentColor);
--themeActiveTrimColor: var(--themeLightTrimColor);
然后,我将活动主题设置的范围限定在单个 class:
.theme--dark {
--themeActiveTextColor: var(--themeDarkTextColor);
--themeActiveEditorialTextColor: var(--themeDarkPltNLow);
--themeActiveBGColor: var(--themeDarkBGColor);
--themeActiveAccentColor: var(--themeDarkAccentColor);
--themeActiveTrimColor: var(--themeDarkTrimColor);
}
您的意图似乎是不必明确声明主题,而是调整一些 "root variables" 来调整它。但我建议您可能有一个模式,其中单个 class 可以更改活动主题。这种模式的优点是您还可以调整 class 名称上的任何 "root variables"。
在 Stack Overflow 的 design system 中,我们使用 Less 编译 CSS 颜色值。
我们有像 @orange-500
这样的全局 Less 变量,它们经常针对悬停状态、建筑边框样式、背景颜色等进行修改。
在Less中,这写成darken(@orange-500, 5%)
。我正在尝试使用本机 CSS 变量实现类似的效果。切换到 CSS 变量将使我们能够更快地发布依赖于主题的功能(Stack Exchange Network、暗模式等),CSS、while 在媒体查询(高对比度、暗模式等)上启用交换变量。
当变量范围限定为 CSS class:
时,这个覆盖hsl
颜色亮度值的示例有效
.card {
--orange: hsl(255, 72%, var(--lightness, 68%));
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
然而,我们需要在一个单一的、可交换的地方全局指定我们的颜色变量以支持全局主题,但这并不像预期的那样工作:
:root {
--orange: hsl(255, 72%, var(--lightness, 68%));
}
.card {
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
我试过从 :root
切换到 html
或 body
,但没有成功。对此有任何解决方法吗?
我有兴趣了解是否有比此解决方案更理想的解决方案,但作为一种可能的解决方法,您可以进一步分解 CSS 变量并在元素样式定义中构建值,例如所以:
:root {
--orangeColor: 37,72%;
--redColor: 1,72%;
--blueColor: 215,72%;
--greenColor: 126,72%;
--LumDefault: 68%;
--LumDark: 45%;
--LumLight: 80%;
}
.card {
background: hsl(var(--orangeColor), var(--LumDefault));
}
.card:hover {
background: hsl(var(--orangeColor), var(--LumDark));
}
.card:active {
background: hsl(var(--redColor), var(--LumDark));
color: hsl(var(--greenColor), var(--LumLight));
}
<div class="card">
Hello world
</div>
我确实意识到这并没有像您想要完成的那样覆盖,但是从您陈述的业务案例来看,它将为您提供一种在全球范围内管理元素的方法...只是需要多做一些工作来定义您的CSS 在前端。
简单的解决方案是将 CSS 变量放入单独的 CSS 文件中,然后根据需要将其换出。例如,支持暗模式的媒体查询可以进行交换,或者您可以使用 JavaScript、预烘焙主题等。
这样做的好处是用您的变量定义交换 CSS 文件会实时更改 CSS 渲染。
假设您在 light/dark 模式下使用媒体查询。如果浏览器理解并请求 "dark mode" 则只加载第一个文件。但是,如果浏览器不理解这些媒体查询,您的 "default" 就是 light.css,因为两个 CSS 文件都已加载,但 后续规则会覆盖先前的规则.
<link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" href="/light.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)">
<!-- The main stylesheet -->
<link rel="stylesheet" href="/style.css">
在这些样式表中,您想使用 :root
伪 class,因为它与 HTML 基本相同,但在大多数浏览器中具有更高的特异性。
light.css
:root {
--text-color: #333;
--background-color: #fff;
}
dark.css
:root {
--text-color: #dadada;
--background-color: #333;
}
此外,请注意简化变量并在元素内构建完整规则是一个好主意,正如 Travis 在该答案中提到的那样。
style.css(主要样式文档)
body {
color: var(--text-color);
background-color: var(--background-color);
}
附带说明一下,您可能希望 read 在 CSS color-scheme
属性 上获得更好的本机浏览器元素支持。
这是一个范围界定问题。您这样做的方式是从 :root
继承 --orange
,而 :root
中的 --orange
的亮度为 68%。
为了更改它,您需要将 re-scope 变量 --orange
设置为将查找新 --lightness
值的元素。有几种方法可以做到这一点:
选项 1:复制元素上的 --orange
变量:
:root {
--lightness: 68%;
--orange: hsl(255, 72%, var(--lightness));
}
.card {
background: var(--orange);
--orange: hsl(255, 72%, var(--lightness));
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
显然这有点糟糕,因为您将不得不复制那个 --orange
变量。
选项 2:
您可以抽象 --orange
的其他参数,这样它就不会重复。我会喜欢这种方法,尽管它更多的是文本:
:root {
--lightness: 68%;
--orangeHue: 255;
--orangeSat: 72%;
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card {
background: var(--orange);
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card:hover {
--lightness: 45%;
}
<div class="card">
Hello world
</div>
你可以也做的是将其具体限定在可能应用于HTML元素或.darkMode
class body。这也可能是有道理的,因为很清楚代码的意图是什么:
选项 3
:root {
--lightness: 68%;
--orangeHue: 255;
--orangeSat: 72%;
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
.card {
background: var(--orange);
}
.card:hover {
--lightness: 45%;
}
.darkMode .card {
--orange: hsl(var(--orangeHue), var(--orangeSat), var(--lightness));
}
<div class="darkMode">
<div class="card">
Hello world
</div>
</div>
不管你怎么走,问题是 --orange
变量继承自其设置 --lightness
的原始范围。将其视为 "inheriting a computed value".
为了让--orange
获得新的亮度,你需要在某个地方有一个新的--orange
。
选项 4
我不确定你的主题模式是什么,但我可以解释一下我是如何自己创建暗模式的 blog . If you look at the CSS你会看到我已经创建了两个完整的主题相同的命名约定:
--themeLightTextColor: rgb(55, 55, 55);
--themeLightBGColor: rgb(255, 255, 255);
--themeLightAccentColor: rgb(248, 248, 248);
--themeLightTrimColor: rgb(238, 238, 238);
--themeDarkTextColor: rgb(220, 220, 220);
--themeDarkBGColor: rgb(23, 23, 23);
--themeDarkAccentColor: rgb(55, 55, 55);
--themeDarkTrimColor: rgb(40, 40, 40);
然后我做的是创建 第三个 变量集,其工作是 "active" 经理:
--themeActiveLinkColor: var(--linkColor);
--themeActiveLinkColorHover: var(--linkColorHover);
--themeActiveTextColor: var(--themeLightTextColor);
--themeActiveEditorialTextColor: var(--themeLightPltNLow);
--themeActiveBGColor: var(--themeLightBGColor);
--themeActiveAccentColor: var(--themeLightAccentColor);
--themeActiveTrimColor: var(--themeLightTrimColor);
然后,我将活动主题设置的范围限定在单个 class:
.theme--dark {
--themeActiveTextColor: var(--themeDarkTextColor);
--themeActiveEditorialTextColor: var(--themeDarkPltNLow);
--themeActiveBGColor: var(--themeDarkBGColor);
--themeActiveAccentColor: var(--themeDarkAccentColor);
--themeActiveTrimColor: var(--themeDarkTrimColor);
}
您的意图似乎是不必明确声明主题,而是调整一些 "root variables" 来调整它。但我建议您可能有一个模式,其中单个 class 可以更改活动主题。这种模式的优点是您还可以调整 class 名称上的任何 "root variables"。