CSS局部变量

发布于 大漠

CSS自定义属性也被称为CSS变量。这是非常令人兴奋的,因为我们在CSS中终于拥有真正的变量。什么意思,真正的变量?我的意思是可以动态更新和修改变量。虽然CSS处理器有了变量(Sass和PostCSS),这些变量通过编译变成了CSS,但没有动态能力更新其变量。这不是真正的CSS变量,只是用来存储和更新可用的值。

你可以通过--设置CSS变量和通过var(--varName)访问已声明的变量。这是一个非常基本的方法,可以像下面声明一个变量和让文本的颜色为red

div {
    --color: red;
    color: var(--color);
}

CSS变量是非常令人兴奋的

让我们一起来聊聊,为什么动态的CSS为变量会比以前做得更好。

动态JavaScript+CSS

使用CSS变量,我们现在可以更容易地通过JavaScript更新它的值。这意味着,我们不需要使用内联属性样式或者更新类名。我们可以简单通过替换CSS变量的值。

可以像下面那样给:root传递值:

document.documentElement.style.setProperty('--varName', 'propValue')

这样直接在你的CSS文件中写入或更新:root的值。所以,如果我想更新一个加载条的进度比例时,可以这样做:

function calculateLoadProgress() {
    let loadProgress = 0;

    // codes to update loadProgress here

    return loadProgress;
}

// Set width of progress bar
document.documentElement.style.setProperty('--progressBarWidth', calculateLoadProgress());

这是众多示例中的一个。@David Khourshid使用React做了一些非常有趣的探索和通过JS库确定值,然后传回给CSS变量。他在最近的CSS Conf EU上做了相关的分享与讨论

动态属性片段

CSS变量有一个很酷的功能需要注意,那就是如何在属性值中指定CSS变量。虽然以前我们单独使用border来声明边框,现在我们可以使用变量更新属性的任何部分,包括缩写的border或者是不知道参数的阴影和渐变属性。

比如这个示例:

.button-gradient {
    background: linear-gradient(var(--gradientAngle), var(--gradientStart),var(--gradientStop));

    --gradientAngle: 60deg;
    --gradientStart: lightpink;
    --gradientStop: lightyellow;
}

.button-gradient:hover {
    --gradientAngle: 0deg;
}

我们更新的是--gradientAngle而不是整个background属性。我们也可以使用JavaScript更新任何元素的这些值。是不是很不错。

干净的组件

CSS变量可以让我们写出更干净的组件,也可以更好的修改组件。下面一个是按钮的组件,让我们看看代码:

传统上,使用像BEM这样的来命名约定,我们将通过处理器,让修饰符(modifier)重写基类:

// Variables
$primaryColor: lightgreen;
$buttonBgColor: $primaryColor;

// Base Class
.button {
    background: $buttonBgColor;
    // other properties
}

// Modifier Class
.button--blue {
    background: lightblue;
}

在上面的示例中,我们通过提高选择器权重,让后面的属性覆盖前面的属性,但文件体积增大了,我们的代码库也将会被弄乱。但使用CSS变量,我们不需要通过样式覆盖前面的样式,只需要更新CSS的变量。下面是使用CSS变量写的代码:

// Variables
:root {
    --primaryColor: lightgreen;
    --buttonBgColor: var(--primaryColor);
}

// Base Class
.button {
    background: var(--buttonBgColor);
}

// Modifier Class
.button--blue {
    --buttonBgColor: lightblue;
}

使用局部变量我们能做得更好

和上面类似,最新的文档教程案例中CSS变量都是在CSS文件中通过:root来声明变量和变量值。

用来设置全局变量这是一个很好的方式,但不是必需的。CSS变量不仅仅可以在:root中声明,它也可以在CSS文件中的任何时候声明,比如在一个CSS的代码块中指定变量。这类似于JavaScript中使用关键词let来声明变量,可以在{}中声明变量,这种方式声明的变量被称为局部变量。所以我们可以利用这种特性,更好的处理我们的组件样式。

Leveraging CSS Variable scope improves the size, specificity, & semantics of our stylesheets. More...

例如,--buttonBgColor放在:root中作为一个全局变量,并不是我们所需要的东西(比如前面的示例)。较好的做法是在<button>内重命名变量--bgColor。这使它与它的父组件更紧密的耦合在一起,也更具有语义化。

CSS变量的使用规则:除非你需要一个全局变量,否则尽量使用局部CSS变量。然后在你自己需要地方使用。这样可以减少CSS变量在:root中的大量堆积,也使代码更加清晰。

在代码块{}中声明的变量,其作用域就像是JavaScript中使用let关键词声明的变量一样,都是属于局部变量

组织和示例

在Sass中,我们可以使用&扩展和重写一个嵌套,也可以更清晰,更可视化的面向对象。在这里我们可以看一个完整的示例:

  • 默认样式(指定属性)
  • 默认值(基本变量)
  • 差异化(更新变量)

代码如下:

.button {
    // 1. Default Styles
    background: var(--bgColor);
    padding: var(--size);
    line-height: var(--size);
    border: var(--outline);
    font-size: var(--size);

    // 2. Default Values
    --bgColor: lightgreen;
    --outline: none;
    --size: 1rem;

    // 3. Variances
    &--blue {
        --bgColor: lightblue;
    }
    
    &--pink {
        --bgColor: hotpink;
    }
    
    &--large {
        --size: 1.5rem;
    }
    
    &--outlined {
        --outline: 2px solid currentColor;
    }
}

**注:**我们仍然可以使用:root声明全局变量,比如基本的颜色和字号重置样式,但局部变量减少权生,也从而减小大小和增加语义化。

默认值

另外一个有趣的就是可以在:root中声明一个默认值,以防他们不存在。var()有另外一个能力,可以接受两个参数,并且可以嵌套在变量中。在下面的示例中,如果没有声明--bgColor,那就Card就会调用--colorPrimary的值,也就是red。所以我们可以删除步骤2的中.button的基本样式和在修饰符中更新--bgColor(如果我们想让默认按钮的颜色是主色)。

// 0. Set global variables here
:root {
    --colorPrimary: red;
}

.button {
    // 1. Default Styles
    // If --bgColor is not defined, the background will be the fallback: red
    background: var(--bgColor, var(--colorPrimary));

    // 2. Default Values
    // Since --bgColor is defined, the button remains lightgreen
    // If the line below was missing, the button would be red
    --bgColor: lightgreen;

    // ...
}

主题换肤

如果我们有更复杂的组件,我们仍然可以使用这种技术,并且可以结合像Sass一样的CSS处理器使它更简洁。可以像下面这样的来写按钮,让Card的按钮比主题的基本按钮更大。

.button--large {
    .card & {
        --size: 1.7rem;
    }
}

我们可以在任何地方更新--size的值,让Card的按钮变得略大一点。使用&技术编译出来的代码:

.card .button--large {
    --size: 1.7rem;
}

所以我们的结构可以这样写:

<div class="card">
    <button class="button button--pink button--large">
        Large Pink Button
    </button>
</div>

让具有红色边框的.card运用上面的代码。我们可以看到有一个更大的紫色按钮:

准备好了?

CSS变量得到了当今浏览器广泛的支持,尽管IE不支持,但Edge也得到了一定的支持。

如果你现在就要开始使用,有两个选择。

@supports

在CSS中,我们可以使用@support来检测浏览器是否支持。如果你想玩一些更现代的CSS属性,比如CSS Grid,那么@support是一个很好的工具。你可以像下面那样使用来检测你的浏览器是否支持CSS变量:

@supports(--color: red) {
    // code here implementing variables
}

使用@support是一个很好的选择,但它也像CSS的变量一样还存在很多争议。所以说,提供一份备用的值,可能是一个更好的解决方案。

传递一份备用值

你可以复用CSS的宽松风格,给同一个属性发送多个值。首先像平时一样写属性值,然后第二写声明的变量:

div {
    --color: red;
    color: red;
    color: var(--color);
}

这是多余的,但它允许你在一个现有的代码库中慢慢缓解使用一些CSS变量,一旦得到浏览器的支持,使其更容易重构。

CSS变量是超级强大的,并且局部变量使它们成为一个更加强大的工具,能让你模块更干净,更好的模块化设计系统。

扩展阅读

本文根据@Una的《Locally Scoped CSS Variables: What, How, and Why》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://una.im/local-css-vars

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/css/local-css-vars.htmlNike Kyrie 5 EP 'UFO'