R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。

对 CSS 变量的一点实践

注意:本文发布于 2139 天前,文章中的一些内容可能已经过时。

什么是 CSS 变量

之前我刚接触 CSS 预处理器的时候,被里面的变量功能给吸引了,你可以跟正常的编程语言一样定义变量,里面的值可以是各种各样的东西,例如 2rem 或者 #123456,甚至还可以对变量做一些运算,例如两个数值相加,或者把这个颜色变深 10%

后来无意间听说了 CSS 变量这个概念,于是上网查了一下,这儿的变量其实更相当于预定义的常量,因为 CSS 还不支持很多预处理器中的运算(calc 只能对数值进行运算)。

用法其实很简单,例如我要写一套配色(这也是我网站目前的配色):

/* variables.css */
:root {
    --color-dark-1: #353739;
    --color-dark-2: #242628;
    --color-dark-3: #111517;
    --color-medium: #96999b;
    --color-light-1: #aaacaf;
    --color-light-2: #d6d7d9;
    --color-light-3: #f2f4f6;
    --color-selection: #f57c00;
    --color-blue: #2d7d9a;
    --color-purple: #4a148c;
    --color-shadow: rgba(0, 0, 0, 0.3);
}

接下来页面的 CSS 就可以直接用这些变量了:

/* main.css */
::selection {
    background: var(--color-selection);
    color: var(--color-light-3);
}
nav a:hover {
    background: var(--color-light-3);
    color: var(--color-dark-3);
}

如果我以后想换一套配色,或者切换主题(因为我太懒所以没做这功能),只需要简单的替换一下定义变量的 CSS 文件就好了。

兼容性

Can I Use 的数据来看,除了老牌的 IE、几乎啥也不支持的 Opera Mini 和部分国产毒瘤以外,主流浏览器已经支持的很不错了。所以如果你的受众不是太老土,就可以放心的使用了。

一些奇怪的用法

如果只是写一篇介绍性的文章,那么本文到此已经可以结束了,但我想写一下自己在实践中的一些收获,以及遇到的一些坑。

变量的定义也是规则

这是非常好的一点,也正因如此,对 CSS 变量的定义也符合层叠和优先级。例如我要写一些不同状态的组件:

.input {
    line-height: 24px;
    transition: border-color 0.2s ease-out;
}
.input.default {
    border: 1px solid var(--color-default);
    color: var(--color-default);
}
.input.success {
    border: 1px solid var(--color-success);
    color: var(--color-success);
}
.input.danger {
    border: 1px solid var(--color-danger);
    color: var(--color-danger);
}
.validate-error {
    /* 当然,没事干的时候少加 !important */
    border: 1px solid var(--color-danger) !important;
    color: var(--color-danger) !important;
}

其实如果用 CSS 变量,我们可以完全让颜色定义的规则跟其它的样式规则分离:

/* colors */
.input.default {
    --main-color: var(--color-default);
}
.input.success {
    --main-color: var(--color-success);
}
.input.danger {
    --main-color: var(--color-danger);
}
.validate-error {
    --main-color: var(--color-danger) !important;
}
/* styles */
.input {
    border: 1px solid var(--main-color);
    color: var(--main-color);
    line-height: 24px;
    transition: border-color 0.2s ease-out;
}

与 calc 配合

我网站中的卡片比较特殊:

  1. 为了美观,中间需要有一个 var(--card-gap-size) 的间距;
  2. 正常情况下,一个卡片最窄是 var(--card-min-width)
  3. 在父元素中,一行原本能摆下 x 个卡片,但摆不下第 x+1 个时,这 x 个卡片会自动调整为相同宽度;
  4. 如果继续拉伸窗口使得原本第 x+1 个卡片可以放到这一行,那就变成 x+1 个卡片平分宽度;

因为“能否放 x 个卡片”取决于父元素宽度而不是窗口宽度,因此不好使用媒体查询来区分屏幕宽度,因为父元素本身也有专门的媒体查询规则:大窗口占 80%(还有一个 max-width 限制),小窗口则几乎占满。

其实这种需求用 flex 做是最划算的了:

.flex {
    display: flex;
    flex-wrap: wrap;
}

.flex .card {
    flex: 1 0 var(--card-min-width);
    margin: calc(var(--card-gap-size) / 2);
    box-sizing: border-box;
}

但这样会有个问题,就是最左边卡片的 margin-left 和最右边卡片的 margin-right 不是很好去掉(因为你不太好通过规则选中所有最左边和最右边的卡片),因此可以考虑 Bootstrap 中经常使用的负 margin,这时候也需要 CSS 变量的帮助:

.flex {
    margin-top: calc(0px - var(--card-gap-size) / 2);
    margin-left: calc(0px - var(--card-gap-size) / 2);
    margin-right: calc(0px - var(--card-gap-size) / 2);
    margin-bottom: calc(0px - var(--card-gap-size) / 2);
}

可能有同学会说:CSS 当数值为 0 时不要写单位!但是这儿是个特例,calc 无法直接写负值(calc(-var(--card-gap-size))),也无法写成 calc(0 - var(--card-gap-size)),但是加了单位之后就可以了,这也是我遇到的一个坑。

// 不会写结尾……End.

Disqus 加载中……如未能加载,请将 disqus.com 和 disquscdn.com 加入白名单。

这是我们共同度过的

第 3078 天