内容简介:Recently, CSS has added a lot of new cool features such ascustom properties and newfunctions. While these things can make our lives a lot easier, they can also end up interacting with preprocessors, like Sass, in funny ways.So this is going to be a post ab
Recently, CSS has added a lot of new cool features such ascustom properties and newfunctions. While these things can make our lives a lot easier, they can also end up interacting with preprocessors, like Sass, in funny ways.
So this is going to be a post about the issues I’ve encountered, how I go around them, and why I still find Sass necessary these days.
The errors
If you’ve played with the new min()
and max()
functions, you may have ran into an error message like this when working with different units: “Incompatible units: vh
and em
.”
This is because Sass has its own min()
function, and ignores the CSS min()
function . Plus, Sass cannot perform any sort of computation using two values with units that don’t have a fixed relation between them.
For example, cm
and in
units have a fixed relation between them, so Sass can figure out what’s the result of min(20in, 50cm)
and doesn’t throw an error when we try to use it in our code.
The same things goes for other units. Angular units, for example, all have a fixed relation between them: 1turn
, 1rad
or 1grad
always compute to the same deg
values. Same goes for 1s
which is always 1000ms
, 1kHz
which is always 1000Hz
, 1dppx
which is always 96dpi
, and 1in
which is always 96px
. This is why Sass can convert between them and mix them in computations and inside functions such as its own min()
function.
But things break when these units don’t have a fixed relation between them (like the earlier case with em
and vh
units).
And it’s not just different units. Trying to use calc()
inside min()
also results in an error. If I try something like calc(20em + 7px)
, the error I get is, “ calc(20em + 7px)
is not a number for min
.”
Another problem arises when we want to use a CSS variable or the result of a mathematical CSS function (such as calc()
, min()
or max()
) in a CSS filter like invert()
.
In this case, we get told that “ $color: 'var(--p, 0.85)
is not a color for invert
.”
The same thing happens for grayscale()
: “ $color
: ‘ calc(.2 + var(--d, .3))
‘ is not a color for grayscale
.”
opacity()
causes the same issue: “ $color
: ‘ var(--p, 0.8)
‘ is not a color for opacity
.”
However, other filter
functions — including sepia()
, blur()
, drop-shadow()
, brightness()
, contrast()
and hue-rotate()
— all work just fine with CSS variables!
Turns out that what’s happening is similar to the min()
and max()
problem. Sass doesn’t have built-in sepia()
, blur()
, drop-shadow()
, brightness()
, contrast()
, hue-rotate()
functions, but it does have its own grayscale()
, invert()
and opacity()
functions, and their first argument is a $color
value. Since it doesn’t find that argument, it throws an error.
For the same reason, we also run into trouble when trying to use a CSS variable that lists at least two hsl()
or hsla()
values.
On the flip side, color: hsl(9, var(--sl, 95%, 65%))
is perfectly valid CSS and works just fine without Sass.
The exact same thing happens with the rgb()
and rgba()
functions.
Furthermore, if we import Compass and try to use a CSS variable inside a linear-gradient()
or inside a radial-gradient()
, we get another error, even though using variables inside conic-gradient()
works just fine (that is, if the browser supports it).
This is because Compass comes with linear-gradient()
and radial-gradient()
functions, but has never added a conic-gradient()
one.
The problems in all of these cases arise from Sass or Compass having identically-named functions and assuming those are what we intended to use in our code.
Drat!
The solution
The trick here is to remember that Sass is case-sensitive, but CSS isn’t.
That means we can write Min(20em, 50vh)
and Sass won’t recognize it as its own min()
function. No errors will be thrown and it’s still valid CSS that works as intended. Similarly, writing HSL()
/ HSLA()
/ RGB()
/ RGBA()
or Invert()
allows us to avoid issues we looked at earlier.
As for gradients, I usually prefer linear-Gradient()
and radial-Gradient()
just because it’s closer to the SVG version , but using at least one capital letter in there works just fine.
But why?
Almost every time I tweet anything Sass-related, I get lectured on how it shouldn’t be used now that we have CSS variables. I thought I’d address that and explain why I disagree.
First, while I find CSS variables immensely useful and have used them for almost everything for the past three years, it’s good to keep in mind that they come with a performance cost and that tracing where something went wrong in a maze of calc()
computations can be a pain with our current DevTools. I try not to overuse them to avoid getting into a territory where the downsides of using them outweigh the benefits.
In general, if it acts like a constant, doesn’t change element-to-element or state-to-state (in which case custom properties are definitelythe way to go) or reduce the amount of compiled CSS ( solving the repetition problem created by prefixes), then I’m going to use a Sass variable.
Secondly, variables have always been a pretty small portion of why I use Sass. When I started using Sass in late 2012, it was primarily for looping, a feature we still don’t have in CSS. While I’ve moved some of that looping to an HTML preprocessor (because it reduces the generated code and avoids having to modify both the HTML and the CSS later), I still use Sass loops in plenty of cases, like generating lists of values, stop lists inside gradient functions, lists of points inside a polygon function, lists of transforms, and so on.
Here’s an example. I used to generate n
HTML items with a preprocessor. The choice of preprocessor matters less, but I’ll be using Pug here.
- let n = 12; while n-- .item
Then I would set the $n
variable into the Sass (and it would have to be equal to that in the HTML) and loop up to it to generate the transforms that would position each item:
$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}' } } } }
However, this meant that I would have to change both the Pug and the Sass when changing the number of items, making the generated code very repetitive.
I have since moved to making Pug generate the indices as custom properties and then use those in the transform
declaration.
- 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) } }
This significantly reduces the generated code.
However, looping in Sass is still necessary if I want to generate something like a rainbow.
@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()) }
Sure, I could generate it as a list variable from Pug, but doing so doesn’t take advantage of the dynamic nature of CSS variables and it doesn’t reduce the amount of code that gets served to the browser, so there’s no benefit coming out of it.
Another big part of my Sass (and Compass) use is tied to built-in mathematical functions (such as trigonometric functions), which are part of the CSS spec now , but not yet implemented in any browser. Sass doesn’t come with these functions either, but Compass does and this is why I often need to use Compass.
And, sure, I could write my own such functions in Sass. I did resort to this in the beginning, before Compass supported inverse trigonometric functions. I really needed them, so I wrote my own based on the Taylor series . But Compass provides these sorts of functions nowadays and they are better and more performant than mine.
Mathematical functions are extremely important for me as I’m a technician, not an artist. The values in my CSS usually result from mathematical computations. They’re not magic numbers or something used purely for aesthetics. A example is generating lists of clip paths points that create regular or quasi-regular polygons. Think about the case where we want to create things like non-rectangular avatars or stickers.
Let’s consider a regular polygon with vertices on a circle with a radius 50%
of the square element we start from. Dragging the slider in the following demo allows us to see where the points are placed for different numbers of vertices:
Putting it into Sass code, we have:
@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 }
Note that here we’re also making use of looping and of things such as conditionals and modulo that are a real pain when using CSS without Sass.
A slightly more evolved version of this might involve rotating the polygon by adding the same offset angle ( $oa
) to the angle of each vertex. This can be seen in the following demo. This example tosses in a star mixin that works in a similar manner, except we always have an even number of vertices and every odd-indexed vertex is situated on a circle of a smaller radius ( $f*50%
, where $f
is sub-unitary):
We can also have chubby stars like this:
Or stickers with interesting border
patterns. In this particular demo, each sticker is created with a single HTML element and the border
pattern is created with clip-path
, looping and mathematics in Sass. Quite a bit of it, in fact.
Another example are these card backgrounds where looping, the modulo operation and exponential functions work together to generate the dithering pixel background layers:
This demo just happens to rely heavily on CSS variables as well.
Then there’s using mixins to avoid writing the exact same declarations over and over when styling things likerange inputs. Different browsers use different pseudo-elements to style the components of such a control, so for every component, we have to set the styles that control its look on multiple pseudos.
Sadly, as tempting as it may be to put this in our CSS:
input::-webkit-slider-runnable-track, input::-moz-range-track, input::-ms-track { /* common styles */ }
…we cannot do it because it doesn’t work! The entire rule set is dropped if even one of the selectors isn’t recognized. And since no browser recognises all three of the above, the styles don’t get applied in any browser.
We need to have something like this if we want our styles to be applied:
input::-webkit-slider-runnable-track { /* common styles */ } input::-moz-range-track { /* common styles */ } input::-ms-track { /* common styles */ }
But that can mean a lot of identical styles repeated three times. And if we want to change, say, the background
of the track, we need to change it in the ::-webkit-slider-runnable-track
styles, in the ::-moz-range-track
styles and in the ::-ms-track
styles.
The only sane solution we have is to use a mixin. The styles get repeated in the compiled code because they have to be repeated there, but we don’t have to write the same thing three times anymore.
@mixin track() { /* common styles */ } input { &::-webkit-slider-runnable-track { @include track } &::-moz-range-track { @include track } &::-ms-track { @include track } }
The bottom line is: yes, Sass is still very much necessary in 2020.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解Nginx
陶辉 / 机械工业出版社 / 2013-4-15 / 89.00元
本书是阿里巴巴资深Nginx技术专家呕心沥血之作,是作者多年的经验结晶,也是目前市场上唯一一本通过还原Nginx设计思想,剖析Nginx架构来帮助读者快速高效开发HTTP模块的图书。 本书首先通过介绍官方Nginx的基本用法和配置规则,帮助读者了解一般Nginx模块的用法,然后重点介绍如何开发HTTP模块(含HTTP过滤模块)来得到定制的Nginx,其中包括开发一个功能复杂的模块所需要了解的......一起来看看 《深入理解Nginx》 这本书的介绍吧!