是否有基于 CSS 自定义属性创建可主题化组件变体的最佳实践?

Are there best practices for creating themeable component variants based on CSS custom properties?

我目前正在与一个开发大量网络组件的团队合作,这些组件应该是客户公司当前和即将开发的网络应用程序的基础。

我希望这些组件支持颜色主题;第一个用例是支持 暗模式 .

当前方法

到目前为止,我对这种方法很满意。

问题

现在我开始添加这些按钮的不同语义版本:.btn-danger.btn-success 等,它们应该使用颜色作为传输这些语义的手段。

可能的解决方案 - 两种不同的选择

这里是我不确定要采用哪种方式来实现这一目标的地方,因为我看到了 2 个备选方案:

  1. 为这些语义创建新的变量,在它们的名字中携带语义:

    :root {
        --bg-color-button-success: var(--color-light-green);
        --text-color-button-success: var(--color-black);
        --border-color-button-success: var(--color-green);
    }
    

    并在按钮样式定义中使用这些:

    .btn {
        background-color: var(--bg-color-button);
        color: var(--text-color-button);
        border-color: var(--border-color-button);
    }
    .btn.btn-success {
        background-color: var(--bg-color-button-success);
        color: var(--text-color-button-success);
        border-color: var(--border-color-button-sucess);
    }
    
    • 优点
      • 您可以通过仅修改两个文件来重新设置整个应用程序的主题。
    • 缺点
      • 这种方法显然会产生大量的变量,因为我们需要为每个需要应用不同颜色的地方定义一个单独的变量。
      • 如果您稍后想要引入其他按钮变体,则必须同时编辑 colors.cssbutton.css
  2. 另一种方法可能是重新定义指定上下文中的现有变量:

    .btn {
        background-color: var(--bg-color-button);
        color: var(--text-color-button);
        border-color: var(--border-color-button);
    }
    .btn.btn-success {
        --bg-color-button: var(--color-light-green);
        --text-color-button: var(--color-black);
        --border-color-button: var(--color-green);
    }
    
    • 优点
      • 很多变量少。
      • 变量只会在实际使用的地方重新定义。
      • 定义新按钮变体比其他方法更容易,因为您只需要编辑 button.css.
    • 缺点
      • 要重新设置组件库的主题,除了 colorpalette.csscolors.css.
      • 之外,您可能还需要触及每个组件的 css 文件

我试图总结两种方法的优缺点。

问题

还有哪些其他原因使您更喜欢其中一种方式?

提示

在最终投票结束前请考虑以下

我知道很多人可能会认为这个问题离题,因为它吸引了自以为是的答案,但我确信这个问题很多 对于许多开发人员来说具有实际意义,因为 css 自定义属性最近才成为事实上的标准,这通常会导致最佳实践的出现缓慢。所以自以为是的答案 以及这些观点的原因 正是我在这里要问的 故意 .

这也绝不是 Best Practices - CSS Theming 的重复,因为该问题来自 2010 年,当时 css 自定义属性不存在。

在我看来,主题系统的优先级要求是容易改变外观。所以你的第一个解决方案适合这种情况。说说它的缺点吧。

This approach obviously results in a myriad of variables, as we need to define a separate variable for each place where we need to apply a different color.

需要更多变量。因为它们有不同的用途。当您重新设计主题时,它会帮助您快速更改外观,而无需查看所有组件文件。你只需要关心它以防万一重新设计主题。所以我觉得不应该算作缺点

If you later want to introduce additional button variations, you'd have to edit both colors.css and button.css.

同样,我不认为这是缺点,因为您只需要在 2 个文件中添加一些额外的代码。而且这些代码不会影响之前存在的任何东西。

但是您忘记提及这种方法的最大缺点。您需要重写 CSS 规则。如果我们使用渐变背景会发生什么?代码将是这样的:

.btn {
    background: linear-gradient(to left, var(--text-color-button-1), var(--text-color-button-2) 50%, var(--text-color-button-3) 75%, var(--text-color-button-4) 75%);
    color: var(--text-color-button);
    border-color: var(--border-color-button);
}
.btn.btn-success {
    background: linear-gradient(to left, var(--text-color-button-success-1), var(--text-color-button-success-2) 50%, var(--text-color-button-success-3) 75%, var(--text-color-button-success-4) 75%);
    color: var(--text-color-button-success);
    border-color: var(--border-color-button-sucess);
}

如果我们想把50%这个数字改成另一个数字,那将是一场噩梦。 因此,让我们将您的两种方法合二为一,解决它们的缺点。

这是给想要快速扫描的人的解决方案:

color.css

--color-red-100: red;
--color-red-500: red;
--color-red-900: red;
--color-green-100: green;
--color-green-500: green;
--color-green-900: green;

--color-bg-button-1: var(--color-red-100);
--color-bg-button-2: var(--color-red-500);
--color-bg-button-3: var(--color-red-900);
--color-bg-button-4: var(--color-red-100);

--color-bg-button-success-1: var(--color-green-100);
--color-bg-button-success-2: var(--color-green-500);
--color-bg-button-success-3: var(--color-green-900);
--color-bg-button-success-4: var(--color-green-100);

button.css

.btn {
    background: linear-gradient(to left, var(--text-color-button-1), var(--text-color-button-2) 50%, var(--text-color-button-3) 75%, var(--text-color-button-4) 75%);
}
.btn.btn-success {
    --text-color-button-1: var(--color-bg-button-success-1);
    --text-color-button-2: var(--color-bg-button-success-2);
    --text-color-button-3: var(--color-bg-button-success-3);
    --text-color-button-4: var(--color-bg-button-success-4);
}

在这种方法中:

  • 通过使用基色变量,如果你只是想调整一些灰色阴影,你只需要改变基色变量,按钮和其他任何东西都会相应地改变.
  • 通过使用范围变量但链接到新变量,您无需重写 CSS 规则,只需在一个文件中编辑变量值即可轻松重新设计主题。
  • 当然,这种方法仍然有您第一个解决方案的两个缺点,但我认为它可以接受更高优先级的要求。

对我来说,最佳实践应该放在特定的上下文中,所以这个解决方案正好适合您的首要任务是轻松重新设计主题。如果您只是想使用 CSS 变量来轻松重用并且没有任何更改变量值的计划,那么应该选择 OP 的第二个解决方案以进行快速编程。