sass / sass

Sass makes CSS fun!

Home Page:https://sass-lang.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unitless values should not compare as equal to unitful values

mirisuzanne opened this issue · comments

(Edit after the fact by @nex3)

Tasks:

  • Deprecate existing behavior in stable.
  • Remove behavior from master.

I know the existing behavior was intentional, but it breaks the basic rules of math: if 1px == 1 and 1 == 1em, then 1px == 1em. That sounds like a bug to me. In CSS the actual unit or lack of unit is very important. I can't think of any real use-case where I would be equally happy with or without a unit.

I can't think of any real use-case where I would be equally happy with or without a unit.

There is at least one situation: line-height will behave identically for unitless values and ems (inheritance will behave differently though).

Just sayin'. Don't think i'm trying to argue, i fully agree with Miriam.

(inheritance will behave differently though).

That's exactly my point. As an author, that distinction is huge.

I'm on board with this, although it'll require a deprecation period.

Thanks!

This is where having both == and === would make sense.

1 ==  1px // true
1 === 1px // false

This would allow you to preserve backward compatibility while having a clean and standard way to support @ericam's request.

What would be the work around for this then? At the moment, I'm using $value == 0 for a function, and in this case, 0rem == 0 and 0px == 0 is true

You could use unitless().

For True we use a function that compares both value and unit:

@function is-equal($one, $two) {
  @if type-of($one) == number and type-of($two) == number {
    @return $one == $two and unit($one) == unit($two);
  } @else {
    @return $one == $two;
  }
}
@function is-equal($one, $two) {
  @if type-of($one) == "number" and type-of($two) == "number" {
    @return $one == $two and unit($one) == unit($two);
  }

  @return $one == $two;
}

test {
  a: is-equal(1, 1);
  a: is-equal(1, 1px);
  a: is-equal(1, "1");
  a: is-equal("a", "a");
}

But it breaks if neither is a number:

test {
  a: is-equal(string, string);   // true
}

// $number: "string" is not a number for `unit'

And type is already compared by Sass with a simple == so I don't think the type-check is actually needed (except to make sure only numbers get the unit comparison).

Nice catch. Fixed. Which is what you had in the first place.

They look pretty similar now. :)

Strange. The function you've got for is-equal brings up the same error.

The result of 0rem == 0will befalse in future releases of Sass.

Am I missing something here?

I wrote that function before there was a deprecation warning to avoid. This adjustment eliminates the warning:

@function _true-is-equal($one, $two) {
  @if type-of($one) == number and type-of($two) == number {
    @if unit($one) == unit($two) {
      @return $one == $two;
    } @else {
      @return false;
    }
  } @else {
    @return $one == $two;
  }
}

Wouldn't this have further effects?

  • If 0 == 0px is false, 0 != 0px should be true
  • If 0 == 0px is false, 0 <= 0px as well as 0 >= 0px should be false
  • Of course 0 < 0px and 0 > 0px is false

Now we have:

0 < 0px, 0 <= 0px, 0 == 0px, 0 >= 0px and 0 > 0px are all false.

This is rather atypical. In general if a number is not smaller or larger than another number they have to be equal.

This may suggest, that unitless numbers should not be compared at all to numbers with units and just like px and % are incompatible, no unit and a unit should be incompatible, too.

That 0 == 0px is false and 0 != 0px is true would be consistent, because 0% == 0px is false and 0% != 0px is true. But 0 < 0px should then be an illegal operation just like 0% < 0px is illegal.

This of course is a massive change, and maybe 0 < 0px and the other comparisons should be deprecated, too.

At the end of the day, this also changes additions and subtractions. 5px + 5 should not be working anymore as well as 5px - 5 because numbers with units and unitless numbers are incompatible.

0 != 0px should definitely return true; this currently doesn't produce a deprecation message, but it should.

The rest of the operations should continue to behave as they do currently. Not only would changing this be a much more serious backwards incompatibility, there's some utility in being able to use a unitless number in a generic way.

Clearly, I am not as clued up on this as some. How would I fix the following, specifically @if $value == 0 is picking up a warning:

@mixin rems($property, $px-values) {
  // Convert the baseline into rems
  $baseline-rem: $baseline-px / 1rem;
  // Print the first line in pixel values
  #{$property}: $px-values;
  // If there is only one (numeric) value, return the property/value line for it.
  @if type-of($px-values) == "number" {
    #{$property}: $px-values / $baseline-rem;
  }
  @else {
    // Create an empty list that we can dump values into
    $rem-values: unquote("");
    @each $value in $px-values {
      // If the value is zero, return 0
      @if $value == 0 {
        $rem-values: append($rem-values, $value);
      }
      @else {
        $rem-values: append($rem-values, $value / $baseline-rem);
      }
    }
    // Return the property and its list of converted values
    #{$property}: $rem-values;
  }
}

@Wordius assuming $px-values is, as the name indicates, in px, you would do $value == 0px.

What are the thoughts here on the === unitless comparison? Seems like it might make the deprecation warning have a more useful suggestion. @nex3?

I'm strongly opposed to adding a new operator with such a narrow purpose.

Question: how does this change affect the comparable() function, and arithmetic operations on unit/unitless numbers? Is it only about equality checks, or will the following expressions also change?

e.g. comparable(2px, 2) == true; 2px + 2 == 4

Unitless numbers are still considered comparable to numbers with units. This does lead to the unfortunate case where $x >= $y isn't always the same as $x > $y or $x == $y, but I'm not sure I see a great way around that.

For your specific examples:

  • comparable(2px, 2) == true returns true in both the old scheme and the new.
  • 2px + 2 == 4 returned true in the old scheme (because 2px + 2 is 4px and 4px == 4), and will instead return false (because now 4px != 4).

Thanks @nex3 ; so I can take it that unit/unitless operations remain a stable core behavior? i.e. 1px + 1 will always be 2px? It's just a question related to the sass-calc PR reference above.

I can take it that unit/unitless operations remain a stable core behavior?

That's correct. There are no plans to change anything but boolean operators (and so far, only ==).