Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get unitless value #533

Closed
flops opened this issue Sep 30, 2012 · 45 comments
Closed

Get unitless value #533

flops opened this issue Sep 30, 2012 · 45 comments
Labels
enhancement New feature or request

Comments

@flops
Copy link

flops commented Sep 30, 2012

Is there any way to parseInt the values and get unitless value ?
All i`ve found that i only can get is it unitless value or not...

For example:

@function clear-units($value){
    @if type-of($value) == "number" {
        @if (unitless($value)) {
            @return $value;
        } @else if unit($value) == "em"{
            @return $value / 1em;
        } @else if unit($value) == "px" {
            @return $value / 1px;
        } @else if unit($value) == "pt" {
            @return $value / 1pt;
        }
    } @else {
        @warn "Not a number value: #{$value}";
        @return $value;
    }
}

made this function. But as you can see all units must be declared in if statement, and i don`t really like the realization.

@pdaoust
Copy link

pdaoust commented Oct 3, 2012

yes, it does seem a bit verbose. I wrote a function like that myself... It would be nice to see this rolled into SASS core, along with a cast() function that converts one unit into another, without doing arithmetic on it (with the exception of %, which should be divided by 100).

@nex3
Copy link
Contributor

nex3 commented Oct 5, 2012

There's a built-in unitless() function that does this.

@nex3 nex3 closed this as completed Oct 5, 2012
@flops
Copy link
Author

flops commented Oct 6, 2012

Not even close.
unitless - gives you Bool.
Function in example returns value without units, to convert em to px for example.

@nex3
Copy link
Contributor

nex3 commented Oct 12, 2012

Sorry, you're correct. Reopening.

@robwierzbowski
Copy link

In the meanwhile, if you don't want to cycle through all of those ifs:

@function strip-units($number) {
  @return $number / ($number * 0 + 1);
 }

http://stackoverflow.com/questions/12328259/how-do-you-strip-the-unit-from-any-number-in-sass/12335841#12335841

@pdaoust
Copy link

pdaoust commented Jan 8, 2013

@robwierzbowski Thanks for the tip; I'll incorporate it into my own stuff and see if it works!

@chriskempson
Copy link

Would love to see this in SASS core.

@KittyGiraudel
Copy link

Function from @robwierzbowski works great. I'd love to see this native. It occurred to me it comes in handy sometimes.

@chriseppstein
Copy link

I'd like to see use cases where this is a good idea. I have doubts.

@chriseppstein
Copy link

I think too many people don't know how to work with units and will just use this to do bad things instead of proper math. :(

@KittyGiraudel
Copy link

So... a function returning a unique identifier per Sass run (#771) is okay, but a function returning an integer from a length/percentage is not? :D

What I find interesting is you provide a function to return the unit from a length (unit()), but not a reverse function. It seems kind of odd to me.

@chriseppstein
Copy link

@hugogiraudel I gave my concerns and I asked for use cases. Your most recent comment is unproductive and unpersuasive.

@robwierzbowski
Copy link

I used to have a real-life use case for this, but I can't find an example in my recent work. Off the top of my head:

@function raise($measurement, $exponent) {
  $result: $measurement;
  @for $i from 1 through ($exponent - 1) {
    $result: $result * without-unit($measurement);
  }
  @return $result;
}

@ericam is the original stackoverflow answer-er, and might have some in-use examples.

@KittyGiraudel
Copy link

I didn't mean to sound rude or anything Chris. Sorry if I offended in any case. I just thought this isn't a borderline feature.

I remember having used this in a REM/PX mixin a while back (http://hugogiraudel.com/2013/03/18/ultimate-rem-mixin/). Not a key feature, but definitely useful in some cases. :)

@chriseppstein
Copy link

@robwierzbowski This actually makes my point. It's nonsense mathematically to perform and exponential operation with a dimensional value. Taking the unit off, makes the operation succeed, but it doesn't make sense. What does 2px ^ 5em mean? By doing this, you're making something succeed that should be an error to the caller.

@hugogiraudel That article is just another case of people who do not understand how arithmetic with dimensional values works. I want us to focus on training them on how to do the math correctly, not giving tools to enable bad behavior.

@robwierzbowski
Copy link

@chriseppstein: Note with the example function there's no way to do 2px ^ 5em. The exponent can only be a unitless number, and the without-unit is only applied when multiplying the measurement to itself. It is correct math.

But, I would like to see some real world, in-use examples. Like I said, at one point I wanted this but haven't needed it for quite a while, and have no opinion on whether it should be part of Sass or not.

@chriseppstein
Copy link

@robwierzbowski Oh, sorry I misread that. Again, this is a misunderstanding of how dimensional values work. 2ft ^ 2 is 4 square feet (an area), not 4 ft (which would be a length). The correct thing to do in this calculation is let the units multiply along with the number. the easiest way to think how to properly do math with units is to think of a unit like it is an unknown quantity in algebra. (2 * x) ^ 2 is 4 * x^2. If the units at the end of a calculation don't cancel out to become the dimension you're expecting, this is a good sign that a mathematical error was made.

@mirisuzanne
Copy link
Contributor

I don't have any use cases for stripping units. I just enjoy solving the puzzle. I have 3-4 more useless Sass functions if anyone wants them.

I think we did try to manipulate units early on with Susy, and quickly realized - after a scolding from Chris :) - that it was much simpler and more reliable to keep the units in place. I agree with @chriseppstein that there is really no good reason for this, at least not one I've ever thought of.

@robwierzbowski
Copy link

@chriseppstein Thanks, that makes a lot of sense.

@KittyGiraudel
Copy link

I have 3-4 more useless Sass functions if anyone wants them.

I do. :)

@cimmanon
Copy link

The only reason I strip the units off of a number is because I wish to use em as my fallback for rem, rather than px. I could just forget about rem since I've correctly calculated what the em equivalent is, but there may be rounding errors, depending on the browser involved.

http://codepen.io/cimmanon/pen/KfnuA

body {
  font-size: 133%;
}

.em {
  border: 0.75187em solid;
}

.rem {
  border: 1rem solid;
}

In Opera 12.16 (until Opera 15 has the same features as v12, this browser may as well not exist), for instance, the .em element border has a calculated width of 15px, while the .rem element border has a calculated width of 16px.

@robwierzbowski
Copy link

I think @chriseppstein's point is that you/we should be using Sass math to convert between units because it is idiomatic and less prone to accidental failure. I'm sure you can convert rem to em in your mixin without stripping the unit.

@pdaoust
Copy link

pdaoust commented Jul 23, 2013

I use a little 'unitless' function all the time. My primary (actually, only) use case is for conversion between units, which I believe Sass doesn't have built in (although I know Compass has it built in). I'm in a rather unpleasant tooling environment (Visual Studio), so I can't expect Compass to reliably work, even with Web Workbench. That's why I'm doing unit conversion right in SCSS. For instance:

// Hats off to @robwierzbowski for this; it's a lot simpler than my original function
@function strip-units($value) {
    @return $value / ($value * 0 + 1);
}

@function convert-angle($value, $unit) {
    $old-units: unit($value);
    $unitless-value: strip-units($value);

    $base-value: null;
    // Convert the angle to degrees, but without the degree unit
    @if $old-units == grad {
        $base-value: $unitless-value * 0.9;
    } @else if $old-units == turn {
        $base-value: $unitless-value * 360;
    } @else if $old-units == rad {
        $base-value: $unitless-value * (180 / 3.1415926);
    }

    // Now convert it to the desired units
    @if $unit == deg {
        @return $base-value * 1em;
    } @else if $unit == grad {
        @return $base-value / 0.9 * 1grad;
    } @else if $unit == turn {
        @return $base-value / 360 * 1turn;
    } @else if $unit == rad {
        @return $base-value * (3.1415926 / 180) * 1rad;
    }
}

Now I realise this doesn't constitute an ideal use case -- I'm very happy to just have my strip-units() function, and alternatively I suppose I could get more serious with SASS math, like so:

@if $old-units == deg {
    $base-value: $value / 1deg;
} @else if $old-units == grad {
    $base-value: $value * 0.9 / 1grad;
} @else if $old-units == turn {
    $base-value: $value * 360 / 1turn;
} @else if $old-units == rad {
    $base-value: $value * 180 / 3.1415926rad;
}

(Is this an idiomatic use of SASS math? It works, but is it muddying the concept at all? For some reason it doesn't improve the meaning of the code for me, even though I do understand the basics of dimensional math. It seems like I'm just stripping the unit anyway, one way or another.)

Anyhow, after having weighed in with my support, I really don't think I prefer either way over the other. So just ignore my use case if you don't think it adds any value :)

@chriseppstein
Copy link

@pdaoust I'd do it like this:

$convertable-units: deg grad turn rad;
$conversion-factors: 1 10grad/9deg 1turn/360deg 3.1415926rad/180deg;
@function convert-angle($value, $unit) {
  @if index($convertable-units, unit($value)) and index($convertable-units, $unit) {
    @return $value
             / nth($conversion-factors, index($convertable-units, unit($value)))
             * nth($conversion-factors, index($convertable-units, $unit));
  } @else {
    @warn "Cannot convert #{unit($value)} to #{$unit}";
  }
}

@chriseppstein
Copy link

I'm going to close this. I've yet to see a single good use case and so I remain convinced this is an anti-pattern.

@robwierzbowski
Copy link

Convinced. 👍

@KittyGiraudel
Copy link

Nice function Chris. I would have probably used a single 2-dimensional lists instead of 2 lists but I guess it's a matter of taste here. Thanks for the useful one anyway. ;)

@chriseppstein
Copy link

@hugogiraudel ya. that works too. This was easier to write on the fly :)

@pdaoust
Copy link

pdaoust commented Jul 26, 2013

you know, actually, I was in the process of reimplementing my conversion functions as two-dimensional lists :) Thanks, @chriseppstein, for the example converter -- once I parsed it, I understood the math that was going on.

@chriseppstein
Copy link

I think Sass's math is really nice, once the lightbulb goes on!

@montmanu
Copy link

Not trying to get involved in the back and forth, just wanted to share one particular use case I recently came across that led me here:

$screen-sm-min: 768px;
$thumbnail-width: 150px;
$thumbnail-height: 150px;
$thumbnail-width-sm: 300px;
$thumbnail-height-sm: 300px;

.thumbnail {
  width: $thumbnail-width;
  height: $thumbnail-height;
  background-image: url("http://placehold.it/#{$thumbnail-width}x#{$thumbnail-height}");
}

@media (min-width: $screen-sm-min){
  .thumbnail {
    width: $thumbnail-width-sm;
    height: $thumbnail-height-sm;
    background-image: url("http://placehold.it/#{$thumbnail-width-sm}x#{$thumbnail-height-sm}");
  }
}

The URL expects a unitless integer, so without any changes, the above compiles to something like:

.thumbnail {
  background-image: url("http://placehold.it/300pxx300px");
}

when what is desired is:

.thumbnail {
  background-image: url("http://placehold.it/300x300");
}

Obviously, there are several solutions to achieve the desired effect, but what I was looking for was something similar to the following:

.thumbnail {
  background-image: url("http://placehold.it/#{parse-int($thumbnail-width)}x#{parse-int($thumbnail-height})");
}

@pdaoust
Copy link

pdaoust commented Feb 28, 2014

@montmanu That's a very unusual, but very interesting and valid, use case. I can understand the core team's reluctance to make this a built-in function, but @robwierzbowski 's snippet is perfectly adequate. You could just call it parse-int() or whatever you like:

@function strip-units($value) {
    @return $value / ($value * 0 + 1);
}

@montmanu
Copy link

@pdaoust So do I.. I was reluctant to even share that example knowing that this issue has generated a bit of disagreement and is now closed, so I wasn't sure what value it would add to the discussion. @robwierzbowski's solution is what I ended up using and it has met my needs. Maybe worth mentioning that when naming the function parse-int() the grunt-contrib-compass task I was using had problems compiling. I didn't do too much digging to find out why, but when I renamed it to something less likely to create a namespace conflict (e.g., strip-units()) it compiled successfully.

@chriseppstein
Copy link

@montmanu This code is actually shorter and it works:

.thumbnail {
  background-image: url("http://placehold.it/#{$thumbnail-width / 1px}x#{$thumbnail-height / 1px)");
}

@cimmanon
Copy link

@chriseppstein Yes, it is shorter, but to someone who isn't familiar with what's going on here, 1px looks like some sort of magic number. Using a clearly-named function removes this ambiguity.

@ucavus
Copy link

ucavus commented Apr 4, 2014

I have a valid use case: unitless line height. I wanted to make a line height from a ratio of two variables that have units. Yes this still produces a valid line height rule, but it completely ruins natural CSS inheritance. Unitless line heights are very useful because they're interpreted as proportional to the font size of the context. (I usually don't use anything else.) With a unit, it becomes a fixed size everywhere, resulting in text that overlaps or is too sparse.

@KittyGiraudel
Copy link

Divide one value by the other? For instance, 1.5em / 1em.

@ucavus
Copy link

ucavus commented Apr 4, 2014

I was just coming back to write that I'd made a mistake. :P

@MattyBalaam
Copy link

Possibly there is a better solution to this, but I have a case at the moment which I can only think to solve with a unitless result.

I have an image sprite which needs a defined size to crop, so I am then scaling the image to fit into other spaces with a transform.

I would like to work out this scale automatically can do this by dividng the image sprite size with the size of other elements. e.g. in one case the image sprite is 353px wide but I want it to fit into an area with a width of 115px - dividing 115/353 seems to give me the correct ratio to accomplish this.

@chriseppstein
Copy link

115px/353px results in the same unitless ratio.

@MattyBalaam
Copy link

You are right. I think I had assumed I would get a px result from that calculation, sorry for the noise.

@drewlustro
Copy link

Sick strip-units() function @robwierzbowski

👍

@robwierzbowski
Copy link

Thanks, not my own! Copied/adapted from another source far in the past.

Honestly I haven't had to strip units since this issue was posted.
@chriseppstien ++

On Tuesday, May 6, 2014, Drew Lustro notifications@github.com wrote:

Sick strip-units() function @robwierzbowskihttps://github.com/robwierzbowski

[image: 👍]


Reply to this email directly or view it on GitHubhttps://github.com//issues/533#issuecomment-42348889
.

Rob Wierzbowski
@robwierzbowski http://twitter.com/#!/robwierzbowski
http://github.com/robwierzbowski
http://robwierzbowski.com

@marknotton
Copy link

Not really necessary anymore, but I took this approach to the same task.

// Remove units from a number.
@function strip-units($number) {
  @if type-of(#{$number}) == "number" or not unitless($number) {
    $num-length  : str-length(#{$number});
    $unit-length : str-length(unit($number));
    @return str-slice(#{$number}, 1,  $num-length - $unit-length);
  } @else {
    @warn "This isn't a number, or it doesn't have any units anyway.";
    @return $number;
  }
}

@chriseppstein
Copy link

@marknotton That function returns a string not a number. Please. Just use unit based arithmetic and you'll never need this function.

@sass sass locked and limited conversation to collaborators Aug 18, 2014
whatsnewsisyphus added a commit to whatsnewsisyphus/knife that referenced this issue Nov 8, 2014
…on px size declarations

included a stripUnits function as suggested by https://github.com/ronilaukkarinen on Pushplaybang#3. This is originally an excerpt from sass/sass#533 by https://github.com/robwierzbowski

The suggestion on Pushplaybang#3 broke the ie/px fallback, printing unitless units like the following, so I plugged in stripUnits there as well, and added the px suffix back in
```
font-size:16;
font-size:1rem
```

Streamlined calculateRem while I was at it
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests