sass / sass

Sass makes CSS fun!
https://sass-lang.com
MIT License
15.06k stars 2.15k forks source link

Stop using / as division #2565

Open nex3 opened 5 years ago

nex3 commented 5 years ago

The current plan:

  1. Create an automated migration tool that can migrate users from /-as-division to a divide() function.
  2. Deprecate /-as-division. Any time a / would be interpreted unambiguously as division, or an ambiguous / would be made unambiguous (for example by being stored in a variable), we produce a deprecation warning .
  3. Add a top-level divide() function (math.div() in the new module system). This will work exactly the same way division works today, with the obvious exception that it will never be treated as a slash separator.
  4. Add a new slash-separated list type to represent slash as a separator. For the time being, this will not have a literal syntax.
  5. Add a slash-list() function (list.slash() in the new module system) that returns a slash-separated list. Providing the ability to work with slash-separated lists even before they have a literal syntax will help stylesheets be forwards-compatible with the new wold.
  6. Wait until at least three months after the module system is released.
  7. Release Dart Sass 2.0.0, which parses / as a list separator that binds more loosely than space but more tightly than comma.
  8. Add support for first class calc(), which will allow /-as-division in the form of calc(5px / 2).
  9. Deprecate the slash-list()/list.slash() function in favor of using the literal syntax.
  10. Whenever Dart Sass 3.0.0 releases, remove slash-list() and list.slash().

The original issue:

When Sass was first designed, the most prominent use of / as a separator in CSS was the short-form font property, which used it to separate values for font-size and line-height. Because this was a relatively narrow edge case, we were able to use / as the division operator as well, with some heuristics to decide between them. When / was used as a separator, the value it produced was actually the divided number, but it was rendered with the slash instead.

However, the slash separator has been seeing more use in recent CSS specs. The grid-row property uses it to separate values for grid-row-start and grid-row-end, and Colors Level 4 uses it in many color functions (including the new recommended syntax for rgb() and hsl()) to separate the alpha value from the rest of the parameters.

The increased prominence of this syntax means that the sharp edges of the heuristics we use to distinguish between /-as-separator and /-as-division will start cutting more users. Once Sass supports the new color function syntax (https://github.com/sass/sass/issues/2564), users are likely to try passing the alpha by variable, which will currently fail. We need to decide whether to do something about this, and if so, what.

How do we represent division?

Today, division is represented by the operator /, which as discussed above is ambiguous. We could fairly easily add a new syntax for this, which would allow us to migrate users away from /, eventually leaving it as unambiguously a separator.

I see a few options for this:

What is a slash separator?

If we do decide to free up / to unambiguously represent a separator, we need to figure out how that separator works in terms of SassScript. This is particularly tricky because its scope varies from property to property. In font, it's tightly bound--the slash in italic small-caps bold 16px/2 cursive separates 16px from 2, but it doesn't separate italic small-caps bold 16px from 2 cursive. However, in grid-row and the color functions, it's loosely bound--the slash in rgb(1 2 3 / 0.5) separates 1 2 3 from 0.5, and is not bound to 3 in particular.

I think it makes sense in general to consider / another kind of list separator, so that it works smoothly with existing APIs. But because of the scoping differences it's not clear whether it should bind more tightly than space-separated lists or not. That is, should 1 2 / 3 4 be equivalent to (1 2) / (3 4) or 1 (2 / 3) 4?

nex3 commented 5 years ago

@tabatkins Any insight you can provide would be welcome. In particular:

jelbourn commented 5 years ago

Would it make sense to continue interpreting / as division in places where it would otherwise be invalid CSS, otherwise treating it as the native separator? That would necessitate introducing a division function, but it would only need to be used when there would be ambiguity. The compiler could error on any ambiguity.

Admittedly, this could get confusing. It might be valuable to analyze existing libraries to see how prevalent ambiguities would actually be.

nex3 commented 5 years ago

Sass doesn't have any contextual information about the property which it's evaluating code for. This is both because we have a general policy of encoding as little information about CSS itself as possible, and because there's no way to know what property a given variable or argument might be used with. Without that, I'm not sure there's a way to know which uses of / would be valid CSS and which wouldn't.

rickcnagy commented 5 years ago

Is there any reason that // isn’t an option? It would be similar to Python 3.

nex3 commented 5 years ago

// is already the syntax for a single-line/silent comment.

tabatkins commented 5 years ago

How likely is the / separator to continue to be used in new rules/functions?

We will continue using it rarely, as we do today, as a secondary separator.

If / is used more, is it likely to bind tightly or loosely?

The general rule, which we almost always stick to, is that CSS separates repetitions of values by ,, then within each repetition (or within the whole value, for non-repeating properties), primarily uses ` (space) to separate tokens, only using/as a separator when required due to ambiguity. We don't have a strict notion of binding strength for the/; the only firm rule is that,binds more loosely than space or/`.

(More precisely, sometimes / binds tightly to the next token, like in font or rgb(), while in others it binds loosely and separates groups of space-separated tokens, like in border-radius or border-image.)

How likely is ~ to appear as syntax in a property value?

Beyond it's current use in selectors, I don't expect it to show up property values; we tend to be conservative about new characters to reduce grawlix-ness. That said, it's still possible that we add a selector() function or similar at some point in the future, allowing ~ to show up at the property level. (Selectors will not show up "unshielded" by a function tho, I'm sure of that; their syntax is too open-ended to reasonably do that.)

That said, the fact that it is already used in Selectors does reduce the friction for using it some time in the future; other proposals for using new ASCII characters sometimes founder on "that's easy to type on an English keyboard, but not on my [other-language] keyboard", but ~ is already a sunk cost in that regard.

We did vacate the naked-parenthesis space for y'all, and I'm willing to defend that line, so if you want to reserve "within ()" as the space for safe free-form math, I think you can safely do so. Or, of course, just use "calc()". ^_^

nex3 commented 5 years ago

The general rule, which we almost always stick to, is that CSS separates repetitions of values by ,, then within each repetition (or within the whole value, for non-repeating properties), primarily uses ` (space) to separate tokens, only using/` as a separator when required due to ambiguity.

Out of curiosity, why did you end up with rgb(r g b / a) rather than rgb(r g b a)? It seems like the latter shouldn't be ambiguous.

(More precisely, sometimes / binds tightly to the next token, like in font or rgb(), while in others it binds loosely and separates groups of space-separated tokens, like in border-radius or border-image.)

I actually hadn't realized that border-radius and border-image had / as part of their value grammars. In that case, binding looser than space definitely hits the most properties today, and there's no reason to think that won't hold true in the future. It makes the parse tree for font pretty weird, but it will still render fine.

That said, it's still possible that we add a selector() function or similar at some point in the future, allowing ~ to show up at the property level. (Selectors will not show up "unshielded" by a function tho, I'm sure of that; their syntax is too open-ended to reasonably do that.)

Anything shielded in a function name is totally fine for us syntax-wise. We have a strong precedent with calc() for having special parsing for certain functions.

That said, the fact that it is already used in Selectors does reduce the friction for using it some time in the future; other proposals for using new ASCII characters sometimes founder on "that's easy to type on an English keyboard, but not on my [other-language] keyboard", but ~ is already a sunk cost in that regard.

That also makes it appealing for us. On the whole, it sounds like ~ is a likely to be our best option if we do decide to continue to use a single character.

We did vacate the naked-parenthesis space for y'all, and I'm willing to defend that line, so if you want to reserve "within ()" as the space for safe free-form math, I think you can safely do so. Or, of course, just use "calc()". ^_^

Leaving parentheses alone has been extremely helpful :bowing_woman:, and in fact it is one of the heuristics we use today to decide what to do with /. But as discussed above, those heuristics are likely to become more annoying the more prevalent slash-separation becomes.

tabatkins commented 5 years ago

Out of curiosity, why did you end up with rgb(r g b / a) rather than rgb(r g b a)? It seems like the latter shouldn't be ambiguous.

It's not technically ambiguous in rgb() and hsl(), but the / does serve as a nicely visible indicator that "hey, this color has an explicit alpha": in rgb(0 0 0 0) vs rgb(0 0 0) vs rgb(0 0 0 / 0), the first two aren't super-apparent on first glance, but the last two are really obvious.

But we were also trying to solve the problem for color(), which can take any number of arguments after the colorspace name, and we wanted to enable some of those to be optional. There's no easy way to add an easily-readable, easily-writeable, unambiguous alpha value there, without relying on some form of separator, and we already wanted to use the , for its normal "separating repetitions" functionality, so you could supply a fallback color. / ends up serving its "disambiguate a value from similar surrounding values" function really well there, so we applied it globally to the other colors.

Finally, as a minor but significant additional point, gray() only takes a single color-relevant argument, so with alpha it has two values. Between rgb/hsl/lab/lch all taking 3+alpha, color taking n+alpha, and gray taking 1+alpha, having an explicit visual indicator of when the alpha was being set makes things more readable in general, without having to remember how many arguments any particular function takes before the alpha.

Anything shielded in a function name is totally fine for us syntax-wise. We have a strong precedent with calc() for having special parsing for certain functions.

Cool. We'll never have a selector more complicated than an ID unshielded. (I know that still offers some ambiguity for you, as both IDs and hex colors use the same token, but eh. Most of the time it's still unambiguous due to character sets. ^_^)

nex3 commented 5 years ago

I discussed this with a crack team of Sass users at Google, and we came up with the following plan:

  1. Deprecate /-as-division. Any time a / would be interpreted unambiguously as division, or an ambiguous / would be made unambiguous (for example by being stored in a variable), we produce a deprecation warning .
  2. Add a top-level divide() function (math.div() in the new module system). This will work exactly the same way division works today, with the obvious exception that it will never be treated as a slash separator.
  3. Add a new slash-separated list type to represent slash as a separator. For the time being, this will not have a literal syntax.
  4. Add a slash-list() function (list.slash() in the new module system) that returns a slash-separated list. Providing the ability to work with slash-separated lists even before they have a literal syntax will help stylesheets be forwards-compatible with the new wold.
  5. After a suitable deprecation period, start parsing / as a list separator that binds more loosely than space but more tightly than comma.
  6. Deprecate the slash-list()/list.slash() function in favor of using the literal syntax.
  7. In the next major revision, remove slash-list() and list.slash().

This is not set in stone. It's a big change, and I want to make sure people have a chance to provide feedback before I start working on a more formal spec. I'll leave this open for at least a few weeks so folks have a chance to chime in.

One major open question (at least for Dart Sass) is, how long do we leave the deprecation before we drop support for / as division, and do we do a major version bump at that point? Because this is a CSS compatibility issue, Dart Sass's compatibility policy gives us license to remove the old behavior as early as three months after the deprecation warning is released, without a major version bump. However, this affects a much broader range of stylesheets than most, so I think that's too short. Would six months but no major version bump work?

fedfigca commented 5 years ago

My ¢2 is that / is commonly used as division almost everywhere else, using a different syntax will be yet another challenge for new users, and for old code alike, so trying to keep it should be the priority.

Using some sort of encapsulation for the arithmetic might be one way to proceed in places where the slash could mean a separator, or any other way, but if we have to teach new users (who probably already program other languages) a different syntax for math, it might become one of those issues that make people look for alternatives.

weo3dev commented 5 years ago

is that / is commonly used as division almost everywhere else

Guess what. CSS is not "everywhere else", and, just like any programming language, CSS has a designed syntax agreed upon not just by open source organizations but also the platforms in which we see CSS in action.

@fedfigca All CSS developers need to [came across too strongly here] get up to speed and come to recognize the paradigm shift that is CSS Grid. It was built specifically to fix a two-decades-old problem in the CSS spec of there not being any one tool specifically for layout. We have used hacks and workarounds for over 20 years because we never had CSS grid from the outset.

Now we do, and part of its designated and designed spec is using the forward-slash as a means of defining columns and rows, and spans of columns and rows for any grid child.

Any NEW user of CSS should be learning that for layout, we now use CSS Grid. Period. Full stop.

Saying, "so trying to keep it [forward slash as division only in Sass] should be the priority" is really ignoring the larger picture of the progression of the language Sass is built to parse.

weo3dev commented 5 years ago

@nex3 I do not envy you, and wish you the best of luck in moving forward with the planned roadmap.

fedfigca commented 5 years ago

is that / is commonly used as division almost everywhere else

Guess what. CSS is not "everywhere else", and, just like any programming language, CSS has a designed syntax agreed upon not just by open source organizations but also the platforms in which we see CSS in action.

No need to "guess what" anybody here, we're just talking and pitching in our points and opinions, if you come at people like that you'll scare off valuable opinions 😉

I still think that doing math differently is a mistake, is not my decision to make, it'll be what it'll be, and people will use whatever they find the most comfortable and less painful.

nex3 commented 5 years ago

Using some sort of encapsulation for the arithmetic might be one way to proceed in places where the slash could mean a separator, or any other way, but if we have to teach new users (who probably already program other languages) a different syntax for math, it might become one of those issues that make people look for alternatives.

We do eventually plan to have Sass natively support calc(): https://github.com/sass/sass/issues/2186. Once that lands, if you do a calculation that's fully computable at compile-time, it will return a number, so you'll be able to write calc($width / $height) instead of divide($width, $height).

There are some very real downsides to making that the only way division works, though. First, encouraging users to use calc() for local calculations can hide errors. Rather than failing, we'd probably produce a plain-CSS calc() for any unit math Sass doesn't understand, so if someone tries to write something like calc(1/2em) they'll end up with a broken property that's hard to debug. Having a Sass-only way to divide is a good way of getting users to express when they expect an operation to be doable by Sass, so Sass can fail when it's not doable.

Second, it means we can't solve this issue until #2186 is completely solved. First-class calc is a very large and complex feature, and designing, speccing, and implementing it will take a long time—especially since we'd need to wait until LibSass supports it to even begin deprecating /-as-division. I don't want to speak for @xzyfer, but I'd guess that LibSass's development time is going to go almost entirely towards the module system for the next six months at least, so that means it may be as much as a year before we could even begin to address this issue.

fedfigca commented 5 years ago

There are some very real downsides to making that the only way division works, though. First, encouraging users to use calc() for local calculations can hide errors. Rather than failing, we'd probably produce a plain-CSS calc() for any unit math Sass doesn't understand, so if someone tries to write something like calc(1/2em) they'll end up with a broken property that's hard to debug. Having a Sass-only way to divide is a good way of getting users to express when they expect an operation to be doable by Sass, so Sass can fail when it's not doable.

Your points about doing it all with calc() are very true, doing something different like math() (or any better name you can think of) and allowing that to take the usual math syntax in a similar way of what calc takes might be a better way than to have something to just divide, and letting Sass know that it should take over the calculations instead of producing calc() values

Cleecanth commented 5 years ago

I'm sure I'm leaving out a ton of complexity, but I second the idea of using a "fenced"/psuedo-function for Sass-specific math (something like math(2/3)).

It feels cleaner than eliminating a common character for math functions and it makes for a pretty straight-forward upgrade path.

Having written some fairly complex Sass over the past 6-or-so years, I've tended to gravitate more and more towards clarity over convenience, though, so I'm a bit biased. At the same time, a wrapper around Sass-specific math still feels like it maintains the spirit of both languages (CSS and Sass) while still being pretty basic to write.

I also realize this isn't one of the original proposals, so it's very possible there's a good reason not to do it. In which case, I could probably get behind using ~ (or really any other character[s]).

nex3 commented 5 years ago

Something like math() definitely seems tempting once you understand all the ins and outs of the problem, but I suspect for a user who's not super familiar with Sass (and possibly not with CSS either), it's likely to be very confusing. It's not obvious from seeing math() how it's different than calc() (especially once calc() supports a superset of its behavior), why you need to use it for division and not other arithmetic operations, or even whether it's a Sass feature or a plain CSS feature.

It occurs to me that it may be possible to mitigate (although probably not eliminate) the set of invalid calc()s that could be generated. CSS clearly defines what it means for a calc()'s type to be valid or not, so we can eagerly fail if someone attempts to write calc(1/2em). This leads me to believe that the best long-term solution for writing division using / is to do it within calc().

nex3 commented 5 years ago

I've brought everyone's concerns to my user team, and we've affirmed that we want to proceed using divide() without waiting for calc() support or adding an interim math() syntax. However, we will wait to enable with the deprecation until we have a migration tool in place to make it easy for users to update their stylesheets.

We've also decided on a more concrete timeline. We'll launch the deprecation as soon as possible after the migration tool is implemented, then land the removal in a major version release no earlier than three months after the launch of the module system. We want to ensure a healthy overlap of module-system-supporting versions and division-operator-supporting versions so users can opt to migrate to the module system without migrating away from the division operator.

weo3dev commented 5 years ago

Hey @fedfigca - apologies for not responding earlier - thank you for calling me out on my attitude that is not a benefit to the community. Hoping the decision to move forward is a good one. Peace.

nex3 commented 5 years ago

As a small adjustment, I think we should also make the divide() function accept string arguments and do the same thing the / operator does with strings now (create an unquoted string containing /). It should definitely emit a deprecation warning when a string is passed, but allowing strings will make automated migration much easier.

nex3 commented 5 years ago

The official proposal pull request is now live for comments!

mirisuzanne commented 5 years ago

I understand that I'm late to this conversation, but I want to push back on a few things as an author:

One of Sass's long-term strengths has been the shallow learning-curve for people who do not consider themselves programmers. Another has been the ability to do math in CSS (especially before calc() was reliable, and still for math that should be precompiled). I'd worry that handling one specific math operator in a totally new way – unlike all other math operators in Sass – causes issues on both fronts. If that had been the case when I picked up Sass, I likely would have been scared away by the strange seemingly-arbitrary design of that feature.

I'm also starting to question the long-term compiled-calc()proposal. While it's super elegant at first glance - it strikes me as a potential repeat of the @import issue (and - namespacing issue) – ignoring the general rule that Sass-compiled additions to CSS syntax should be clearly marked and explicit. PostCSS plugins have made a mess out of this several times (see attempts to precompile custom properties) – and it's always frustrating as an author to have my CSS code altered without clear ways to turn that off.

I think an explicit math function of some kind would actually be more clear on both fronts - short and long term. I might consider naming it something like precalc() to acknowledge the similarity, and highlight the difference between the two. For now, prrecalc() could simply provide a safe context for /-as-division, and down the road it could potentially take on the proposed smart-calc() role, while still being explicit.

mirisuzanne commented 5 years ago

The module system also helps clear up any confusion - since any precalc() function would have to be loaded explicitly from a Sass namespace, e.g. math.precalc() – where calc() and other sass functions are global.

nex3 commented 5 years ago

One of Sass's long-term strengths has been the shallow learning-curve for people who do not consider themselves programmers. Another has been the ability to do math in CSS (especially before calc() was reliable, and still for math that should be precompiled). I'd worry that handling one specific math operator in a totally new way – unlike all other math operators in Sass – causes issues on both fronts. If that had been the case when I picked up Sass, I likely would have been scared away by the strange seemingly-arbitrary design of that feature.

I don't think it's quite fair to say that divide() is unlike all other math operators in Sass—things like rounding, absolute values, and so on are all functions. Once we add exponentiation, that'll be a function as well, even though in some programming languages it's **.

I see what you're saying that division is most commonly represented in programming languages as /, and that many users will expect it to be there. I'd like for it to be there too, but we have to balance that out with the increasingly-painful problems with CSS treating / as a separator. And I don't think it's likely to be as upsetting as you do, especially if we can provide error messages that explain what's going on when it's likely that a user accidentally used /.

I'm also starting to question the long-term compiled-calc()proposal. While it's super elegant at first glance - it strikes me as a potential repeat of the @import issue (and - namespacing issue) – ignoring the general rule that Sass-compiled additions to CSS syntax should be clearly marked and explicit. PostCSS plugins have made a mess out of this several times (see attempts to precompile custom properties) – and it's always frustrating as an author to have my CSS code altered without clear ways to turn that off.

I think an explicit math function of some kind would actually be more clear on both fronts - short and long term. I might consider naming it something like precalc() to acknowledge the similarity, and highlight the difference between the two. For now, prrecalc() could simply provide a safe context for /-as-division, and down the road it could potentially take on the proposed smart-calc() role, while still being explicit.

I'd say that first-class calc() will be more like Sass's support for CSS units and colors than like Sass's @import. We're just using calc() to mean the same thing it does in CSS, but with extra abilities. The fact that you can use variables in it is no different than using variables in a call to rgb(), and still clearly signals that something Sass-y is going on; and the fact that we can detect when it's completely resolvable at runtime and emit 2.5px rather than calc(5px/2) is just an optimization, like converting hsl() to hex color codes.

mirisuzanne commented 5 years ago

I'd say that first-class calc() will be more like Sass's support for CSS units and colors than like Sass's @import.

That's a great point. Put in that context, I see what you mean, and why calc() would work. We just have to avoid the over-optimizations that some tools attempt: like the minimizer that recently removed the unit from a 0%, totally breaking my hsl(). I trust that.

I don't think I'm wrong to be somewhat worried about divide(), though. I realize something has to be done, but I'm concerned with keeping it as CSS-like and readable as possible. Of the proposals mentioned, divide() feels the least CSS-like to me. In CSS, there is an established pattern for / as division, and for fencing math equations inside a function – but there is no pattern for basic math operations themselves being functions. sass.math() seems both more readable, and more CSS-like to me than divide().

I'm not really thinking about how most programming languages represent division. I actually use functional versions of + - * / = in my code often – I don't think they're bad to have – but I do think they stray farther from CSS in a very noticeable way for people unfamiliar with imperative languages. Unlike pow() or abs() or other advanced math functions, + - * / = are pretty universal symbols outside any technical field. I can generally write those in an email to anyone I know, without any confusion. I'm specifically worried about how this would come across to people who don't work with other languages besides CSS.

nex3 commented 5 years ago

I don't think I'm wrong to be somewhat worried about divide(), though. I realize something has to be done, but I'm concerned with keeping it as CSS-like and readable as possible. Of the proposals mentioned, divide() feels the least CSS-like to me. In CSS, there is an established pattern for / as division, and for fencing math equations inside a function – but there is no pattern for basic math operations themselves being functions.

This is becoming much less true. Values and Units Level 4 currently defines min(), max(), clamp(), sin(), cos(), tan(), asin(), acos(), atan(), atan2(), pow(), sqrt() and hypot(). Most of these aren't yet implemented in browsers, but we should design for the future where they probably will be.

I'm not really thinking about how most programming languages represent division. I actually use functional versions of + - * / = in my code often – I don't think they're bad to have – but I do think they stray farther from CSS in a very noticeable way for people unfamiliar with imperative languages. Unlike pow() or abs() or other advanced math functions, + - * / = are pretty universal symbols outside any technical field. I can generally write those in an email to anyone I know, without any confusion. I'm specifically worried about how this would come across to people who don't work with other languages besides CSS.

This is fair, but I'd expect users from a non-programming background to come in with much looser expectations for how math will be represented than people who have programmed before. Even given that, I don't think choosing a different operator (like ~) is likely to be much friendlier than math.div()—there's no other character that has any mindshare for non-technical users as division, except I guess ÷ which is prohibitively difficult to type. Something like precalc() is also likely to trip novice users up, given that all other mathematical operations can be freely written outside of a function context.

I definitely see what you're saying about the downsides of not supporting /, but I don't see that the alternatives are better. Choosing a different operator character will make reading division harder as well as writing, and a Sass-only fence like math() or precalc() is impossible for users to guess and will provide very limited value once we support first-class calc().

mirisuzanne commented 5 years ago

I think what I’m actually suggesting is that the most CSS-like and consistent is to fence all math inside some form of calc function, rather than trying to find the perfect way to special-case division. It’s a bigger adjustment from Sass historically- but I think it might actually add clarity to both reading and writing the language. At least, I tend to try and fence math somehow anyway, for readability. If we’re gonna make some breaking change - we could rethink our math solution entirely. Really, that’s what you are proposing with calc() as well - I’m just suggesting that a math() or precalc() gets us a lot closer in both syntax and concept than a div() special-case. Also a simpler manual upgrade path both times.

Though I’m totally in favor of all math operations getting functional alternatives as well, now that we can namespace them :)

nex3 commented 5 years ago

The backwards-incompatibility pain of deprecating all mathematical operators is almost certainly too severe to bear if it's not strictly necessary for CSS compatibility, even if it were something we thought would be a substantial readability improvement, which I'm honestly pretty suspicious of. I think many Sass users, both novice and invested, would disagree with your sense that explicitly bracketing all math is a clarity win.

mirisuzanne commented 5 years ago

Haha, that's totally fair. :) Deprecating all un-fenced math is clearly too severe, and I know nothing about actual difficulty programming these things in Dart. So one last question: is there a major downside here to providing the options:

Is that downside mostly in the time it would take to write that feature?

nex3 commented 5 years ago

Adding more features always comes at a cost to the overall complexity of the language, and that's doubly true for multiple ways to do the same thing—it means users have to choose when to use one or the other, and readers familiar with one have to figure out why the other is used instead and if there's any material difference between them. Ultimately this usually ends in a style guide enforcing only one option anyway.

As far as fencing existing math syntax goes, calc() will be a way to do that.

atg commented 4 years ago

I would question whether the CSS grid syntax is actually more important than backwards compatibility (presumably the color syntax can be special cased).

I would also question whether Google is a representative sample of sass users. As far as I'm aware Google has all their code in a monorepo and pays engineers to perform these kinds of migrations. The proposed migration plan seems perfect for Google, but does it not seem perfect for the rest of us.

6 months is certainly too short for a transition period -- a change of this magnitude would take years. I don't know if I could even locate every sass compiler on my computer, let alone update them all regularly. Sass compilers are like glitter, they get everywhere - globally, as project dependencies, in CIs, in text editors.

The biggest problem is taking valid code, making it invalid for a period of time, and then making it valid again X amount of time later with totally different semantics. After the transition period is up it will be impossible to discern which code is pre-transition (MUST be migrated) and which is post-transition (must NOT be migrated).

nex3 commented 4 years ago

Breaking changes certainly do suck, but in some cases they're unavoidable. One of the most important guarantees Sass makes is that it's a CSS superset, which means that plain CSS must be valid Sass with the same meaning it has in CSS. Sticking to this principle, even when it's meant painful migrations, is a big part of what's made Sass successful over the long term. We try to make decisions that will still be right five or ten years down the road.

The CSS working group's use of forward slash as a separator has increased considerably over the last few years, and it's a reasonable bet that that will continue. Maybe we could continue to special-case the color syntax (although I emphasize that it's very awkward to make that workaround happen), but we can't special-case everything. A transition needs to happen, even if it hurts.

khamer commented 3 years ago

I'm not sure if there's a better place to document this; let me know. One of the side effects is that projects that hadn't transitioned to @use from @import may run into issues. It's possible to create a shim _divide.scss, and you can import once globally and then use divide() instead of math.div() and avoid putting @use statements in other files yet:

@use "sass:math";

@function divide($a, $b) {
    @return math.div($a, $b);
}

I understand why you wouldn't want to add a new, unnamespaced function divide(), but this shim should avoid the warning for new projects without them needing to refactor anything from @import to @use yet.

antoniandre commented 3 years ago

Atm, the new math.div() syntax still breaks in vue-cli (I am also trying in Vite where it does not break), but we can't upgrade the sass-loader dependency yet since vue-cli runs on Webpack 4. Not sure if there's a solution yet. But for the moment preparing my code to be compatible, I think @khamer's idea is great, in order to have that change remaining only in a single place for when it will be supported.

@use "sass:math";

@function divide($a, $b) {
    @return math.div($a, $b);
}

Also in a long term, I like the idea to not have to import sass:math at the top of all the files which use the new math.divide, especially in all the .vue components. :/ So in the end I think this divide mixin looks great. I feel it's also shorter to type and simpler to read than math.div! 👍

nex3 commented 3 years ago

@khamer

I'm not sure if there's a better place to document this; let me know. One of the side effects is that projects that hadn't transitioned to @use from @import may run into issues.

It's entirely possible to use @use in files that otherwise use @import (or are loaded with @import). The new module system was designed to be as interoperable with the old @import world as possible.

@antoniandre

Atm, the new math.div() syntax still breaks in vue-cli (I am also trying in Vite where it does not break), but we can't upgrade the sass-loader dependency yet since vue-cli runs on Webpack 4.

Breaks it how? Is there a tracking bug for this?

nex3 commented 3 years ago

@moleCuleFFF Please remember the community guidelines: Disagreement is no excuse for poor manners.

Path delimiters are not part of the expression syntax of most programming languages. I know of no language other than Sass where the / in 1px / 2px could be a separator or a division operator depending only on the context of the expression in question. The fact is that as long as CSS was going to expand its use of the / separator as it has over the last few years, our hands are tied: we needed to get rid of /-as-division. The only question is what the right way of doing so was, and the time for discussing that has come and gone.

lk77 commented 3 years ago

hello,

how to disable this warning ?

running the migrator against node_modules does not seems to be a solution to me

for me it does not make sense to display a warning at all, just talk about it in the upgrade guide for v2, but stop spamming with deprecation notices when the end user don't care about, and can't do nothing about it because those deprecation notices are from node_modules.

kerryj89 commented 3 years ago

hello,

how to disable this warning ?

running the migrator against node_modules does not seems to be a solution to me

for me it does not make sense to display a warning at all, just talk about it in the upgrade guide for v2, but stop spamming with deprecation notices when the end user don't care about, and can't do nothing about it because those deprecation notices are from node_modules.

It would be nice to at least have an quiet and quiet-deps options in node-sass that ignores node_modules directory as I'm guessing for 99% of the users they will be importing other peoples libraries that they don't have control over. Some dev importing it will probably hit the deprecation warning and let that library's dev know about it.

flachware commented 3 years ago

I admit that this is a very tricky issue, but especially as a long-term Sass user I’m sorry to say that from my point of view the math.div() solution does not do justice to the claim of being 'syntactically awesome'.

What we have now:

@use "sass:math";

div {
  padding: $space-sm + 2px;
  margin: math.div($space-sm, 2); 
}

The problems I see here: a) it is confusing that one basic arithmetic operation is represented by a function call whereas the other three are not, b) the function requires knowledge about the module system, c) it is hard to read, especially in case there are multiple divisions in a single calculation that now require nested function calls, d) it is cumbersome to write.

Discarded alternatives:

div {
  padding: $space-sm + 2px;
  margin: $space-sm ÷ 2;
}

div {
  padding: ($space-sm + 2px);
  margin: ($space-sm / 2);
}

div {
  padding: $space-sm + 2px;
  margin: ($space-sm / 2);
}

I’m a bit surprised that some of the mentioned alternatives above were ruled out early and did not get any further consideration.

÷: granted, a key combination is required to enter the division sign, which is not great. But having to type @use "sass:math"; math.div(,) for a simple division isn’t great either – plus the cons mentioned above. However, I do see a legibility issue here, ÷ looks arguably a bit close to + on low-res screens.

(): fencing all math inside parentheses was ruled out as being a 'too severe' change. I fail to see the big disadvantage here, existing Sass code has to be migrated either way and new projects can adopt any syntax. This approach would be consistent, more concise than any other solution, does not require knowledge about the module system, and it would be universal (parentheses are used in maths anyway). Deprecating an operator is a severe change in any case.

And then there is the option to only fence calculations that contain the division sign with parentheses, like it is done in Stylus (and probably other preprocessors). This is a bit inconsistent, but that’s really the only con here. Maybe I’m missing something here, but either of the 'naked parentheses' options looks more promising to me than the concluded solution.

I am aware that it is technically 'too late' for suggestions, but I believe it’s never too late for improvements, so I made my case for a simpler solution.

nex3 commented 3 years ago

@kerryj89 https://github.com/sass/sass/issues/3065 is tracking adding those flags to Node Sass. It's difficult to square them with the current importer API, so unfortunately we have to wait until the new importer API is released—but fortunately we're actively working on that right now.

@flachware I agree that the syntax is not as great as we'd like. If it were up to me, I would have pushed the CSS working group to avoid expanding the use of / as a separator so that we could go on using our old heuristic that mostly treated / as division. Unfortunately, the single strictest design guideline we have to follow is compatibility with CSS. It even takes precedence over beautiful syntax when necessary.

Using parentheses to tag division has a number of serious downsides:

I will say that one of the major features we're working on this year is first-class calc (#818), which will provide a context in which / can be used as division again. It's more verbose and has more knock-on differences than the old ability to use / anywhere-ish, but it won't require a separate @use rule.

essenmitsosse commented 3 years ago

Ok, hear me out: Feature flag.

Change the default behaviour to math.div but add a feature flag (on a per file basis or whatever) to allow the old way, maybe while disallowing the new /-as-separator interpretation. This would at least make migrating a bit less of a hassle for old projects, while not breaking the rule to have CSS be valid Sass (since CSS wouldn't have the feature flag).

flachware commented 3 years ago

@nex3 thank you for your response. Up front, none of 'my' proposed alternatives (they are actually from you and @mirisuzanne) are in conflict with the compatibility with CSS.

I beg to differ, I think in the context of a programming language parentheses are not a subtlety, they are either there or not, for both humans and parsers.

Re your second point, I fail to see how math.div() solves this better than naked parentheses. If I copy & paste old Sass code it will be invalid, if I copy & paste pure CSS it will be valid in both cases.

In my understanding () would mean 'this is a calculation' (so it’s not my intention to tag divisions but to declare calculations). Think of () the same way as you would for the upcoming first-class calc() except that it would not fall back to the CSS calc(). In this sense (1/2 1/3) and ("foo": 1/2) are not ambiguous but clearly invalid calculations and would throw a compiler error.

It’s good to hear that you will be working on the first-class calc() soon, because this is in line with my strategy to update the deprecated / directly to it, mainly for reasons of readability. But at this point I am wondering again, why is calc($space-sm / 2) feasible and ($space-sm / 2) not? Would it be a possibility to make () a shorthand of the first-class calc()?

I guess the math.div() solution crosses the line because it makes divisions even more complicated than in CSS itself, I think that’s the bar here. The whole point of Sass is to make stylesheets easier to handle, if part of the syntax is more complicated it should also be more powerful. So it’s my understanding that the Sass syntax has to be more elegant and concise than the CSS syntax generally.

mirisuzanne commented 3 years ago

We all agree this is a frustrating situation, and that math.div() is not an ideal solution. But it's a very much needed temporary fix while we work on the larger, more elegant solution. Even if the previous behavior wasn't breaking your site, it was breaking our core promise of CSS compatibility, and it had to be changed ASAP. That change was discussed in depth last year, and is now underway.

We spent a lot of time considering all the tradeoffs, before we came up with a plan that gets us there as safely as possible. That all happened here, in the open, last year. We know it's not a painless transition, and we know that math.div() is not the best we can do long-term -- but it does address the immediate problem, while we work on a more elegant solution. And it avoids a lot of potential issues along the way. That discussion about the best path forward happened here in the open last year, and the resulting plan is now actively underway. Re-hashing the first-step of that plan, after-the-fact, doesn't actually help us move forward.

At this point, we all just want to get first-class calc() rolled out as soon as possible – and that's where the focus will remain.

whaaaley commented 3 years ago

I've been using Sass for over a decade and never had issues with division or / as a separator. I think the deprecation should have held off until calc() was rolled out. The / was the status quo since the start and by deprecating it before an actual usable alternative is released seems a bit silly to me. I would bet real money that this preemptive deprecation will cause more headaches than headaches from "breaking css compatibility".

Why warn people to update everything to math.div when calc() is just around the corner? That's two massive updates vs just one. Especially if calc() will actually be released "asap".

I'll be locking my Sass version to the previous patch until calc() is released and would recommend everyone else do as well to avoid making multiple migrations. Good luck guys.

Edit: Version 1.32.13 for those wondering.

nex3 commented 3 years ago

@whaaaley I think it's hyperbolic to say that math.div() isn't usable. It works the same way as many other mathematical operations in Sass, and while division is somewhat more common than math.pow() or math.min() the extra four characters and a @use rule are not as onerous as you're making them out to be. As we continue to expand Sass's core library and work towards deprecating all top-level Sass functions, it's a reasonable expectation that most Sass files will begin with one or two core-library @uses.

We're deprecating this now for several reasons. First, we generally try to deprecate old behavior as soon as we know it's going to be removed at some point in the future. This gives users as much time as possible to move away from it. Second, it's not obvious that calc() is the ideal solution for all circumstances anyway. While it is terser, it raises the possibility that a given calc() expression will accidentally return a Calculation object rather than a number in some circumstances and then be incompatible with other Sass operations.

Ultimately, as Miriam said, this discussion has already been done and settled in public, and the decision has been made. There's not much point relitigating it now. If the deprecation warnings are bothering you, my primary recommendation is to run the migrator, but if you're unwilling to do that for whatever reason running in --quiet mode is certainly better than pinning to an old version and losing out on compatibility with libraries and new CSS features.

whaaaley commented 3 years ago

@nex3 I can agree that math.div is fine for many use cases, but in my case I have many color conversion functions and other algorithm functions that use a LOT of division and converting those to math.div is a readability nightmare. So I think we can just agree to disagree here.

As for "relitigating"... I think it's a good thing to keep in mind that many people don't follow the development of this project closely and the deprecation warning will likely be the first time people learn about this change. I imagine in the coming weeks as people update their dependencies more people will find their way to this issue wondering what happened.

I think it's better to keep an open mind to suggestions given how critical and common the division operator is.

Anyway, I'm not trying to dictate how to run your project. I'm just trying to offer some perspective. I've loved using Sass for as long as I've been using it and I like many of the changes that have been made over the years.

Edit: And for whatever it's worth, I do agree with @flachware's suggestion. I think fencing math in parens would be a great solution and deserves some more consideration. I think it's 100x less intrusive than slapping math.div in the middle of your equation.

Muffinman commented 3 years ago

As many others will after me, I found this thread after seeing a warning after updating one of our projects.

I have to say this change is really surprising to me. Sass is such a great tool for developers exactly because it generates readable, flexible CSS with very little cognitive overhead for the developer.

I feel like the DX deterioration of this change has been thoroughly overlooked. I understand you have no control over the CSS working group adding these additional uses for /, but I still feel that it would have been 100x better to find a new way of defining these grid-row-start and grid-row-end directives rather than throwing out perfectly elegant syntax (along with introducing the issue of 1000s of old projects that now require manually updating).

In fact, as a developer, I don't think I would ever write grid-row: 1 / 3; in sass and expect it to work. I would most likely write separate directives or try to wrap in quotes. For how infrequently I write this compared to using a division operator it's really a no-brainer for me.

Another thought here (which may not be of concern for you), is that developers will seriously think twice about using sass on new projects given the possibility of other such drastic changes to syntax in the future. This is especially true given the current momentum of Tailwind and the like.

/2p

lehni commented 3 years ago

100% agreeing with everybody else here. Our Vue / Nuxt based project spits out 100s of these warnings after the upgrade, slowing down the build a lot. Converting the full code-base will be far from trivial, resulting in much less readable code. It's a bizarre change of behavior that was rolled out with a minor version change. We downgraded back to the last version that doesn't warn about this and wait for the dust to settle. I have strong doubts that this is the final decision in this discussion.

lehni commented 3 years ago

Also, why was @moleCuleFFF's comment marked as spam? They point out that another library has found a different way to handle this operator ambiguity, and I think that's valuable information.

nex3 commented 3 years ago

@whaaaley We go to great pains to make our design and decision-making process as open and participatory as possible, and to publicize major decisions on the Sass blog and Twitter account. You're right that some people don't follow it closely, but part of what that means is that those people have a weaker ground for objecting to the decisions that get made when they could have participated in the process. The Sass team is very busy, and we don't have infinite time and energy to go round and round on old decisions. Part of the point of having such a deliberative process in the first place is so that we can lock in decisions and move on.

That said, because this is a hot-button issue, I'll walk through some of our design reasoning in detail. First: matching CSS syntax is non-negotiable. It is the core syntactic design goal for Sass. This gets at @Muffinman's point: even if you personally would expect grid-row: 1 / 3 to perform division, in CSS it unambiguously does not do so. This focus on compatibility isn't mere idealism: it's crucial for onboarding new authors to Sass, because they invariably know CSS better than they do Sass; and it's crucial for avoiding even more disruptive breaking changes in the future, because the CSS working group won't hesitate to add new features that re-use any syntax they've claimed as their own. So far we've been able to avoid that with / because the font shorthand is optional, we've hacked in special support for the color syntax, and grid support isn't something that often needs to be stored in a variable—that buys us time to do a proper deprecation process and give everyone time to upgrade. But if we just stick with the old behavior, it would be entirely possible for CSS to introduce a new feature that unavoidably uses it in a way that breaks Sass's heuristics and we'd have to do the same dance but in a matter of months rather than years.

Second, relating specifically to the possibility of fencing division with parens, I went into some detail above about the downsides of that approach. There's another general principle at work though that's worth mentioning: verbosity is less bad than surprise. Ideally Sass would be terse and unsurprising, but when our hand is forced (as it is here), it's better to err on the side of heavyweight-but-obvious syntax than lightweight-but-surprising. (I acknowledge that for people coming from other programming languages, / not meaning division is itself surprising, but we need to prioritize the surprise for people coming from a CSS background and we can't make Sass match other programming languages' syntax without violating CSS compatibility anyway.)

As @mirisuzanne pointed out, first-class calc() (#2186) will provide a similar solution for providing a context in which / means division, while being more explicit and less overloaded than parentheses. Will this be satisfyingly terse for you all? If so, I'm prepared to prioritize its development to minimize the amount of time that math.div() is the only division solution that doesn't produce a warning.

@lehni

Our Vue / Nuxt based project spits out 100s of these warnings after the upgrade, slowing down the build a lot.

Upgrade to Dart Sass 1.34.0; it limits the number of repeated deprecation warnings that are printed by default to 5.

Also, why was @moleCuleFFF's comment marked as spam? They point out that another library has found a different way to handle this operator ambiguity, and I think that's valuable information.

"Don't use Sass, use this other stylesheet preprocessor!" is not an appropriate thing to post in the Sass issue tracker.