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

First-class calc #818

Open
4 of 8 tasks
c-johnson opened this issue Jun 24, 2013 · 73 comments
Open
4 of 8 tasks

First-class calc #818

c-johnson opened this issue Jun 24, 2013 · 73 comments

Comments

@c-johnson
Copy link

@c-johnson c-johnson commented Jun 24, 2013


The following scss code does not work:

$var: 10px;
height: calc(100% - $var);

Tested with latest version (3.2.9).

@Snugug
Copy link

@Snugug Snugug commented Jun 24, 2013

You need to interpolate the $var in order to have it print out the 10px there:

$var: 10px;
height: calc(100% - #{$var});

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Jun 24, 2013

@nex3 is this intentional?

@nex3
Copy link
Contributor

@nex3 nex3 commented Jun 28, 2013

@nex3 is this intentional?

More or less. calc is considered to be a type of string literal, similarly to how url is treated. We could theoretically include a parser for the calc mini-grammar that would have explicit rules about how variables work, but then variables would work differently in that context than any other context, which may be unexpected.

I'm going to mark this as a feature request and leave it open until we come to a decision about what behavior we'd ideally like for calc.

@ghost
Copy link

@ghost ghost commented Jun 28, 2013

I think that calc should be treated as a special case. If you do calc(100% - $var) and $var = 5%, it should just output 95% without the calc because it's unnecessary, but if $var is 100px, it should still output the calc.

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Jun 28, 2013

So the thing about calc() is that it's a CSS syntax hack. There are a couple of syntax issues which make the bare calculations like we have in Sass complicated and annoying to implement, so they introduce calc as a kind of quote. So what I think you should write in this case is simply 100% - $var and if $var is 5% it would compile to 95% and if $var is 100px it would compile to calc(100% - 100px).

In a future release, we will add support for calc() output, we just need some way to indicate that the feature should be enabled.

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Nov 7, 2013

@chriseppstein what kind of indication would be good for you to enable it, i would find this an immensely useful feature seeing the impending death of ie8.

@cimmanon
Copy link

@cimmanon cimmanon commented Nov 7, 2013

@mcgoooo Why do you think there is some magical impending death of IE8 on the horizon? Windows XP is still the 2nd most popular OS right now and it cannot upgrade beyond IE8. IE8 is also more popular than any other IE version at the moment.

Even if IE8 wasn't a concern, Android still is (no version of Android supports calc).

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Nov 7, 2013

today google apps dropped support for ie9.
In april windows is officially discontinuing support for windows xp, meaning that for some they will not have to support ie8, and may even be the recommended direction to protect our users.

As for android, i have been using it to great effect with standard values and progressively enhancing with them and the results are wonderful. i have been very impressed with the support, and even found a reasonable polyfill for the (few) android browsers that do not support. My current android device with chrome seems to handle it natively.

polyfill
https://github.com/CJKay/PolyCalc

would some +1's on the issue help, i can maybe have a look at doing a pull request

currently to support i am looking at using erb before i use the sass, hardly an ideal solution, is there maybe another way to get this to work?

@lolmaus
Copy link

@lolmaus lolmaus commented Nov 7, 2013

@cimmanon is absolutely right that calc() can't be used in projects that require maximum browser coverage. I'm not using it for this reason in any of my projects.

But this is not the case for everybody. I'm digging into Derby.js, a modern full stack framework based on Node. It doesn't support IE8 at all and is perfect for building rich internet applications that aren't supposed to be used on mobile.

In this case, i would totally use calc(), so i'm looking forward to @chriseppstein and other Compass maintainers to implement its proper support.

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Nov 7, 2013

@lolmaus i think you said it better than me 👍

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Nov 7, 2013

also here is the associated bug
https://code.google.com/p/android/issues/detail?id=55199&q=calc&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

ahh i see why it works on my native browser as well as chrome, i have kit kat
http://caniuse.com/#search=cal

@manovotny
Copy link

@manovotny manovotny commented Mar 25, 2014

Super glad I found this issue!

The interpolate tip @Snugug pointed out works like a champ, but I would love to see this handled automatically at some point.

For now, the work around will suffice.

@lolmaus
Copy link

@lolmaus lolmaus commented Jul 28, 2014

@csdco, this shouldn't be so. Can you elaborate on your environment, versions, etc?

@charlie-s
Copy link

@charlie-s charlie-s commented Jul 28, 2014

My bad, I was using grunt-sass to compile, which is missing some features. I switched to grunt-contrib-sass and it's working as expected. Perhaps others will run into this comment.

@vlrprbttst
Copy link

@vlrprbttst vlrprbttst commented Sep 9, 2015

+1

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Sep 9, 2015

using calc all the time now, is a great css feature.

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Sep 9, 2015

@cimmanon i beleive ie8 is dead now, as is ie9 just about. Is there any way you could point me in the direction to fix this myself and put a patch in?

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Sep 9, 2015

Ok, here's my proposal for calc integration.

calc() expressions should be parsed into SassScript AST expressions. The parsing should use the calc grammar with the exception that it will allow Sass variable references in place of a value. I think this is the most natural expectation from the author's perspective.

Any SassScript expression that cannot be computed because the units are incompatible will no longer result in an error, instead that expression will output as a calc() expression.

Any calc() expression that can be resolved statically will evaluate to static value in the output instead of outputting as a calc() expression.

We need to decide how intermediate expressions will work.

For example:

$padding: 3px;
$margin: 2rem;
$runtime-expression: $padding + $margin;
$derived-expression: $runtime-expression / 2;

.output {
  calc-expression: $runtime-expression;
  derived-expression: $derived-expression
}

This could be an error at line 2 (because 3px + 2rem immediately resolves to a calc string) or it could output a value for derived-expression as calc( ( 3px + 2rem ) / 2 ). I lean towards the latter, even though it is harder to implement. Expressions would resolve their variable references into values, but otherwise unresolvable expressions would stay as expression references instead of values until output so that they can be composed with other expressions.

If we want SassScript expressions to be mixable in the same line of code with Calc expressions, I think we'd need a better parsing hint than interpolation (which always results in a string). Probably sass-script(...expression...). E.g. calc(1px + 2em + sass-script($foo % 3)) Personally, I think this is unnecessary at this time.

If we do this right, basically the calc() expression syntax is an implementation detail and not something that our users will need to use directly unless they want to.

@nex3, @xzyfer, @mgreter thoughts?

@davidkpiano
Copy link

@davidkpiano davidkpiano commented Sep 9, 2015

@chriseppstein would this proposal also support similar operations on calc expressions themselves?

E.g. calc(3px + 4rem) + 1rem - calc(2px + 5%) == calc(1px + 5rem - 5%), treating calc() as if they were parentheses.

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Sep 9, 2015

@davidkpiano Yes. though the output would probably be the following:

calc(3px + 4rem + 1rem - ( 2px + 5% ) )

We can write an expression simplifier as a follow-on feature at some point if it's a big deal.

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Sep 9, 2015

@chriseppstein 👍
thanks, even a simple version of this would save me so many headaches.

@nex3
Copy link
Contributor

@nex3 nex3 commented Sep 11, 2015

@chriseppstein

Any SassScript expression that cannot be computed because the units are incompatible will no longer result in an error, instead that expression will output as a calc() expression.

This has potentially serious browser support implications. Based on caniuse, there are about 10% of browsers in use today that don't fully support calc(), and for users targeting those browsers automatic calc() generation is a ticking time bomb—it'll look like it works when they test on their main browser, but will fail in potentially-confusing ways on more obscure platforms.

This is something I think we should consider someday, but I don't think it's time for it yet.

The rest of your proposal sounds solid to me. I'm even okay with explicitly declared calc() values supporting arithmetic with normal numbers. This makes calc() a clear signal that something not-fully-compile-time is going on without having to write it over and over again.

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Sep 13, 2015

@nex3 i fully disagree with this, for websites such as the one i run (https://boomf.com) the support on desktop browsers is at 99%ish of our desktop browsers, and there is different concerns on mobile.

to put it in context, we have made a business choice to not support below ie9, which supports calc with one minor bug (doesn't work in background position values)

We have been using this on production for close to a year with absolutely no problems, as it solves a lot of problems, and pushes the web forward. We give less than perfect to the 0.5% that is still usable.

I also disagree fundamentally with the aspect of not including something in case a professional in the field does not know how to use something in a reasonable and good manor, our job is to work around browser problems. you don't stop people using the feature by not including it, you just make it more difficult (and liable to bugs) The type of people that are going to be using experimental features, are likely to understand how to use these things correctly.

The only outlier is android, of which and with general responsive layouts these days, it is more needed in the field of desktop, which is at full support level pretty much. there is one minor bug in ie9 that does not really affect general usage.

screen shot 2015-09-13 at 13 18 07

one month of browser usage
screen shot 2015-09-13 at 13 25 03

and internet explorer usage
screen shot 2015-09-13 at 13 25 11

@cimmanon
Copy link

@cimmanon cimmanon commented Sep 13, 2015

Whether or not your target market supports calc is not the only consideration that needs to be made here. There are places where calc is explicitly forbidden according to the CSS specification, such as in media queries.

I am very uncomfortable with Sass silently converting expressions using incompatible units to calc. We already have a lot of confused users who don't understand that #{10}px is a string and that you can't do anything with it other than print it as is. You can't do greater/less than comparisons on it, you can't send it through the unitless function and get anything other than true. Having Sass do this without any explicit say-so is going to add to the confusion (why does Sass think this is a string? I am using numbers in my expression!).

@mcgoooo
Copy link

@mcgoooo mcgoooo commented Sep 13, 2015

@cimmanon that makes sense, it is definitely a tough and tricky problem and may cause some problems.
it would be nice to have it in sass, but if's not really possible/desirable, that is understandable, you have much better knowledge of how this would trip up users.

i may of jumped the gun a couple of years ago when i was bringing it up, but as a css feature it is mature enough to be used in production. with the cycle of android and lollipop replacing out the native browser with chrome, i can imagine it will be garnering wider attention in the next year or so. it can create some really amazing layouts.

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Sep 14, 2015

@nex3 10% is low enough for some users. I'd like to build this and keep it behind a feature flag that can be enabled at compile time. When the percentage is low enough we can change the default.

On Sep 11, 2015, at 2:06 PM, Natalie Weizenbaum wrote:

@chriseppstein
Any SassScript expression that cannot be computed because the units are incompatible will no longer result in an error, instead that expression will output as a calc() expression.

This has potentially serious browser support implications. Based on caniuse, there are about 10% of browsers in use today that don't fully support calc(), and for users targeting those browsers automatic calc() generation is a ticking time bomb—it'll look like it works when they test on their main browser, but will fail in potentially-confusing ways on more obscure platforms.

@xzyfer
Copy link

@xzyfer xzyfer commented Sep 14, 2015

Apologies for my naivety. Is the issue with calc() purely that it allows
arithmetic on units that Sass considers erroneous, or is there more to it?
On 14 Sep 2015 14:00, "Chris Eppstein" notifications@github.com wrote:

@nex3 10% is low enough for some users. I'd like to build this and keep it
behind a feature flag that can be enabled at compile time. When the
percentage is low enough we can change the default.

On Sep 11, 2015, at 2:06 PM, Natalie Weizenbaum wrote:

@chriseppstein
Any SassScript expression that cannot be computed because the units are
incompatible will no longer result in an error, instead that expression
will output as a calc() expression.

This has potentially serious browser support implications. Based on
caniuse, there are about 10% of browsers in use today that don't fully
support calc(), and for users targeting those browsers automatic calc()
generation is a ticking time bomb—it'll look like it works when they test
on their main browser, but will fail in potentially-confusing ways on more
obscure platforms.


Reply to this email directly or view it on GitHub
#818 (comment).

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Sep 14, 2015

Is the issue with calc() purely that it allows arithmetic on units that Sass considers erroneous, or is there more to it?

@xzyfer There's lots of issues with calc.

  • Presently we treat calc values as essentially strings right now, so when you change a variable from 2em to calc(2em - 2px) which is still conceptually a numeric value but it can't be used in any further mathematical calculations because it became a string.
  • We can optimize calc values that can be fully resolved at compile time but we don't.
  • We can change an error into a success (as you mentioned)
  • Calc Expressions and SassScript Expressions are two ways of writing numeric expressions, but once parsed to an expression (AST), they are like scss vs sass syntaxes there's no reason they can't interoperate.

@chriseppstein
Copy link

@chriseppstein chriseppstein commented Sep 14, 2015

I am very uncomfortable with Sass silently converting expressions using incompatible units to calc. We already have a lot of confused users who don't understand that #{10}px is a string and that you can't do anything with it other than print it as is. You can't do greater/less than comparisons on it, you can't send it through the unitless function and get anything other than true. Having Sass do this without any explicit say-so is going to add to the confusion (why does Sass think this is a string? I am using numbers in my expression!).

@cimmanon I think there's some miscommunication here. My proposal is not that calc would evaluate immediately to a string, but instead to some intermediate "expression" representation that is not a simple value yet. A lot can be done to that expression that can't be done to a string.

You can't do greater/less than comparisons on it

This is true, trying to do a comparison would raise an error because there's not a calc-compatible if statement (yet).

you can't send it through the unitless function and get anything other than true

Not so, I can determine that there is no unit on the value calc(10 - 2) and that the unit of calc(2em - 2px) is em without actually performing the math on it.

why does Sass think this is a string?

Sass wouldn't think it's a string. It would give better error messages with my proposal than the current system. E.g. calc(2em + 3px) % 1em would return the error like "Error: cannot take the modulus of runtime expressions." (because calc doesn't have a modulo operator).

@cyraid
Copy link

@cyraid cyraid commented Dec 19, 2019

I used:

  $line-height: calc(1rem + 0.1vmin);
  max-height: calc((#{$line-height} * 4) + (#{$padding * 2}));

This way $line-height could be used on it's own in other places. Line-height could not be used on it's own, if say, it were to be used in a different location as a string. It's always nice to prepare for the future, and future changes without worrying of things breaking. :)

The merging calc could optimize and reduce values, but mostly it's for capturing invalid combinations like (1px + 100%). Scenarios like these could be output into a calc() result. But also if you have a calc() and another calc() both could be appended to say calc(($var1) + ($var2)). Something as simple as that could be accomplished.

@RedClaySchoolDistrict
Copy link

@RedClaySchoolDistrict RedClaySchoolDistrict commented Feb 28, 2020

If "calc" is "hacky" why not output the result with SCSS?

SCSS
width: calc(20px+ 20px);

Output
width: 40px;

That would eliminate the "hackiness" and make it more supported and fix the variable issue.

@nex3 nex3 mentioned this issue Dec 31, 2020
@nex3 nex3 changed the title Variables don't work with calc() First-class calc Dec 31, 2020
@nex3
Copy link
Contributor

@nex3 nex3 commented Dec 31, 2020

I'm merging #2186 into this issue and retargeting this at supporting first-class calc in general rather than just allowing variables in calc().

nex3 added a commit that referenced this issue Dec 31, 2020
See #818
nex3 added a commit that referenced this issue Dec 31, 2020
See #818
nex3 added a commit that referenced this issue Jan 9, 2021
See #818

Co-authored-by: Jennifer Thakar <jathak@google.com>
nex3 added a commit to sass/sass-site that referenced this issue Mar 15, 2021
nex3 added a commit to sass/sass-site that referenced this issue Mar 15, 2021
See sass/sass#818
Co-authored-by: Jennifer Thakar <jathak@google.com>
@nex3 nex3 mentioned this issue Jun 1, 2021
8 tasks
@nex3
Copy link
Contributor

@nex3 nex3 commented Jun 17, 2021

The blog post for this has been out for three months, and there have been no objections of any kind, so I'm going to mark this as accepted. I expect to start work on specs and implementation in July.

nex3 added a commit that referenced this issue Jun 17, 2021
See #818
nex3 added a commit that referenced this issue Jun 17, 2021
See #818
nex3 added a commit to sass/sass-site that referenced this issue Sep 2, 2021
nex3 added a commit to sass/sass-site that referenced this issue Sep 2, 2021
nex3 added a commit to sass/sass-spec that referenced this issue Sep 13, 2021
Add specs for first-class calc

See sass/sass#818
nex3 added a commit to sass/dart-sass that referenced this issue Sep 13, 2021
nex3 added a commit to sass/sass-site that referenced this issue Sep 13, 2021
See sass/sass#818

Co-authored-by: Awjin Ahn <awjin@google.com>
@nex3
Copy link
Contributor

@nex3 nex3 commented Sep 14, 2021

This has now been released as part of Dart Sass 1.40.0. I'm going to keep this tracking issue open until we finish landing support for this in the embedded protocol and JS API, though.

@lehni
Copy link

@lehni lehni commented Sep 16, 2021

@nex3 great news, thank you for prioritising this! I will spend time transitioning our code base to this soon, and report back how it's been working for us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet