When Sass and New CSS Options Collide


Lately, CSS has added a variety of new cool options corresponding to custom properties and new functions. Whereas this stuff could make our lives loads simpler, they will additionally find yourself interacting with preprocessors, like Sass, in humorous methods.

So that is going to be a submit concerning the points I’ve encountered, how I am going round them, and why I nonetheless discover Sass vital as of late.

The errors

When you’ve performed with the brand new min() and max() capabilities, you’ll have bumped into an error message like this when working with completely different items: “Incompatible items: vh and em.”

An error when working with various kinds of items within the min()/ max() operate

It is because Sass has its personalmin() operate, and ignores the CSS min() operate. Plus, Sass can not carry out any type of computation utilizing two values with items that don’t have a set relation between them.

For instance, cm and in items have a set relation between them, so Sass can work out what’s the results of min(20in, 50cm) and doesn’t throw an error after we attempt to use it in our code.

The identical issues goes for different items. Angular items, for instance, all have a set relation between them: 1turn, 1rad or 1grad all the time compute to the identical deg values. Similar goes for 1s which is all the time 1000ms, 1kHz which is all the time 1000Hz, 1dppx which is all the time 96dpi, and 1in which is all the time 96px. Because of this Sass can convert between them and blend them in computations and inside capabilities corresponding to its personal min() operate.

However issues break when these items don’t have a set relation between them (like the sooner case with em and vh items).

And it’s not simply completely different items. Making an attempt to make use of calc() inside min() additionally leads to an error. If I strive one thing like calc(20em + 7px), the error I get is, “calc(20em + 7px) isn’t a quantity for min.”

Screenshot. Shows the `'calc(20em + 7px)' is not a number for 'min'` error when trying to set `width: min(calc(20em + 7px), 50vh)`.
An error when utilizing completely different unit values with calc() nested within the min()operate

One other drawback arises after we need to use a CSS variable or the results of a mathematical CSS operate (corresponding to calc(), min() or max()) in a CSS filter like invert().

On this case, we get instructed that “$shade: 'var(--p, 0.85) isn’t a shade for invert.”

Screenshot. Shows the `$color: 'var(--p, 0.85)' is not a color for 'invert'` error when trying to set `filter: invert(var(--p, .85))`.
var() in filter: invert() error

The identical factor occurs for grayscale(): “$shade: ‘calc(.2 + var(--d, .3))‘ isn’t a shade for grayscale.”

Screenshot. Shows the `$color: 'calc(.2 + var(--d, .3))' is not a color for 'grayscale'` error when trying to set `filter: grayscale(calc(.2 + var(--d, .3)))`.
calc() in filter: grayscale() error

opacity() causes the identical challenge: “$shade: ‘var(--p, 0.8)‘ isn’t a shade for opacity.”

Screenshot. Shows the `$color: 'var(--p, 0.8)' is not a color for 'opacity'` error when trying to set `filter: opacity(var(--p, 0.8))`.
var() in filter: opacity() error

Nonetheless, different filter capabilities — together with sepia(), blur(), drop-shadow(), brightness(), distinction() and hue-rotate()— all work simply nice with CSS variables!

Seems that what’s taking place is just like the min() and max() drawback. Sass doesn’t have built-in sepia(), blur(), drop-shadow(), brightness(), distinction(), hue-rotate() capabilities, but it surely does have its personal grayscale(), invert() and opacity() capabilities, and their first argument is a $shade worth. Because it doesn’t discover that argument, it throws an error.

For a similar motive, we additionally run into bother when making an attempt to make use of a CSS variable that lists no less than two hsl()or hsla() values.

Screenshot. Shows the `wrong number of arguments (2 for 3) for 'hsl'` error when trying to set `color: hsl(9, var(--sl, 95%, 65%))`.
var() in shade: hsl() error.

On the flip facet, shade: hsl(9, var(--sl, 95%, 65%)) is completely legitimate CSS and works simply nice with out Sass.

The very same factor occurs with the rgb()and rgba() capabilities.

Screenshot. Shows the `$color: 'var(--rgb, 128, 64, 64)' is not a color for 'rgba'` error when trying to set `color: rgba(var(--rgb, 128, 64, 64), .7)`.
var() in shade: rgba() error.

Moreover, if we import Compass and attempt to use a CSS variable inside a linear-gradient() or inside a radial-gradient(), we get one other error, although utilizing variables inside conic-gradient() works simply nice (that’s, if the browser helps it).

Screenshot. Shows the At least two color stops are required for a linear-gradient error when trying to set background: linear-gradient(var(--c, pink), gold).
var() in background: linear-gradient() error.

It is because Compass comes with linear-gradient() and radial-gradient() capabilities, however has by no means added a conic-gradient() one.

The issues in all of those instances come up from Sass or Compass having identically-named capabilities and assuming these are what we meant to make use of in our code.

Drat!

The answer

The trick right here is to do not forget that Sass is case-sensitive, however CSS isn’t.

Meaning we are able to write Min(20em, 50vh)and Sass gained’t acknowledge it as its personal min() operate. No errors can be thrown and it’s nonetheless legitimate CSS that works as meant. Equally, writing HSL()/ HSLA()/ RGB()/ RGBA() or Invert() permits us to keep away from points we checked out earlier.

As for gradients, I often favor linear-Gradient() and radial-Gradient() simply because it’s nearer to the SVG version, however utilizing no less than one capital letter in there works simply nice.

However why?

Nearly each time I tweet something Sass-related, I get lectured on the way it shouldn’t be used now that we’ve CSS variables. I believed I’d tackle that and clarify why I disagree.

First, whereas I discover CSS variables immensely helpful and have used them for nearly all the pieces for the previous three years, it’s good to remember that they arrive with a efficiency value and that tracing the place one thing went mistaken in a maze of calc() computations generally is a ache with our present DevTools. I strive to not overuse them to keep away from getting right into a territory the place the downsides of utilizing them outweigh the advantages.

Screenshot. Shows how `calc()` expressions are presented in DevTools.
Not precisely simple to determine what’s the results of these calc() expressions.

Usually, if it acts like a relentless, doesn’t change element-to-element or state-to-state (during which case customized properties are undoubtedly the way to go) or cut back the quantity of compiled CSS (solving the repetition problem created by prefixes), then I’m going to make use of a Sass variable.

Secondly, variables have all the time been a fairly small portion of why I take advantage of Sass. After I began utilizing Sass in late 2012, it was primarily for looping, a function we nonetheless don’t have in CSS. Whereas I’ve moved a few of that looping to an HTML preprocessor (as a result of it reduces the generated code and avoids having to switch each the HTML and the CSS later), I nonetheless use Sass loops in loads of instances, like producing lists of values, cease lists inside gradient capabilities, lists of factors inside a polygon operate, lists of transforms, and so forth.

Right here’s an instance. I used to generate n HTML objects with a preprocessor. The selection of preprocessor issues much less, however I’ll be utilizing Pug right here.

- let n = 12;

whereas n--
  .merchandise

Then I’d set the $n variable into the Sass (and it must be equal to that within the HTML) and loop as much as it to generate the transforms that will place every merchandise:

$n: 12;
$ba: 360deg/$n;
$d: 2em;

.merchandise {
  place: absolute;
  high: 50%; left: 50%;
  margin: -.5*$d;
  width: $d; top: $d;
  /* prettifying types */

  @for $i from Zero to $n {
    &:nth-child(#{$i + 1}) {
      remodel: rotate($i*$ba) translate(2*$d) rotate(-$i*$ba);
			
      &::earlier than { content material: '#{$i}' }
    }
  }
}

Nonetheless, this meant that I must change each the Pug and the Sass when altering the variety of objects, making the generated code very repetitive.

Screenshot. Shows the generated CSS, really verbose, almost completely identical transform declaration repeated for each item.
CSS generated by the above code

I’ve since moved to creating Pug generate the indices as customized properties after which use these within the remodel declaration.

- let n = 12;

physique(type=`--n: ${n}`)
  - for(let i = 0; i < n; i++)
    .merchandise(type=`--i: ${i}`)
$d: 2em;

.merchandise {
  place: absolute;
  high: 50%;
  left: 50%;
  margin: -.5*$d;
  width: $d;
  top: $d;
  /* prettifying types */
  --az: calc(var(--i)*1turn/var(--n));
  remodel: rotate(var(--az)) translate(2*$d) rotate(calc(-1*var(--az)));
  counter-reset: i var(--i);
	
  &::earlier than { content material: counter(i) }
}

This considerably reduces the generated code.

Screenshot. Shows the generated CSS, much more compact, no having almost the exact same declaration set on every element separately.
CSS generated by the above code

Nonetheless, looping in Sass continues to be vital if I need to generate one thing like a rainbow.

@operate get-rainbow($n: 12, $sat: 90%, $lum: 65%) {
  $unit: 360/$n;
  $s-list: ();
	
  @for $i from Zero by means of $n {
    $s-list: $s-list, hsl($i*$unit, $sat, $lum)
  }
	
  @return $s-list
}

html { background: linear-gradient(90deg, get-rainbow()) }

Certain, I may generate it as a listing variable from Pug, however doing so doesn’t benefit from the dynamic nature of CSS variables and it doesn’t cut back the quantity of code that will get served to the browser, so there’s no profit popping out of it.

One other huge a part of my Sass (and Compass) use is tied to built-in mathematical capabilities (corresponding to trigonometric capabilities), which are part of the CSS spec now, however not but applied in any browser. Sass doesn’t include these capabilities both, however Compass does and this is the reason I typically want to make use of Compass.

And, positive, I may write my very own such capabilities in Sass. I did resort to this at first, earlier than Compass supported inverse trigonometric capabilities. I actually wanted them, so I wrote my very own primarily based on the Taylor series. However Compass offers these kinds of capabilities these days and they’re higher and extra performant than mine.

Mathematical capabilities are extraordinarily necessary for me as I’m a technician, not an artist. The values in my CSS often end result from mathematical computations. They’re not magic numbers or one thing used purely for aesthetics. A instance is producing lists of clip paths factors that create common or quasi-regular polygons. Take into consideration the case the place we need to create issues like non-rectangular avatars or stickers.

Let’s take into account a daily polygon with vertices on a circle with a radius 50% of the sq. aspect we begin from. Dragging the slider within the following demo permits us to see the place the factors are positioned for various numbers of vertices:

Placing it into Sass code, we’ve:

@mixin reg-poly($n: 3) {
  $ba: 360deg/$n; // base angle
  $p: (); // level coords listing, initially empty
	
  @for $i from Zero to $n {
    $ca: $i*$ba; // present angle
    $x: 50%*(1 + cos($ca)); // x coord of present level
    $y: 50%*(1 + sin($ca)); // y coord of present level
    $p: $p, $x $y // add present level coords to level coords listing
  }
	
  clip-path: polygon($p) // set clip-path to listing of factors
}

Be aware that right here we’re additionally making use of looping and of issues corresponding to conditionals and modulo which might be an actual ache when utilizing CSS with out Sass.

A barely extra developed model of this would possibly contain rotating the polygon by including the identical offset angle ($oa) to the angle of every vertex. This may be seen within the following demo. This instance tosses in a star mixin that works in an identical method, besides we all the time have a good variety of vertices and each odd-indexed vertex is located on a circle of a smaller radius ($f*50%, the place $f is sub-unitary):

We are able to even have chubby stars like this:

Or stickers with attention-grabbing border patterns. On this specific demo, every sticker is created with a single HTML aspect and the border sample is created with clip-path, looping and arithmetic in Sass. Fairly a little bit of it, in actual fact.

One other instance are these card backgrounds the place looping, the modulo operation and exponential capabilities work collectively to generate the dithering pixel background layers:

This demo simply occurs to rely closely on CSS variables as properly.

Then there’s utilizing mixins to keep away from writing the very same declarations again and again when styling issues like range inputs. Completely different browsers use completely different pseudo-elements to type the elements of such a management, so for each part, we’ve to set the types that management its look on a number of pseudos.

Sadly, as tempting as it might be to place this in our CSS:

enter::-webkit-slider-runnable-track, 
enter::-moz-range-track, 
enter::-ms-track { /* widespread types */ }

…we can not do it as a result of it doesn’t work! The complete rule set is dropped if even one of many selectors isn’t acknowledged. And since no browser recognises all three of the above, the types don’t get utilized in any browser.

We have to have one thing like this if we would like our types to be utilized:

enter::-webkit-slider-runnable-track { /* widespread types */ }
enter::-moz-range-track { /* widespread types */ }
enter::-ms-track { /* widespread types */ }

However that may imply a variety of equivalent types repeated thrice. And if we need to change, say, the background of the observe, we have to change it within the ::-webkit-slider-runnable-track types, within the ::-moz-range-track types and within the ::-ms-track types.

The one sane answer we’ve is to make use of a mixin. The types get repeated within the compiled code as a result of they need to be repeated there, however we don’t have to write down the identical factor thrice anymore.

@mixin observe() { /* widespread types */ }

enter {
  &::-webkit-slider-runnable-track { @embrace observe }
  &::-moz-range-track { @embrace observe }
  &::-ms-track { @embrace observe }
}

The underside line is: sure, Sass continues to be very a lot vital in 2020.



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *