CSS 实现糖果色幽灵按钮

390 阅读15分钟

原文:css-tricks.com/css-ing-can…

最近,当我在找一些编码灵感的时候,因为我的艺术细胞几乎为0,所以我唯一能做的就是找些别人已经创造出来好看的玩意,用简洁的代码来实现他们... 然后我发现了这些糖果色幽灵按钮

对于一个我可以快速编码实现的很酷的小玩意来说,它们似乎就是最佳选择。不到15分钟,我就实现了下面的结果:

The pure CSS candy ghost buttons.

我认为这项技术很值得分享,所以在这篇文章,我将介绍我最初是如何实现的,以及是否还有别的实现方式。

起步

一个按钮是由...创建的,你准备好了吗?一个 button 元素!它有一个 data-ico 属性,我们给他赋值了一个 emoji 图标。然后在style属性里设置了一个自定义的非用词表(译注:非css的自有属性名)属性 --slist

<button data-ico="👻" style="--slist: #ffda5f, #f9376b">boo!</button>

在写完这篇文章后, 我了解到 Safari 在文字剪裁效果上存在许多问题,也就是说在 button 元素或设置了 display: flex 的元素(也许 grid 属性也是这样)上不生效。遗憾的是,这意味着这里介绍的所有技术在Safari 上都行不通。唯一的解决方法就是把所有应用到 button 元素上的样式,改为设置在 button 内部的 span 元素上,来覆盖父级的 border-box 。希望可以帮助到像我一样使用 Linux 无法接触到苹果设备的人(除非你想因为这点事就去打扰一个月见不到两次坐在四楼的那个家伙,把他最近刚买的 iPhone 5 算上)。我还学会了将来可以使用Epiphany。谢谢 Brian 的建议。

对于 CSS 的部分,我们在伪元素 ::after 上添加了一个图标,并且在 button 上使用了 grid 布局,为了使文字和图标能很好的对齐。在 button 上,我们还设置了 borderpaddingborder-radius,使用了自定义属性 --slist ,通过对角线渐变美化字体。

button {
  display: grid;
  grid-auto-flow: column;
  grid-gap: .5em;
  border: solid .25em transparent;
  padding: 1em 1.5em;
  border-radius: 9em;
  background: linear-gradient(to right bottom, var(--slist)) border-box; // 高亮行
  font: 700 1.5em/ 1.25 ubuntu, sans-serif;
  text-transform: uppercase;
  
  &::after {
    content: attr(data-ico)
  }
}

关于上面的代码,有一点需要说明。 在高亮的那行上,我们将 background-originbackground-clip 都设置为 border-boxbackground-position 也通过将background-origin 设置为 0 0 设置在这个盒子的左上角,这样我们就得到了通过 background-size 设置尺寸的盒子。

也就是说,如果 background-origin 被设置为 padding-boxbackground-position0 0 点就在 padding-box 的左上角。如果 background-origin 被设置为 border-box,则background-position 0 0 点是在 border-box 的左上角。如果 background-origin 被设置为 padding-boxbackground-size50% 25% 意味着 padding-box 宽度的 50%padding-box 高度的 25%。如果background-origin 设置为 border-box,那么同样的background-size 设置为 50% 25% 意味着 border-box 宽度的 50% 和高度的 25%

background-origin 的默认值是 padding-box,这意味着默认大小为 100% 100% 的渐变将覆盖 padding-box,然后在 border 下方平铺(如果边框是完全不透明的,我们是看不见的)。 然而,在我们的例子中,border 是完全透明的,我们希望我们的渐变延伸到整个 border-box。 这意味着我们需要将 background-origin 值更改为 border-box

The result after applying the base styles (live demo).

简单,但令人遗憾的非标准 Chromium 内核解决方案

这涉及到使用三个遮罩 层并对其进行合成。如果你需要回顾复习一下如何合成遮罩,可以看看这个速成课程。(译注:遮罩合成指的是我们可以使用不同的操作将多个不同的遮罩层合并成一个独立的遮罩层)

需要特别注意的是,对于 CSS 遮罩图层,只有 alpha 通道会影响所有被遮罩的元素,因为被遮罩元素的每个像素都会得到一个相应的遮罩像素的 alpha 值,而 RGB 通道不会以任何方式影响结果,可以是任何有效值。下面,您可以看到 紫色透明渐变叠加的效果和使用遮罩 的渐变效果。

我们将从底部的两个图层开始。 第一个是完全不透明的层,完全覆盖整个 border-box,这意味着它每一个像素的 alpha 值都是 1。 另一层也是完全不透明的,但(通过使用 mask-clip)限制在 padding-box,这意味着,虽然这一层在整个padding-box 上的 alpha 值为 1,但是在 border 区域是完全透明的,那里的 alpha 值是 0

如果你很难脑补这一切,有一个小窍门,就是把元素的布局框想象成嵌套的矩形,如下图所示。

在我们的例子中,整个橙色框(border-box)的最底层是完全不透明的(alpha值为 1) 。整个红框是第二层(padding-box) ,我们放置在第一层的上面,是完全不透明的(alpha值是 1) ,在 padding limitborder limit 之间的区域是完全透明的(alpha值为 0)。

关于这些盒子的边界,有一个地方蛮有意思的,就是圆角的边框是由 border-radius决定的。(在padding-box的情况下,依旧是由border-width决定的)。下面的互动演示所示, 我们可以看到 border-boxborder-radius的提供的值,是如何包裹圆角的。padding-box 的圆角边框等于border-radius减去 border-width 。 (当出现负值的时候会限制成0 )。

Codepen screenshot (2D)

Codepen screenshot (3D)

现在让我们回过头来再看 mask 图层,整个 border-box 是完全不透明的,其中在它上面的 padding-box 区域是完全不透明的,但在 border 区域是完全透明的。(在 paddingborder 限制的区域之间)。这两个层通过 exclude 操作进行合成(在非标准的WebKit版本里称作 xor

The two base layers (live demo).

使用这个操作,在两个图层的alpha值是 01 的情况下就非常明显了,就像在我们的例子中一样——第一层的alpha永远都是 1 ,而第二层(我们放置在第一层的顶部)的alpha在 padding-box 内是 1 ,在padding限制和border限制之间的border区域是0

在这种情况下,是布尔逻辑规则非常直观的一个应用——对两个相同的值进行XOR运算得到 0,而对两个不同的值进行XOR运算得到 1.。

在整个padding-box中,第一层和第二层都有一个alpha值为“1”,所以合成它们会给我们这个区域的结果层一个alpha值为0 。然而,在border区域(在padding边界之外,在border边界以内),第一层的alpha值为 1,而第二层的alpha值为 0,所以我们在该区域得到的结果层的alpha值为1

下面的交互演示了这一点,在这里你可以切换查看在3D视图下两个分离的 遮罩层,以及使用这个操作后它们堆叠和合成在一起的效果。

Codepen screenshot (sepereated)

Codepen screenshot (overlapped)

把这些转成代码的形式,我们就得到了:

button {
  /* 基础样式同上 */
  --full: linear-gradient(red 0 0);
  -webkit-mask: var(--full) padding-box, var(--full);
  -webkit-mask-composite: xor;
  mask: var(--full) padding-box exclude, var(--full);
}

在我们进行下一步之前,让我们讨论一下关于上述CSS一些微调的细节。

首先,由于完全不透明的图层可以是任何东西(alpha通道是固定的,值永远是1,RGB通道不用管),所以我通常会把它们变成红色--变红的一共只有三个字符(red)! 同样的,使用圆锥渐变而不是线性渐变也可以节省一个字符,但我很少这样做,因为移动端浏览器只支持遮蔽,但不支持圆锥渐变。使用线性渐变可以确保所有浏览器都可以支持。好吧,除了IE和Chromium内核之前的Edge浏览器,但是,话说回来,反正没有什么炫酷的效果能在这些浏览器上工作。

其次,我们在两个层中都使用了渐变。我们没有在底层使用纯 背景色,因为我们不能单独为background-color本身设置background-clip。如果我们把 background-image层裁切到 padding-box上,那么这个 background-clip值也将适用于下面的 background-color —— 它也将被裁切到 "padding-box "上,我们将没有办法让它覆盖整个 border-box

第三,我们没有明确地为底层设置mask-clip值,因为在这例子里,我们想要的正是border-box 这个属性的默认值。

第四,我们可以把标准的 mask-composite(由Firefox支持)放在 mask 的简写里,而不是非标准的(由WebKit浏览器支持)。

最后,我们总是将标准的版本设置在末尾,这样它就覆盖了非标准的版本,使其也能被支持。

到目前为止,我们最终的代码结果(在这一点上仍然是跨浏览器的)看起来像下面图片中所展示的样子。我们还在根元素上添加了一个 背景图片,这样就可以明显看出我们在padding-box的区域是真正透明的。

The result after masking out the entire padding-box (live demo).

但这不是我们想要的。虽然我们有一个漂亮的渐变 边框(顺便说一下,这是我喜欢的获得渐变 边框的方法,因为我们在整个 padding-box中都有真正的透明度,而不仅仅是覆盖),现在我们还缺少了按钮上的文字。

所以下一步是在之前的图层上使用另一个 遮罩 图层来添加文本,这次是一个仅限于文字的遮罩层(同时也使实际的文本完全 透明,这样我们就可以通过它看到渐变的背景),并将这第三个 "遮罩 "图层与前两个图层的XOR运算结果(在上面的截图中可以看到结果)进行XOR运算。

下面的互动演示中可以看到三个 遮罩层在三维中分离,堆叠和合成的样子。

Codepen screenshot (seperated)

Codepen screenshot (overlapped)

需要注意的是,mask-cliptext值是非标准的。所以,很遗憾,这只能在Chrome中显示出来。在Firefox中,由于我们不在按钮上做任何遮罩,而且文本已经是 "透明的"了,我们实在没法合理地退而求其次了。

button {
  /* 基础样式同上 */
  -webkit-text-fill-color: transparent;
  --full: linear-gradient(red 0 0);
  -webkit-mask: var(--full) text, var(--full) padding-box, var(--full);
  -webkit-mask-composite: xor;
  /* 很遗憾,和上面的结果一样 :( */
  mask: var(--full) padding-box exclude, var(--full);
}

如果我们不想让我们的 按钮 因为这样无法使用,那我们应该在@supports 块中,对代码应用遮罩 并且使文本"透明 "。

button {
  /* 基础样式同上 */

  @supports (-webkit-mask-clip: text) {
    -webkit-text-fill-color: transparent;
    --full: linear-gradient(red 0 0);
    -webkit-mask: var(--full) text, var(--full) padding-box, var(--full);
    -webkit-mask-composite: xor;
  }
}

The final result using the masking-only method (live demo).

我真的很喜欢这个方法,它是我们目前最简单的方法,我真的希望 text 也是mask-clip的标准值,所有的浏览器都能很好的支持它。

然而,我们也有一些其他的方法来实现糖果幽灵按钮的效果,虽然它们比我们刚才讨论的只有Chromium内核支持的非标准方法更复杂或有更多的限制,但他们可以被更好的支持。所以我们来看看这些。

额外的伪元素解决方案

这涉及到像之前一样设置相同的初始样式,但是,我们没有使用遮罩,而是将 背景 裁切到 文本 区域内。

button {
  /* same base styles */
  background: 
    linear-gradient(to right bottom, var(--slist)) 
    border-box;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent
}

就像以前一样,我们还需要使实际的文本变得 透明 ,这样我们就可以透过它看到它后面的粉色渐变 背景,现在它已经被裁切到它的形状里了。

Knockout button text (live demo).

好吧,我们已经有了渐变文本,但现在我们还缺少渐变边框。 所以我们要用一个绝对定位的 ::before 伪元素来添加它,它覆盖了按钮 的整个border-box区域,并从父级继承了 border, border-radiusbackground (除了background-clip,它被重置为了 border-box)。

$b: .25em;

button {
  /* 和之前的一样 */
  position: relative;
  border: solid $b transparent;
  
  &::before { 
    position: absolute;
    z-index: -1;
    inset: -$b;
    border: inherit;
    border-radius: inherit;
    background: inherit;
    background-clip: border-box;
    content: '';
  }
}

inset: -$b 是下面这些代码的 一个简写 :

top: -$b;
right: -$b;
bottom: -$b;
left: -$b

注意,我们在这里使用的是带减号的 border-width,值为($b)。如果值为 0 会使伪元素的 margin-box (在本例中与 "边框 "相同,因为我们在::before 上没有 "边框")只覆盖其父级 buttonpadding-box,而我们希望它覆盖整个border-box。另外,正数是向内的,但我们需要用 border-width向外延伸,从padding 的边界到 border 的边界。因此要用减号——我们要往负方向走。

我们还在这个绝对定位的元素上设置了一个负的z-index, 因为我们不希望它在 按钮文本的上面,阻碍我们选择文本。在这一点上,文本选择是我们区分文本和背景的唯一方法,但我们很快就会解决这个问题。

The result after adding the gradient pseudo (live demo).

注意,由于伪元素内容不可选择,选择只包括按钮的实际文本内容,而不包括 ::after 伪元素中的表情符号。

下一步是添加一个两层的 "遮罩",在它们之间进行 exclude 合成操作,以便只留下这个伪元素的 border 区域可见。

button {
  /* same as before */
    
  &::before { 
    /* same as before */
    --full: linear-gradient(red 0 0);
    -webkit-mask: var(--full) padding-box, var(--full);
    -webkit-mask-composite: xor;
    mask: var(--full) padding-box exclude, var(--full);
  }
}

这几乎就是我们在上一个方法中的一个中间步骤为实际的按钮所做的事情。

The final result using the extra pseudo method (live demo).

在大多数情况下,我发现这是当我们想实现跨浏览器的方案时最好的方法,,当然不包括IE或非Chromium内核的Edge,因为它们都不支持遮罩。

border-image 方案

这时候,你们中的一些人可能会对着屏幕大叫,说没有必要使用::before伪元素,因为我们可以使用渐变的border-image来创建这种幽灵按钮--这是一种在过去七八年前就已经实现的技术方案!

然而,对药丸形状的按钮使用border-image 有一个很大的问题:这个属性与border-radius不兼容,在下面的互动演示中可以看到。一旦我们在一个有border-radius的元素上设置border-image ,我们就失去了border的圆角。即使如此,更有趣的是,background属性仍然是按照圆角来的。

Codepen screenshot

不过,有简单的解决方法,不需要圆角,或者设置的圆角大小最多只有 border-width的大小。

在没有圆角的情况下,除了放弃现在毫无意义的 border-radius 之外,我们不需要对初始样式做太多改变。

button {
  /* same base styles */
  --img: linear-gradient(to right bottom, var(--slist));
  border: solid .25em;
  border-image: var(--img) 1;
  background: var(--img) border-box;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

我们可以在下面看到跨浏览器的效果(即使在非Chromium内核的Edge应该也支持)。

The no corner rounding result using the border-image method (live demo).

所需的圆角是否能小于border-width,关键取决于border-radius这个属性的原理。当我们设置这个属性时,我们设置的半径代表了 border-box的圆角。padding-box的圆角(也就是 border的圆角内径)是border-radius 减去 border-width 的正值(如果大于0),否则是 0(没有圆角)。这意味着如果border-radius小于或等于border-width,边框就没有内圆角。

在这种情况下,我们可以使用inset()函数作为clip-path值,因为它也提供了将矩形的直角变圆的可能性。如果你需要复习一下这个函数的基本知识,你可以看看下面的插图。

How the inset() function works.

inset() 切除矩形以外的一切,切除的部分是由元素的 border-box 来定义的边缘距离。定义的方式与我们定义 margin, borderpadding相同(有一个、两个、三个或四个值),而且这个矩形的圆角,定义方式也与我们定义 border-radius 相同(任何 有效的border-radius 在这里也是有效的)。

在我们的例子中, border-box 边缘的距离都是 0(我们不想从 按钮 的任何一边砍掉任何东西),但我们有一个最多只能与border-width一样大的圆角,所以没有任何内边界圆角是有意义的。

$b: .25em;

button {
  /* 同上 */
  border: solid $b transparent;
  clip-path: inset(0 round $b)
}

请注意,clip-path也可能切除我们在 button 元素上添加的任何外部阴影,无论它们是通过 box-shadow 还是 filter: drop-shadow() 添加的。

The small corner rounding result using the border-image method (live demo).

虽然这个技术不能达到药丸形状的外观,但它目前的确是比较有优势的,也有很好的支持,在某些情况下,它可能正是我们所需要的。

到目前为止所讨论的三种解决方案可以在下面的演示中看到,如果你喜欢通过观看视频而不是阅读内容来学习的话,还可以通过YouTube链接看到我从头开始编写每一种解决方案。

Codepen screenshot

所有这些方法都是在除了文本以外的padding-box中创建真正的透明,所以它们对我们在 button后面的任何background都有效。然而,我们还有一些其他的方法,可能值得一提,尽管它们在这个部分有所限制。

覆盖方案

就和 border-image所用的方法一样,这是一个相当受限的方案。除非我们在 button 后面有一个实心的或固定的 背景 ,否则它不起作用。

background-clip 设定不同的值,背景也分成不同的层,就像渐变边框的覆盖技术一样。唯一不同的是,我们在 button 元素后面的 background 的基础上再添加一个渐变层,并将其置顶,放在 text上。

$c: #393939;

html { background: $c; } 

button {
  /* 同上 */
  --grad: linear-gradient(to right bottom, var(--slist));
  border: solid .25em transparent;
  border-radius: 9em;
  background: var(--grad) border-box, 
              linear-gradient($c 0 0) /* emulate bg behind button */, 
              var(--grad) border-box;
  -webkit-background-clip: text, padding-box, border-box;
  -webkit-text-fill-color: transparent;
}

遗憾的是,由于一个旧的bug,这种方法在Firefox中失败了——当仅设置文本透明,而没有使用任何 background-clip ,最后会形成一个没有文本的药丸状按钮。

The all background-clip cover solution (live demo).

我们仍然可以实现跨浏览器,通过使用覆盖的方法在::before 伪元素上使用渐变 border,以及在真正的button上使用background-clip: text 。这基本上就是我们讨论的第二个解决方案的一个限制更多的版本——我们仍然需要使用一个伪元素。但是,由于我们使用覆盖而不是 遮罩,所以它只在 button后面有一个固定的 background 时才有效。

$b: .25em;
$c: #393939;

html { background: $c; } 

button {
  /* same base styles */
  --grad: linear-gradient(to right bottom, var(--slist));
  border: solid $b transparent;
  background: var(--grad) border-box;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  
  &::before {
    position: absolute;
    z-index: -1;
    inset: -$b;
    border: inherit;
    border-radius: inherit;
    background: linear-gradient($c 0 0) padding-box, 
                var(--grad) border-box;
    content: '';
  }
}

从好的方面看,这个限制更多的版本应该也能在非 Chromium 内核的 Edge 浏览器使用。

The cover solution on a pseudo for a solid background behind the button (live demo).

Below, there’s also the fixed background version. 下面,还有固定背景的版本。

$f: url(balls.jpg) 50%/ cover fixed;

html { background: $f; } 

button {
  /* 同上 */
  
  &::before {
    /* 同上 */
    background: $f padding-box, 
                var(--grad) border-box
  }
}

The cover solution on a pseudo for a fixed background behind the button (live demo).

总的来说,我不认为这是最好的方案,除非我们都在 background 的限制里,而且我们需要在不支持遮蔽,但支持将background 裁切到 text的浏览器中重现这种效果,例如非Chromium内核的Edge。

混合方案

这种方法也有局限性,只有下面这种情况,效果才会实现:每一个渐变像素都可见,其通道的值都比 button下面的 背景 的相应像素值更大或更小。不过,这并不是糟糕的限制,因为它可能会使我们的页面有更好的对比度。

在这里,我们首先是先做出渐变部分(即文字、图标和 边框白色黑色都可以,这取决于我们是否有一个带有浅色渐变的深色主题或带有深色渐变的浅色主题。按钮的其余部分使用(文本和图标周围的区域,并且在边框内)先前选择的颜色的对比色(如果我们将颜色 值设置为 黑色,则为白色 ,否则为 黑色)。

在我们的例子中,我们有一个很浅的渐变按钮 在一个深色的背景上,所以我们先把文字、图标和边框设置为 白色背景 设置为 黑色。我们两个渐变节点的hex值是ff(R), da(G), 5f(B)和f9(R), 37(G), 6b(B)。所以我们用通道值最多只有min(ff, f9) = f9的红色,min(da, 37) = 37 的绿色,min(5f, 6b) = 5f的蓝色的任何背景像素都是安全的。

这意味着在我们的 button后面有一个 背景色,其通道值小于或等于f9, 375f,不论是作为一个纯色的 背景,还是在一个背景图片层下面,使用multiply 混合模式进行混合(这样产生的结果至少与两个层中较暗的一层一样深)。我们将这个背景设置在一个伪元素上,因为在Chrome中与 bodyhtml 混合,是不可行的。

$b: .25em;

body::before {
  position: fixed;
  inset: 0;
  background: url(fog.jpg) 50%/ cover #f9375f;
  background-blend-mode: multiply;
  content: '';
}

button {
  /* 同上 */
  position: relative; /* 所以它显示在 body::before 之上 */
  border: solid $b;
  background: #000;
  color: #fff;
  
  &::after {
    filter: brightness(0) invert(1);
    content: attr(data-ico);
  }
}

请注意,使 icon 完全变成 白色,是通过先用brightness(0)设置为黑色 ,再用invert(1)来反转这个黑色

The black and white button (live demo).

然后我们添加一个渐变::before伪元素,就像我们的第一个跨浏览器方案所做的那样。

button {
  /* 同上 */
  position: relative;
  
  &::before {
    position: absolute;
    z-index: 2;
    inset: -$b;
    border-radius: inherit;
    background: linear-gradient(to right bottom, var(--slist);
    pointer-events: none;
    content: '';
  }
}

唯一的区别是,我们没有给它一个负的z-index,而是给它一个正的z-index。这样,它不仅覆盖在button上面,而且也在::after这个伪元素上,我们将pointer-events设置为none ,以便让鼠标与下面真正的button上的内容进行交互。

The result after adding a gradient pseudo on top of the black and white button (live demo).

现在,下一步是保留我们 button黑色 部分,但用渐变替换 白色的部分(即文字、图标和边框)。我们可以用darken的混合模式来做到这一点,其中的两个图层是黑色和白色的按钮,上面有 ::after 图标和渐变的伪元素。

对于每一个RGB通道,这个混合模式从两个层各取一个值,并使用较暗(较小)的那个值。因为任何值都比 白色 要暗,所以混合后的层就使用该区域渐变的像素值。因为 黑色比所有东西都要暗,所以混合后的层在 按钮所有黑色的地方都是 黑色的。

button {
  /* 同上 */
  
  &::before {
    /* 同上 */
    mix-blend-mode: darken;
  }
}

The “almost there” result (live demo).

好吧,但是只有当 按钮 后面的 背景 是纯 黑色时,我们才能做到这一点。否则,如果 背景的每个像素都比 按钮上的渐变的相应像素要深,我们可以应用第二个混合模式,这次是在实际的 按钮上应用 变浅(之前,我们在::before的伪元素上应用 变暗)。

对于每一个RGB通道,这个混合模式从两个层各取一个值,并使用较浅(较大)的一个作为结果。因为任何值都比 黑色 浅,所以混合后的层使用 按钮后面的 背景,而 按钮黑色的。因为有一个要求是 按钮 的每一个渐变像素都比它后面的 背景 的相应像素要浅,所以混合后的层使用该区域的渐变像素值。

button {
  /* 同上 */
  mix-blend-mode: lighten;
}

The light ghost button on top of a dark background (live demo).

对于在浅色背景上的深色渐变按钮,我们需要切换混合模式。也就是说,在::before 伪元素上使用 lighten,在 button 本身使用 darken。首先,我们需要确保 按钮后面的 背景 足够浅。

假设我们的渐变在#602749#b14623之间。我们的渐变节点的通道值是60R),27G),49B)和b1R),46G),23R),所以按钮后面的背景的通道值需要至少是max(60,b1)=b1的红色,max(27,46)=46的绿色,max(49,23)=49的蓝色。

这意味着在我们的 按钮上有一个 背景色,其通道值大于或等于 b14649,要么是单独作为一个纯色 背景,要么是在 背景图像层下面,使用 screen 混合模式(这总是产生一个至少与这两个层中较浅的层一样的结果)。

我们还需要使 按钮的边框、文本和图标为 黑色,同时将其 背景设置为 "白色"。

$b: .25em;

section {
  background: url(fog.jpg) 50%/ cover #b14649;
  background-blend-mode: screen;
}

button {
  /* same as before */
  border: solid $b;
  background: #fff;
  color: #000;
  mix-blend-mode: darken;

  &::before {
    /* same as before */
    mix-blend-mode: lighten
  }
  
  &::after {
    filter: brightness(0);
    content: attr(data-ico);
  }
}

伪元素"::after "中的图标通过设置 filter: brightness(0)变成 黑色

The icon in the ::after pseudo-element is made black by setting filter: brightness(0) on it.

我们也可以选择将所有的 按钮层作为其 背景的一部分进行混合,对于 lightdark 主题都是如此,但是,如前所述,Firefox只是忽略了任何 background-clip声明,其中 text是数值列表的一部分而不是一个单独的数值。

好了,就这样吧! 我希望你正在(或已经)度过一个可怕的万圣节。我的万圣节绝对是被我发现的所有bug吓坏了......或者说是重新发现的,以及它们还没有被修复的事实。