更新主题和插件以支持自动深色模式

以前,Discourse 中的所有颜色都存储为 SCSS 变量。为了支持自动深色模式颜色方案切换,我们将核心中的这些颜色转换为自定义 CSS 属性。您现在可以在检查器中轻松查看完整列表:

主题和插件需要在样式表中使用的所有 $color SCSS 变量切换到 --color CSS 属性的等效项。在大多数情况下,这是一个简单的查找和替换任务:

-   background-color: $primary-very-low;
+   background-color: var(--primary-very-low);

但有些情况下,主题或插件使用了更复杂的颜色变体,例如使用 SCSS 颜色函数进行加深或变亮。这些情况需要更复杂的重构,为此我们增加了在主题和插件中扩展颜色定义的能力。

在插件中

discourse-encrypt 插件中的此提交是一个良好且简单的重构示例。它将 mix($color1, $color2) SCSS 声明移动到一个单独的文件中,并将其存储为 CSS 自定义属性。然后,新文件被注册为 :color_definitions 资源,以确保新声明的颜色属性包含在颜色定义样式表中。

在主题中

在主题中,您可以通过在 common/color_definitions.scss 样式表中声明 CSS 自定义属性来完成相同的操作。您可以查看此 graceful 主题中的提交以获取示例。

一些额外的注意事项

  • 当使用 rgba($color, 0.5) 函数通过透明颜色时,SCSS 接受 HEX 和 RGB 颜色作为第一个参数,而 CSS 自定义属性只接受 RGB 颜色。这就是我们引入 hexToRGB() 帮助函数以及一些带有 --rgb 后缀的属性的原因。一个例子:
// color_definitions.scss
:root {
  --primary: #{$primary};
  --primary-rgb: #{hexToRGB($primary)};
}

// other stylesheet
.element {
  background-color: rgba(var(--primary-rgb), 0.05);
}
  • 请注意,在上面的代码片段中,SCSS 变量在传递给自定义属性时被插值。这是 SCSS 的要求,更多详情请参阅 https://sass-lang.com/documentation/style-rules/declarations。
  • CSS 的 var() 声明可以为第一个值提供一个备用值(即第二个值),如果第一个值不可用,如在编写 var(--color1, red) 时,如果找不到 --color1 属性,CSS 将回退到红色。在插件中,我们使用 SCSS 颜色变量作为后备,以确保与早期版本的 Discourse 兼容。因此,前面的示例,如果带有后备,将如下所示:
-   background-color: $primary-very-low;
+   background-color: var(--primary-very-low, $primary-very-low);

本文档是版本控制的 - 在 github 上建议更改。

24 个赞

I’m not very good at this stuff and it’ll take me a while to figure this out myself. . . Does this mean that all themes that referred to colors before are now going to be broken?

6 个赞

No, not at all. SCSS variables in themes will continue to work for a long time.

But any colors outputted via SCSS variables will stay static, i.e. they cannot be dynamically switched to a new color scheme when a browser goes from normal to dark mode. So those themes/plugins will continue to work, they just won’t be compatible with automatic dark mode switching.

13 个赞

Thanks for the instructions. Is there a way to also change a background image depending on dark/light mode? (I’ve used the theme switcher component to do that.) Would a CSS class indicating the mode be possible?

2 个赞

Great question, I tried this and noticed that we didn’t properly support using background images or theme variables in the special color definitions stylesheet. So I made some fixes in core, and you should be able to do this now (make sure you pull the latest core).

So, if you have two images in your theme or theme component, with SCSS vars of $bg-light and $bg-dark respectively, you can add this to your color_definitions.scss stylesheet:


$bg: url(dark-light-choose($bg-light, $bg-dark));

:root {
  --custom-bg: #{$bg};
}

And then you can use var(--custom-bg) in your regular stylesheet.

8 个赞

For an image, all you need to do is the vanilla prefers-dark-theme CSS media query.

That won’t work well in all cases, because the media query is not aware of the user’s preferences. Users can disable auto-dark-mode switching, but the media query won’t be aware of that, and it will result in the background meant for the dark color scheme being rendered.

4 个赞

Discourse 是否也可以为 <body> 添加一个 CSS 类来表示配色方案或配色方案 ID?这似乎会容易得多。

我正在尝试解决主题转换中的一个问题,我需要大量的不同的 CSS 规则和变量,这在 color_definitions.scss 文件中变成了一个复杂的混乱。

如果我能在主题中的一个隔离的 SCSS 文件中做到这一点,那么用 color_definitions.scss 来解决一个需要很长时间才能弄清楚的问题,只需要 5 分钟:

body.dark-palette .some-thing {
  // 一些样式
}

body.light-palette .some-thing {
  // 一些样式
}

是的,您可能会在颜色定义文件中遇到许多代码行,尤其是在处理渐变时。

在我看来,这仍然是最合适的位置。它确实远离了使用它的元素,但它是一个方便的单一位置,可以处理基于浅色/深色模式切换的颜色/渐变。

另一种方法是使用类似这样的方法:

@container style(--scheme-type: light){
  body{
    background: red;
  }
}

我们的颜色方案带有一个 --scheme-type 属性,浅色方案为 light,深色方案为 dark。最近的浏览器支持容器查询,因此这应该可以在不向页面主体添加类的情况下完成您在此处尝试完成的任务。

我的 CSS/SCSS 技能不是很好。也许对于精通这些技术的人来说会更容易。

它弄乱了 color_definitions.scss 文件,所以我把它移到了 scss/ 中的另一个文件中,这样我就可以导入它了。我不确定该如何命名,所以它看起来是这样的。

我认为使用 body 类(或者更多示例)会更容易,但无论如何,它目前是有效的。我不知道容器查询是如何工作的,但我以后会研究它。

请注意,截至今天(2025/11/14),Firefox 不支持 自定义属性的样式查询