如何使用 CSS 自定义属性配置应用程序配色方案
已发表: 2022-03-10变量是帮助组织项目颜色的基本工具。 长期以来,前端工程师使用预处理器变量来配置项目的颜色。 但现在,许多开发人员更喜欢组织颜色变量的现代原生机制:CSS 自定义属性。 与预处理器变量相比,它们最重要的优势是它们实时工作,而不是在项目的编译阶段,并且支持级联模型,允许您即时使用继承和重新定义值。
当您尝试组织应用程序配色方案时,您始终可以将所有与颜色相关的自定义属性放在根部分,命名它们,并在所有需要的地方使用它。
这是一个选项,但它是否可以帮助您解决应用程序主题化、白标、品牌更新或组织明暗模式的问题? 如果您需要调整配色方案以增加对比度怎么办? 使用当前方法,您将不得不更新变量中的每个值。
在本文中,我想就如何使用自定义属性来拆分颜色变量提出一种更灵活、更可靠的方法,它可以解决许多此类问题。
设置调色板
任何网站的着色都始于配色方案的设置。 这种方案基于色轮。 通常,只有少数原色构成调色板的基础,其余的都是派生色——色调和中间色调。 大多数情况下,调色板是静态的,并且在 Web 应用程序运行时不会更改。
根据色彩理论,配色方案只有几种选择:
- 单色方案(一种原色)
- 互补方案(两种原色)
- 三色方案(三基色)
- 四色方案(四基色)
- 相邻图案(二或三基色)
对于我的示例,我将使用 Paletton 服务生成三元组配色方案:
我现在有三种主要颜色。 在这些基础上,我将计算色调和中间色调(HSL 格式结合calc
函数是一个非常有用的工具)。 通过更改亮度值,我可以为调色板生成几种额外的颜色。
现在,如果修改了调色板,则只需要更改原色的值。 其余的将自动重新计算。
如果您更喜欢 HEX 或 RGB 格式,那没关系; 可以在编译项目的阶段使用预处理器的相应功能(例如,使用 SCSS 和color-adjust
功能)形成调色板。 正如我之前提到的,这一层大部分是静态的。 在正在运行的应用程序中更改调色板的情况极为罕见。 这就是为什么我们可以使用预处理器来计算它。
注意:我建议同时为每种颜色生成 HEX 文字和 RGB。 这将允许在未来使用 Alpha 通道。
调色板级别是唯一将颜色直接编码在变量名称中的级别,即我们可以通过读取名称来唯一地识别颜色。
定义主题或功能颜色
一旦调色板完成,下一步就是功能颜色的级别。 在这个层面上,颜色的价值并不像它的用途、它执行的功能以及它究竟着色什么那么重要。 例如,主要或应用品牌颜色、边框颜色、深色背景上的文本颜色、浅色背景上的文本颜色、按钮背景颜色、链接颜色、悬停链接颜色、提示文本颜色等.
对于几乎任何网站或应用程序来说,这些都是极其常见的事情。 可以说,这些颜色负责应用程序的某个颜色主题。 此外,这些变量的值严格取自调色板。 因此,我们可以通过简单地使用不同的调色板来轻松更改应用程序主题。
下面,我创建了三个典型的 UI 控件:按钮、链接和输入字段。 它们使用包含我之前在上面生成的调色板中的值的函数变量进行着色。 负责应用程序主题(条件品牌)的主要功能变量是原色变量。
使用顶部的三个按钮,您可以切换主题(更改控件的品牌颜色)。 通过使用适当的 CSSOM API (setProperty) 进行更改。
这种方法不仅对主题化很方便,而且对配置单个网页也很方便。 例如,在 zubry.by 网站上,我使用了一个通用样式表和一个函数变量--page-color
来为所有页面的徽标、标题、控件和文本选择着色。 并且在每个页面的自己的样式中,我只是重新定义了这个变量来将页面设置为它自己的原色。
使用组件颜色
大型 Web 项目总是包含分解; 我们将所有内容拆分为小组件,并在许多地方重复使用它们。 每个组件通常都有自己的样式,这意味着我们使用什么来分解 BEM 或 CSS 模块或其他方法都无关紧要; 重要的是,每段这样的代码都可以称为本地范围并重用。
一般来说,我在两种情况下看到在组件级别使用颜色变量的意义。
第一种是根据应用风格指南重复使用不同设置的组件,例如用于不同需求的按钮,如主要(品牌)按钮、次要按钮、第三级等。
第二种是当组件具有多种不同颜色的状态时,例如按钮悬停、活动和焦点状态; 输入或选择字段的正常和无效状态,等等。
组件变量可能派上用场的更罕见的情况是“白标”的功能。 “白标”是一项服务功能,允许用户自定义或标记用户界面的某些部分,以改善与客户交互的体验。 例如,用户通过服务或电子邮件模板与其客户共享的电子文档。 在这种情况下,组件级别的变量将有助于将某些组件与应用程序的其余颜色主题分开配置。
在下面的示例中,我现在添加了用于自定义主(品牌)按钮颜色的控件。 使用组件级别的颜色变量,我们可以彼此分开配置 UI 控件。
如何确定变量的级别?
我遇到了一个问题,即如何理解可以在根(主题或功能级别)中放置什么,以及在组件级别留下什么。 这是一个很好的问题,如果没有看到您正在处理的情况,很难回答。
不幸的是,与编程相同的方法不适用于颜色和样式,如果我们看到三段相同的代码,那么我们需要对其进行重构。
颜色可以从一个组件到另一个组件重复,但这并不意味着它是一个规则。 这些组件之间不能有任何关系。 例如,输入字段的边框和主按钮的背景。 是的,在我上面的例子中就是这种情况,但让我们看看下面的例子:
深灰色重复——这是输入字段的边框、关闭图标的填充颜色和辅助按钮的背景。 但是这些组件之间没有任何联系。 如果输入字段的边框颜色发生变化,那么我们不会更改辅助按钮的背景。 对于这种情况,我们必须只保留调色板中的变量。
绿色呢? 我们可以明确将其定义为主色或品牌色,最有可能的是,如果主按钮的颜色发生变化,那么第一级的链接和标题的颜色也会发生变化。
红色呢? 输入字段的无效状态、错误消息和破坏性按钮在整个应用程序级别将具有相同的颜色。 这是一种模式。 现在我可以在根部分定义几个常用的函数变量:
关于组件颜色的级别,我们可以轻松识别可以使用自定义属性进行自定义的组件。
按钮以不同的设置重复,不同用例的背景颜色和文本发生变化——主要、次要、第三、破坏性或负面案例。
输入字段有两种状态——不正确和正常,其中背景和边框颜色不同。 因此,让我们将这些设置放入相应组件级别的颜色变量中。
对于其余的组件,没有必要定义局部颜色变量,这将是多余的。
您需要深入研究项目的模式语言,这可能是由设计团队和 UX 开发的。 工程师必须充分理解视觉语言的整个概念,然后我们才能确定什么是通用的,应该生活在功能层面上,什么应该保持在本地可见范围内。
但一切都没有那么复杂,有明显的东西。 页面的一般背景、正文的背景和颜色,在大多数情况下,这就是设置应用程序主题的内容。 收集负责配置特定模式(如暗模式或亮模式)的此类内容非常方便。
为什么不把所有东西都放在根部分?
我有过这样的经历。 在 Lition 项目中,我和团队面临这样一个事实,即我们需要支持 IE11 的 Web 应用程序,而不是网站和登陆。 项目之间使用了一个通用的 UI Kit,我们决定将所有变量放在根目录中,这将允许我们在任何级别重新定义它们。
对于 Web 应用程序和 IE11 案例,同样采用这种方法,我们只需通过以下后处理器插件传递代码,并将这些变量转换为项目中所有 UI 组件的文字。 仅当所有变量都在根部分中定义时,此技巧才有可能,因为后处理器无法理解级联模型的细节。
现在我明白这不是正确的方法。 首先,如果您将组件颜色放入根部分,那么您就打破了关注点分离原则。 因此,您最终可能会在样式表中出现多余的 CSS。 例如,您有一个组件文件夹,其中每个组件都有自己的样式。 您还有一个通用样式表,您可以在其中描述根部分中的颜色变量。 您决定移除按钮组件; 在这种情况下,您必须记住还要从通用样式文件中删除与按钮关联的变量。
其次,就性能而言,这不是最佳解决方案。 是的,颜色变化只会导致重新绘制的过程,而不是回流/布局,这本身并不太昂贵,但是当您在最高级别进行一些更改时,您将使用更多的资源来检查整个树而不是这些变化是在一个小的局部区域。 我建议阅读 Lisi Linhart 的 CSS 变量性能基准以了解更多详细信息。
在我当前的项目 Tispr 中,我和团队使用 split 并且不会将所有内容都转储到根目录中,在较高级别上只有调色板和功能颜色。 还有,我们也不怕IE11,因为这个问题是通过对应的polyfill解决的。 只需安装 npm 模块 ie11-custom-properties 并将库导入您的应用程序 JS 包:
// Use ES6 syntax import "ie11-custom-properties"; // or CommonJS require('ie11-custom-properties');
或者通过脚本标签添加模块:
<script async src="./node_modules/ie11-custom-properties/ie11CustomProperties.js">
此外,您可以通过 CDN 添加不带 npm 的库。 这个 polyfill 的工作基于 IE11 对自定义属性的支持最少的事实,其中可以基于级联定义和读取属性。 这对于以双破折号开头的属性是不可能的,但可能是单破折号(类似于供应商前缀的机制)。 您可以在存储库文档中阅读有关此内容的更多信息,并了解一些限制。 其他浏览器将忽略此 polyfill。
下面是 Tispr Web 应用程序的调色板以及电子文档(例如用户合同、发票或提案)的“白标”功能控件。
为什么不在 JavaScript 端存储颜色变量?
另一个合理的问题:为什么不在 JavaScript 代码中存储调色板和函数变量? 这也可以通过内联样式动态更改和稍后重新定义颜色。 这可能是一种选择,但很可能这种方法不太理想,因为您需要访问某些元素并更改它们的颜色属性。 使用 CSS 变量,您只会更改一个属性,即变量值。
在 JavaScript 中,没有用于处理颜色的本机函数或 API。 在 CSS 颜色模块 5 中,将有很多机会生成派生颜色或以某种方式计算它们。 从未来的角度来看,CSS 自定义属性比 JS 变量更丰富、更灵活。 此外,使用 JS 变量,将不可能在级联中使用继承,这是主要缺点。
结论
将颜色分为三个级别(调色板、功能和组件)可以帮助您在处理项目时更好地适应变化和新要求。 我相信 CSS 自定义属性是组织颜色拆分的正确工具——无论您使用什么样式进行样式设置:纯 CSS、预处理器或 CSS-in-JS 方法。
我是通过自己的经验得出这种方法的,但我并不孤单。 Sara Soueidan 在她的文章中描述了一种类似的方法,她将变量分为全局和组件级别。
我还想建议阅读 Lea Verou 的文章,其中她描述了应用 CSS 变量的可能情况(不仅在颜色方面)。