得益于 Web 标准的发展和设计风格的变化,前端开发者从通过切图还原设计逐渐变为通过代码还原设计。CSS 预处理器也在一定程度上弥补了 CSS 本身表达能力的不足,许多 UI 框架(比如 Element)将基础的颜色值作为配置项供使用者定制,其余的颜色则在它们的基础上调整亮度/饱和度,或者与其他颜色混合而成。虽说做不到一键变色,但是通过重新构建来改变整个网站的配色是没有问题的。
除了可定制,这样做还可以让代码变得更容易维护。相比较充斥着各种颜色值的 CSS 代码,甚至可以表达出一些配色思路。
配色思路?
下面这段样式是从七牛的管理控制台中摘抄的:
代码语言:javascript复制.btn.btn-primary {
color: #1989fa;
background-color: rgba(25,137,250,.04);
border-color: rgba(25,137,250,.4)}
心算一下(Shift 左击),就能发现上面的三个颜色是同一种颜色,只是透明度不同。
由于页面的背景是纯白的,因此调整颜色透明度可以看成是在调整颜色的亮度。按钮虽然只用了「一种颜色」,但是看起来还是比较和谐的。
从中可以看出对主按钮常规状态的设计思路是:
- 使用 #1989fa 作为基础颜色;
- 文字颜色使用基础颜色;
- 将基础颜色调亮 96% 作为背景色;
- 将基础颜色调亮 60% 作为边框的颜色。
预处理器?
使用像 Sass 这样的预处理器很容易实现上面的需求:
代码语言:javascript复制$primary-color: #1989fa;$text-color: $primary-color;$background-color: scale-color($primary-color, $lightness: 96%);$border-color: scale-color($primary-color, $lightness: 60%);.btn-primary {
color: $text-color;
background-color: $background-color;
border-color: $border-color;}
Sass 会在编译期间计算出表达式的值,生成这样的 CSS 代码:
代码语言:javascript复制.btn-primary {
color: #1989fa;
background-color: #f6faff;
border-color: #a3d0fd;}
不过,使用预编译器就意味着需要构建——总有一些人不喜欢「构建」过程,或者倾向于使用更「原生」的解决方案。
那么,使用纯 CSS 可以在一定程度上实现这样的效果吗?答案是肯定的,七牛管理控制台的例子中就用了透明度来实现提升亮度的效果。问题在于,其中的颜色值出现了多次,可维护性还是不高。
CSS 变量
CSS 变量是一项实验中的技术,不过现代浏览器大多都已经支持了,所以如果你的网站面向的用户使用的基本都是现代浏览器,可以考虑使用这项技术。后文尝试使用这项技术来描述 UI 的配色,编写更容易维护的纯 CSS。
配色
在 Adobe Color CC 上最受欢迎的颜色主题里挑了个顺眼的,就可以开始配色了。有了颜色主题,配色会容易一些,只需要选 3 ~ 4 种颜色,就可以配出一个不错的 UI 了。
背景色和文字颜色
为了确保可读性,只要选出反差和亮度差最大的两种颜色即可。在这个颜色主题里,自然是前两个偏黑白的 #323a40 和 #e5eef4 了。想做一个暗色的配色,因此选择前者为背景色,后者为文字颜色。
代码语言:javascript复制:root {
--background-color: #323a40;
--text-color: #e5eef4;}
CSS 变量以两个连字符开头,定义 CSS 变量与设置属性类似。上面这段代码定义了 --background-color 和 --text-color 这两个 CSS 变量。:root 选择器会选择根节点(也就是 <html>),与 html 的区别在于优先级更高,适合用于定义全局 CSS 变量。
代码语言:javascript复制html {
background: var(--background-color);
color: var(--text-color);}
要引用定义的 CSS 变量也很简单,只需要使用 var 函数即可。这样,页面的背景色和文字颜色就设置好了。
在 JSFiddle 上 DIY
主色
然后,选择一个主色。主色通常被用在超链接、主按钮、logo 上。为了它们更突出,应该选择一个与背景色和文字颜色都有一定反差的颜色。这里,我选择颜色主题中的第三个颜色 #37b0c0。
代码语言:javascript复制:root {
--background-color: #323a40;
--text-color: #e5eef4;
--primary-color: #37b0c0;
--input-size: 30px;
--input-padding-horizontal: 10px;
--button-border-radius: 4px;}button {
background: none;
border: 1px solid var(--primary-color);
border-radius: var(--button-border-radius);
color: var(--primary-color);
height: var(--input-size);
padding-left: var(--input-padding-horizontal);
padding-right: var(--input-padding-horizontal);
transition: all .15s ease;}button:hover {
background: var(--primary-color);
color: var(--background-color);
cursor: pointer;}
CSS 变量不仅可以定义颜色值,上面的代码还用 CSS 变量定义了按钮的大小、内边距和边框的半径。
在 JSFiddle 上 DIY
透明度
CSS 里并没有像 Sass 里 darken、lighten 那样的颜色函数,可以考虑使用透明度在一定程度上实现加深或者减淡的效果。不幸的是,CSS 里同样也没有操作颜色透明度的函数。我们只能把颜色的三个分量拆开定义:
代码语言:javascript复制:root {
--background-color-r: 51;
--background-color-g: 59;
--background-color-b: 64;
--background-color: rgb(
var(--background-color-r),
var(--background-color-g),
var(--background-color-b)
);
--text-color-r: 229;
--text-color-g: 238;
--text-color-b: 244;
--text-color: rgb(
var(--text-color-r),
var(--text-color-g),
var(--text-color-b)
);
--primary-color-r: 62;
--primary-color-g: 176;
--primary-color-b: 190;
--primary-color: rgb(
var(--primary-color-r),
var(--primary-color-g),
var(--primary-color-b)
);
--input-size: 30px;
--input-padding-horizontal: 10px;
--button-border-radius: 4px;}
是的,这么定义很麻烦。不过,每个颜色值还是只会出现一次。
代码语言:javascript复制input, button {
--border-color: rgba(
var(--text-color-r),
var(--text-color-g),
var(--text-color-b),
var(--border-color-alpha, .3)
);
border: 1px solid var(--border-color);}
其中,var(--border-color-alpha, .3) 表示引用 --border-color-alpha 变量的值,如果变量没有定义或者无效,则回退到 .3。这样一来,input 和 button 的边框颜色会变成背景色混合 30% 的文本颜色。
代码语言:javascript复制input:focus {
--border-color-alpha: .6;}
当焦点在 input 上时,--border-color-alpha 的值将变为 .6,此时边框颜色会变成背景色混合 60% 的文本颜色。
我使用同样的方法写了一个友好的 header。
在 JSFiddle 上 DIY
白天主题
产品经理找到我说,大多数程序员都觉得我做的页面很友好,但是少数非夜猫子程序员觉得这个主题在白天太刺眼了,希望能有一个「白天主题」。
好在 JavaScript 可以设置 CSS 变量的值,而白天主题只需要把背景颜色和文字颜色互换就可以了。
代码语言:javascript复制const themes = [
{
name: 'dark',
scheme: {
'--background-color-r': 51,
'--background-color-g': 59,
'--background-color-b': 64,
'--text-color-r': 229,
'--text-color-g': 238,
'--text-color-b': 244
}
},
{
name: 'light',
scheme: {
'--background-color-r': 229,
'--background-color-g': 238,
'--background-color-b': 244,
'--text-color-r': 51,
'--text-color-g': 59,
'--text-color-b': 64
}
}];let currentTheme = 0;window.nextTheme = function () {
currentTheme = (currentTheme 1) % themes.length;
const theme = themes[currentTheme];
Object.keys(theme.scheme).forEach(name => {
const value = theme.scheme[name];
document.documentElement.style.setProperty(name, value);
});}
在 JSFiddle 上 DIY
颜色混合
透明度不能解决所有问题,如果需要和另一种颜色混合(单纯与黑白混合可以考虑使用 HSL 模型),或者需要渐变,就只能使用一些「黑科技」了。
比如说,想把背景颜色设置为 50% 文字颜色 50% 主色:
代码语言:javascript复制... {
--base-color: var(--text-color);
--mix-color: rgba(
var(--primary-color-r),
var(--primary-color-g),
var(--primary-color-b),
.5
);
background-color: var(--base-color);
background-image: linear-gradient(
to bottom,
var(--mix-color),
var(--mix-color)
);}
已知问题
除了用起来不如 CSS 预处理器方便之外,Safari 在某些情况下无法工作。比如说
代码语言:javascript复制:root {
--r: 255;
--g: 0;
--b: 0;}.foo {
border: 10px solid
rgba(var(--r), var(--g), var(--b), .5);}
在 Safari 下边框会被渲染为 currentColor 而不是半透明的红色。
解决方法很简单,在内部多定义一个 CSS 变量即可。
在 JSFiddle 上 DIY(请对比在 Chrome 中和 Safari 中的表现)
本文作者:lujjjh
原文链接:https://zhuanlan.zhihu.com/p/29610065