EN
Shopify 知识库 · 指南

Dawn 色彩方案:Liquid 把商家配置编译成 CSS 变量

基于 Dawn v15.3.0 源码,拆解 color_scheme_group 设置如何在 theme.liquid 中循环生成 .color-* 类与 RGB 三元组变量,分区如何套用方案与渐变,服务端如何预先计算对比色,以及这种设计令牌方案的取舍与限制。
历史资料
请结合文中的适用版本和来源阅读。

本文基于 Dawn v15.3.0,源码固定到 ecb06c3(核验于 2026-10-01)。结论限于此提交;本篇为静态源码分析,未完成店铺运行验证。Dawn 上游已发布 v16.0.0,本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对。

商家在主题编辑器里可以定义若干套配色,每个分区再选用其中一套。Dawn 的做法是:布局在服务端把所有方案编译成 CSS 类,分区只需加一个类名。CSS 文件本身不含任何商家颜色。

一、设置:一组字段定义一套方案

settings_schema.json 用 color_scheme_group 类型定义方案的结构,每套方案有 7 个字段:background、background_gradient、text、button、button_label、secondary_button_label、shadow。

同一设置下的 role 把这些字段映射到语义角色,例如 links 对应 secondary_button_label、secondary_button 对应 background、背景同时登记纯色与渐变。平台怎样使用这份映射,本篇未核对官方文档;从主题内部看,这份映射与下文生成变量时的字段复用关系一致。

二、布局:循环生成 .color-<id>

theme.liquid#L57-L100 在 {% style %} 中遍历 settings.color_schemes:

{% for scheme in settings.color_schemes -%}
  {% assign scheme_classes = scheme_classes | append: ', .color-' | append: scheme.id %}
  {% if forloop.index == 1 -%}:root,{%- endif %}
  .color-{{ scheme.id }} {
    --color-background: {{ scheme.settings.background.red }},{{ scheme.settings.background.green }},{{ scheme.settings.background.blue }};
    ...
  }
{% endfor %}

{{ scheme_classes | prepend: 'body' }} {
  color: rgba(var(--color-foreground), 0.75);
  background-color: rgb(var(--color-background));
}

有几个细节:

  • 第一套方案同时写在 :root 上,页面上没有套用任何方案的区域也有默认值。
  • 循环同时累积一个选择器列表,最后生成 body, .color-a, .color-b, ... { color; background-color }。因此任何套用方案的容器都会重设文字色和背景色,嵌套的分区不会继承外层颜色。
  • 变量值是 r,g,b 三元组而不是完整颜色。使用时写成 rgb(var(--color-foreground)) 或 rgba(var(--color-foreground), 0.75),同一个变量可以配合任意透明度。assets/ 下的 CSS 中,rgba(var(--color- 出现在 300 多行里,例如边框、分隔线、次要文字都是前景色加不同透明度,商家只需配置一个文字色。
  • 字段被复用为多个变量。--color-secondary-button 取背景色,--color-link 取次要按钮文字色,徽章的三个变量全部来自文字色与背景色(#L87-L92)。设置项保持精简,代价是这些元素无法单独配色。

三、服务端预先算对比色

assign background_color_brightness = background_color | color_brightness
if background_color_brightness <= 26
  assign background_color_contrast = background_color | color_lighten: 50
elsif background_color_brightness <= 65
  assign background_color_contrast = background_color | color_lighten: 5
else
  assign background_color_contrast = background_color | color_darken: 25
endif

(theme.liquid#L70-L80)

--color-background-contrast 用于需要与背景区分开的元素。计算由 Liquid 颜色过滤器在渲染时完成,浏览器不需要运行任何颜色计算,也不依赖 color-mix() 等较新的 CSS 特性。阈值 26、65 与调整幅度是 Dawn 自定的经验值,源码中没有说明依据。

四、分区:类名加 gradient

分区在根元素上写 color-{{ section.settings.color_scheme }} gradient,例如 rich-text.liquid#L18;sections/ 中共有 27 个文件使用这种写法。

渐变通过一个变量切换:方案设置了渐变时 --gradient-background 取渐变值,否则取纯色(theme.liquid#L64-L68)。.gradient 类先写一行纯色 rgb(var(--color-background)) 作为回退,再写 background: var(--gradient-background)(base.css#L2934-L2937)。CSS 不需要分支判断。

方案是纯 CSS 类,所以也能被 JS 搬运。快速加购弹窗从商品页响应里取出 product-info 后,把它身上的 color-* 和 gradient 类复制到弹窗容器(quick-add.js#L51-L55),弹窗就沿用了商品分区的配色。

取舍与限制

方面现状
渲染成本每个页面都输出全部方案的 CSS,方案越多,内联样式越长;方案数量很少时可以忽略
颜色格式只存 RGB 三元组,没有 alpha 字段,也无法直接用于 color-mix() 等需要完整颜色值的函数
交互状态方案中没有悬停、按下等状态色,组件 CSS 用透明度或阴影模拟
对比度只计算了背景对比色;文字与背景、按钮文字与按钮之间的对比度不做校验,商家可以配出难以阅读的组合
暗色模式全仓库未找到 prefers-color-scheme,系统暗色模式不会切换方案

迁移到自己主题时

这套写法可以原样用于 Tailwind 或 DaisyUI 主题:Liquid 循环生成 .color-<id> 和变量,CSS 框架的颜色令牌引用这些变量即可。需要透明度时,三元组比完整颜色更方便;如果项目以 color-mix() 为主,则改存完整颜色值。建议同时补上 Dawn 缺少的两项:在 Liquid 中用 color_contrast 等过滤器计算对比度并给出提示,以及为悬停、焦点状态生成派生变量。

建议的验证范围

本篇均未执行:在编辑器中新增第三套方案,查看生成的 CSS;把方案的背景分别设为极暗、中等和明亮的颜色,核对 --color-background-contrast 的取值;把某个分区设为渐变背景,确认 .gradient 生效。

源码基线