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

[css-values-5] Inconsistent argument order between clamp() and mix()/progress() #11427

Open
cr7pt0gr4ph7 opened this issue Jan 2, 2025 · 4 comments

Comments

@cr7pt0gr4ph7
Copy link

cr7pt0gr4ph7 commented Jan 2, 2025

The argument order for the newly proposed progress() / mix() functions differs from that of the already existing clamp() function:

/* Source: https://drafts.csswg.org/css-values/#comp-func */
clamp(<lower-bound>, <value>, <upper-bound>)

/* Source: https://drafts.csswg.org/css-values-5/#mix (arguments renamed to illustrate the point) */
mix(<value>, <lower-bound>, <upper-bound>)
mix(<value> of <keyframes-name>)

/* Source: https://drafts.csswg.org/css-values-5/#progress-func (arguments renamed to illustrate the point) */
progress(<progress>, <lower-bound>, <upper-bound>)

From my (limited) viewpoint, this is a potential source of confusion of stylesheet authors. There are good arguments either way (mix(<lower-bound>, <value>, <upper-bound>) would be consistent with clamp(<lower-bound>, <value>, <upper-bound>), while
mix(<lower-bound>, <min>, <upper-bound>) would be more symmetric with mix(<value> of <keyframes-name>). It would probably make sense to include a note about the inconsistency as well as the rationale for the choice made in the spec to prevent a bit of avoidable confusion in the future.

Possibly related: #10489

@Loirooriol
Copy link
Contributor

You say "arguments renamed for claritity" but the arguments represent completely different things so I don't really think these are comparable situations where consistency would be expected.

@cr7pt0gr4ph7
Copy link
Author

cr7pt0gr4ph7 commented Jan 8, 2025

These functions share nothing but one conceptual similarity: They are about one value that sits (or chooses) "between" two bounds, in some way or another.

Argument order is always an arbitrary choice anyways... but on a general level, it's easier to remember the more consistent it is, as opposed to being bound,value,bound for one set of functions, and value,bound,bound for another. Even if what that "bound" specifically means is different.

Though arguably CSS' clamp() with bound,value,bound is the odd one out here, given that the corresponding functions in C++, Java, .NET, Ruby 1 and anything else I could find all use value,bound,bound.

Footnotes

  1. Sidenote: Haskell, Erlang, PHP & Perl do not seem to have a built-in clamp function.

@cdoublev
Copy link
Collaborator

cdoublev commented Jan 9, 2025

I agree for progress(). It takes the same value types and returns the position of the value that sits between the two others.

@cr7pt0gr4ph7
Copy link
Author

I agree for progress(). It takes the same value types and returns the position of the value that sits between the two others.

...and because of the types being the same, accidentally mixing up the argument order cannot be discovered statically, but only through observed wrong behavior at runtime (I've got bitten by that while first using clamp(), and had to manually split up & debug the expression until I discovered that my assumptions about clamp() were wrong).

I'd argue for seeing mix() as the mirror image/inverse of progress() (as mix(progress(X, A, B), A, B) = X), and as such should probably have an analogous argument order.


Due to the prevalence of OOP and it putting the value "being transformed" first (though this also makes sense in functional programming due to currying/partical application), programmers with previous knowledge will probably expect value,bound,bound, whether its value.clamp(bound, bound) or clamp(value, bound, bound).

In my opinion, it would have probably been best for clamp() not to differ from the previous art and stay with value, bound, bound, but as that is not available: What is the second best option?

  • Leaving clamp() as the odd one out, and making progress() and mix() consistent with those externally formed expectations.
  • Favoring internal consistency between clamp(), progress() and mix(), but violating expectations formed by other languages.

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

No branches or pull requests

4 participants
@cr7pt0gr4ph7 @Loirooriol @cdoublev and others