英文 | https://css-tricks.com/when-sass-and-new-css-features-collide/
翻译 | web前端开发
我们都知道,CSS添加了许多新的很酷的功能,例如,自定义属性的新功能,css的一些基础函数。尽管这些事情可以使我们的工作更加轻松,但在实际工作中,还是会与Sass等预处理器进行同时使用。
因此,今天这篇内容是将分享一些我遇到的问题,如何解决这些问题以及为什么这些天我仍然觉得Sass的学习还是非常有必要的。
错误
如果你使用过newmin()和max()函数,则在使用不同的单位时,可能会遇到如下错误消息:“不兼容的单位:vh和” em。
在min()/max()函数中使用不同类型的单元时发生错误截图
这是因为Sass具有自己的min()功能,而忽略CSS min()功能。另外,Sass无法使用两个值之间没有固定关系的单位执行任何类型的计算。
例如,cm和in单位之间具有固定的关系,因此min(20in, 50cm)当我们尝试在代码中使用Sass时,Sass可以找出结果是什么,并且不会引发错误。
其他单位也一样。例如,角度单位在它们之间都具有固定的关系:1turn,1rad或1grad始终计算为相同的deg值。
这同样适用于1s这始终是1000ms,1kHz这始终是1000Hz,1dppx这始终是96dpi,而且1in这是永远96px。
这就是Sass可以在它们之间进行转换并将它们混合在计算和内部函数(例如其自身min()函数)中的原因。
但是,当这些单元之间没有固定的关系时,事情就破裂了(就像前面的emand和vhunit一样)。
这不只是不同的单位。尝试在calc()内部使用min()也会导致错误。
我尝试类似calc(20em + 7px),我得到的错误是“calc(20em + 7px)不是的数字” min。
使用calc()嵌套在min()函数中的不同单位值时发生错误
当我们要使用一个CSS变量或CSS函数的时,出现了另一个问题,在这种情况下,我们被告知invert():“$color: 'var(--p, 0.85)不是颜色” 。
var()在filter: invert()错误
发生相同的事情是grayscale():“ $color:' calc(.2 + var(--d, .3))'不是颜色。”
calc()在filter: grayscale()错误
opacity()导致相同的问题:“ $color:' var(--p, 0.8)'不是'的颜色。'
var()在filter: opacity()错误
然而,其他filter功能-包括sepia(),blur(),drop-shadow(),brightness(),contrast()和hue-rotate()-所有的工作只是与CSS变量有关!
事实证明,正在发生的事情与min()和max()问题类似。没有内置sepia(),blur(),drop-shadow(),brightness(),contrast(),hue-rotate()的功能。
但它确实有它自己的grayscale(),invert()和opacity()功能,以及它们的第一个参数是一个$color值。由于找不到该参数,因此会引发错误。
出于同样的原因,在尝试使用至少列出两个hsl()或两个hsla()值的CSS变量时,我们也会遇到麻烦。
var()在color: hsl()错误。
另一方面,它color: hsl(9, var(--sl, 95%, 65%))是完全有效的CSS,在没有Sass的情况下也可以正常工作。
rgb()和rgba()函数会发生完全相同的事情。
var()在color: rgba()错误。
此外,如果我们导入Compass并尝试在alinear-gradient()或a内使用CSS变量radial-gradient(),即使在内部使用变量conic-gradient()也很好(也就是说,如果浏览器支持),也会遇到另一个错误。
var()在background: linear-gradient()错误。
这是因为Compass带有linear-gradient()和radial-gradient()功能,但从未添加conic-gradient()。
在所有这些情况下,问题都来自具有相同名称的函数的Sass或Compass,假设这些是我们打算在代码中使用的功能。
这些问题该怎么处理,以下是我的一些解决方案。
解决方案
这里的窍门是记住Sass区分大小写,但是CSS不区分大小写。
这意味着我们可以编写,Min(20em, 50vh)而Sass不会将其识别为自己的min()功能。不会抛出任何错误,它仍然是可以正常使用的有效CSS。同样,写作HSL()/ HSLA()/ RGB()/RGBA()或Invert()使我们避免了我们在前面的问题。
至于渐变,我通常更喜欢linear-Gradient()并且radial-Gradient()仅仅因为它更接近SVG版本,但是,在那里至少使用一个大写字母也可以。
每当我发布任何与Sass相关的信息时,我都会得到关于如何使用CSS变量的信息,因为现在有了CSS变量。
首先,虽然我发现CSS变量非常有用,并且在过去三年中几乎将它们用于所有变量。
但要记住,它们会带来性能成本,而且要跟踪在迷宫式calc()计算中出现问题的地方可能会很麻烦。
我们当前的DevTools让你感到痛苦。我尽量不要过度使用它们,以免进入使用它们的弊端超过收益。
弄清楚这些calc()表达式的结果并不容易。
通常,如果它像常量一样工作,则不会更改元素到元素或状态到状态的情况(在这种情况下,绝对要使用自定义属性)或减少已编译CSS的数量(解决重复问题)由前缀创建),那么我将使用Sass变量。
其次,变量一直是我使用Sass的原因中很小的一部分。当我在2012年末开始使用Sass时,它主要是用于循环播放,这是CSS中尚不具备的功能。尽管我已将其中一些循环移至HTML预处理器(因为这样可以减少生成的代码,并且避免以后再修改HTML和CSS)。
但在很多情况下,我仍然会使用Sass循环,例如生成值列表,渐变函数内的停止列表,多边形函数内的点列表,变换列表等。
这是一个例子。我曾经使用预处理器生成HTML项目。预处理器的选择无关紧要,但是,我在这里使用Pug。
- let n = 12;
while n--
.item
然后,将$n变量设置为Sass(必须与HTML中的变量相等)并循环到它以生成将每个项目定位的转换:
$n: 12;
$ba: 360deg/$n;
$d: 2em;
.item {
position: absolute;
top: 50%; left: 50%;
margin: -.5*$d;
width: $d; height: $d;
/* prettifying styles */
@for $i from 0 to $n {
&:nth-child(#{$i + 1}) {
transform: rotate($i*$ba) translate(2*$d) rotate(-$i*$ba);
&::before { content: '#{$i}' }
}
}
}
但是,这意味着在更改项目数时,我必须同时更改Pug和Sass,从而使生成的代码非常重复。
上面的代码生成的CSS
从那以后,我开始让Pug生成索引作为自定义属性,然后在transform声明中使用它们。
- let n = 12;
body(style=`--n: ${n}`)
- for(let i = 0; i < n; i++)
.item(style=`--i: ${i}`)
$d: 2em;
.item {
position: absolute;
top: 50%;
left: 50%;
margin: -.5*$d;
width: $d;
height: $d;
/* prettifying styles */
--az: calc(var(--i)*1turn/var(--n));
transform: rotate(var(--az)) translate(2*$d) rotate(calc(-1*var(--az)));
counter-reset: i var(--i);
&::before { content: counter(i) }
}
这大大减少了生成的代码。
上面的代码生成的CSS
但是,如果我想生成彩虹之类的东西,仍然需要循环使用Sass。
@function get-rainbow($n: 12, $sat: 90%, $lum: 65%) {
$unit: 360/$n;
$s-list: ();
@for $i from 0 through $n {
$s-list: $s-list, hsl($i*$unit, $sat, $lum)
}
@return $s-list
}
html { background: linear-gradient(90deg, get-rainbow()) }
当然,我可以从Pug生成它作为列表变量,但是这样做没有利用CSS变量的动态特性,也没有减少提供给浏览器的代码量,因此没有任何好处出来。
我在Sass(和Compass)中使用的另一大部分是与内置的数学函数(例如三角函数)相关联的,这些函数现在是CSS规范的一部分。
但尚未在任何浏览器中实现。Sass也不具备这些功能,但是Compass具备,这就是为什么我经常需要使用Compass。
而且,当然,我可以在Sass中编写自己的此类函数。在Compass支持逆三角函数之前,我一开始就采用了此方法。
我真的很需要它们,所以我根据泰勒级数写了自己的。但是Compass如今提供了这类功能,它们比我的更好,性能更高。
数学功能对我来说非常重要,因为我是技术人员,而不是艺术家。CSS中的值通常来自数学计算。它们不是魔术数字,也不是纯粹用于美学的东西。
一个示例是生成剪切路径点的列表,这些剪切路径点将创建规则或准规则多边形。考虑一下我们要创建非矩形化身或贴纸之类的情况。
让我们看看,一个在圆上具有顶点的规则多边形,其半径50%为从其开始的正方形元素的半径。在下面的演示中拖动滑块可以使我们看到不同数量的顶点的放置位置:
将其放入Sass代码中,代码如下:
@mixin reg-poly($n: 3) {
$ba: 360deg/$n; // base angle
$p: (); // point coords list, initially empty
@for $i from 0 to $n {
$ca: $i*$ba; // current angle
$x: 50%*(1 + cos($ca)); // x coord of current point
$y: 50%*(1 + sin($ca)); // y coord of current point
$p: $p, $x $y // add current point coords to point coords list
}
clip-path: polygon($p) // set clip-path to list of points
}
请注意,这里我们还使用了循环以及诸如条件和模之类的东西,这在使用不带Sass的CSS时确实很痛苦。
稍微多一些演变的版本可能涉及通过向$oa每个顶点的角度添加相同的偏移角度来旋转多边形。
在下面的演示中可以看到。此示例以类似的方式抛出一个星形混合,但我们总是有偶数个顶点,并且每个奇数索引的顶点都位于半径较小的圆($f*50%,其中$f为次sub)上:
演示示例地址:https://codepen.io/thebabydino/pen/PRMxwj
我们也可以有这样的效果:
演示地址:https://codepen.io/thebabydino/pen/KLyjBx
还有一个带有有趣border图案的贴纸。在这个特定的演示中,每个标签都是使用单个HTML的border元素创建的clip-path,而模式是使用Sass中的,循环和数学创建的。
演示示例地址:https://codepen.io/thebabydino/pen/XWJaXRp
另一个示例是这些卡片背景,其中循环,模运算和指数函数一起工作以生成抖动像素背景层:
演示示例地址:https://codepen.io/thebabydino/pen/gOYxjLz
这个演示恰好也是依赖CSS变量。
然后,使用mixins来避免在对诸如range input之类的东西进行样式化时一遍又一遍地编写完全相同的声明。
不同的浏览器使用不同的伪元素来设置此类控件的组件样式,因此对于每个组件,我们都必须设置用于控制其在多个伪组件上的外观的样式。
如果将其放入我们的CSS中:
input::-webkit-slider-runnable-track,
input::-moz-range-track,
input::-ms-track { /* common styles */ }
但是,我们不能这样做,因为它不起作用!如果没有选择器,整个规则集将被删除。而且由于没有浏览器可以识别上述元素,因此,样式不会在任何浏览器中起到作用。
如果要应用样式,我们需要具有以下内容:
input::-webkit-slider-runnable-track { /* common styles */ }
input::-moz-range-track { /* common styles */ }
input::-ms-track { /* common styles */ }
但这可能意味着许多相同的样式重复了三遍。
而且,例如,如果要更改background,则需要在::-webkit-slider-runnable-track样式,::-moz-range-track样式和样式中进行更改::-ms-track。
我们唯一的理智的解决方案是使用mixin。样式在编译后的代码中会重复,因为它们必须在那儿重复,但是我们不必再写三遍相同的东西。
@mixin track() { /* common styles */ }
input {
&::-webkit-slider-runnable-track { @include track }
&::-moz-range-track { @include track }
&::-ms-track { @include track }
}
最后,Sass仍然非常有必要。