CSS 自定义属性预处理的问题

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

CSS 现在有了自定义属性。 我们最近 写了很多关于它们的文章。 浏览器支持 良好,但当然,像 Internet Explorer 这样的旧版非 Evergreen 浏览器不支持它们,并且永远不会支持。 我可以理解使用“未来 CSS”进行创作的吸引力,并让预处理器将其回传到与旧版浏览器兼容的 CSS。 CSS 的 Babel!为什么不呢?!

不过,这让我感到不安——因为只有某些自定义属性的用例可以进行预处理。 在很多情况下,您对自定义属性的操作根本无法进行预处理。 因此,如果您这样做,您将陷入非常奇怪的境地。

如果您处于可以预处理它们并获得预期结果的情况,那么您可能应该只使用预处理器变量。

预处理器无法理解 DOM 结构

直到构建完整的 DOM 的“运行时”,CSS 才会应用。 更不用说任何给定 CSS 文件很可能只是影响页面的众多文件之一。

postcss-custom-properties 可能是最流行的自定义属性预处理器(它与 cssnext 捆绑在一起),它只允许您在:root中设置变量,例如

:root {
  --backgroundColor: white;
}
.header {
   background-color: var(--backgroundColor);
}

但也许 CSS 自定义属性最有用的一点是它们可以在级联中使用。 这是一个人为的例子,但您会明白我的意思

:root {
  --backgroundColor: white;
}
.header {
   background-color: var(--backgroundColor);
}
.header.is-about-page {
  --backgroundColor: yellow;
}

使用 postcss-custom-properties,它将处理根变量,但不会处理状态变化,从而留下一些非常无用的 CSS

.header {
   background-color: white;
}
.header.is-about-page {
  --backgroundColor: yellow;
}

他们意识到了这一点

转换不完整,并且无法完成(基于自定义属性的动态级联变量依赖于 DOM 树)。 它目前只是旨在提供一种面向未来的方法来使用原生 CSS 自定义属性提供的功能的有限子集(到:root选择器)由于我们不知道此插件上下文中 DOM,因此我们无法生成安全输出

您可以阅读关于所有这些的有趣对话,例如这个

还有一个名为 postcss-css-variables 的自定义属性处理器试图做更多的事情。 例如

.header {
   background-color: var(--backgroundColor, white);
}
.header:hover {
  --backgroundColor: orange;
}
.header.is-about-page {
  --backgroundColor: yellow;
}

输出

.header {
   background-color: white;
}
.header:hover {
   background-color: orange;
}

它可以正确处理悬停,但仍然放弃了声明的选择器。

这两个插件都同意:在 CSS 预处理器中根本无法完美复制 CSS 自定义属性的功能。

因此,如果您这样做,您要么必须限制自己使用它们的方式,要么获得不正确的结果。

您将失去使用 JavaScript 更改它们的能力

除了级联之外,CSS 自定义属性的另一个杀手级功能是能够使用 JavaScript 更改它们。 因此,与其使用 JavaScript 查询 DOM 以获取您需要更改的 X 个事物并分别更改所有事物,不如只更改一个自定义属性,并使其按预期渗透。

通过预处理 CSS 自定义属性,它们在处理后的 CSS 中不存在,因此您将失去此功能。

何时关闭它?

也许您只是将此预处理视为权宜之计。 在某些时候,您将关闭它,然后原生交付自定义属性。 您需要做一些工作以确保

  1. 原生处理方式与预处理器处理方式完全相同
  2. 预处理过程的其他任何部分都不会被它们混淆
  3. 您的回退方案很可靠,如果您仍然需要在不支持它们的浏览器中获得可行的体验

🙃

抱歉,我知道这感觉像是我在批评别人的东西。 实际上,我认为像这样的探索非常酷,值得去做和分享。 制作它们的人毫无疑问比我聪明。

有些人对此感到兴奋。 Mike Street

我在一家必须支持跨浏览器(包括 IE11,不幸的是)的代理机构工作。 尽管我们还无法在生产环境中完全使用 CSS 变量,但它们在开发和将其后处理为原始属性方面提供了许多优势。

我们的 gulp 进程包括 postcss-css-variables,它将样式表中的任何 CSS 变量更改为您为其设置的值。 类似于 SCSS 变量(以相同的方式进行处理),但允许您编写更小的 SCSS,并且在时机成熟时,删除处理并使用已就位的自定义属性部署样式表。

我只是认为分享一些谨慎的建议是值得的。 cssnext 的网站上有一条巨大的横幅写着“今天使用明天的 CSS 语法”,我担心这种营销方式将一些看似简单的东西宣传得过于简单,而实际上它复杂且微妙。

如果您正在预处理自定义属性,您不妨使用实际的预处理器变量

无论如何,这对我来说似乎是一个明智的选择。

Sass、Less 和 Stylus 都有效果很好的变量。 它们甚至具有一定的作用域,尽管不如真正的级联强大。

如果您使用 PostCSS,则有一些 允许使用变量的插件,它们使用不与原生 CSS 语法重叠的语法。

Mike Street 有一个非常酷的用例,他通过对 CSS 自定义属性进行微小更改来翻转媒体查询中渐变的方向(这是 它们可以执行的超级有用的操作)。

div {
  --direction: to bottom;
  
  background: linear-gradient( 
    var(--direction), 
    rgba(0, 0, 0, 1) 0, 
    rgba(0, 0, 0, 0.1) 100%);
  
  @media (max-width: 1000px) {
    --direction: to right;
  }
}

只有 postcss-css-variables(我认为)会尝试处理这个问题。 它简洁明了,但可以使用 SCSS 获得几乎相同的抽象级别 抽象级别

@mixin specificGradient($direction) {
  background: linear-gradient(
    $direction, 
    rgba(0, 0, 0, 1) 0, 
    rgba(0, 0, 0, 0.1) 100%
  );
}

div {
  @include specificGradient(to bottom);
  @media (max-width: 1000px) {
    @include specificGradient(to right);
  }
}

同时使用它们/立即处理回退

您可以完全使用任何预处理器 CSS 自定义属性。

您甚至可以将某些内容(如您的$brandColor等)保留为预处理器变量。 然后,同时利用 CSS 自定义属性来处理您将来可能需要动态化的内容。 您甚至可以立即处理回退,以便在处理过程中处理跨浏览器支持,而不是将其视为您正在预处理的内容。

$brandColor: #f06d06;

html {
  background: $brandColor;
  
  --base-font-size: 100%;
  font-size: 100%;
  font-size: var(--base-font-size);
}