-
Notifications
You must be signed in to change notification settings - Fork 677
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] Proposal: round()
to a finite scale
#11067
Comments
Would infinities be automatically included? round(down, 0, 2 3 5 7 11); /* -infinity or nan? */ Would the list need to be in non-decreasing order, or leave it up to the browser to sort it? Would the list accept duplicates? I guess it would be a way to have a finite list with one item... But IMO it can be confusing that providing 2 values results in a 2-values finite list, while providing 1 value results in an infinite list. It may be better to introduce a new syntax. It could also be useful to round to intervals, e.g. round(var(--num), [0, 2 .. 3, 4, 5 .. 6]) |
All good questions!
Hmm, in this case I'd expect
Hmm, an author in me wants the browser to do the work for me, and do both: remove the duplicates, and then sort the array. Is this a potential performance issue? I imagine 99% of real-world cases won't have too many items there, and if we really don't want people to sort thousands of items, we could have a certain limit in-place. I remember the But the author experience would be for sure much better if the duplicates and non-strict order is allowed.
I can see how this could be confusing, yes, but I wonder if this edge case is too unrealistic to try, and solve it. I'd go with it producing a finite list of 1 element, which is, uh, just that value, but better than falling back to infinite list, as it won't ever be something an author wants.
Oh, so This is an interesting idea, and I can see how introducing a new syntax can help to add things like this one. Although, I am not sure if I remember use cases for this specific case, but if there are some — we can consider it. But maybe this is something that could be potentially solved better with native functions and conditionals? Many tools could be used for the same job, and I wonder if real-life cases that will need something like that will always want something even more complicated. Like, if the value is higher than 6, then do |
I'd say NaN. Infinitely small or large numbers are unexpected in the given use cases.
For author's comfort, the UA should do the sorting. Though authors should still be encouraged to sort the values in increasing order.
It should accept duplicates, but rather to be more resilient against mistakes than for providing a way for a single-item list. There's presumably no use case for a list with a single item. And differenciating finite and infinite lists by syntax is less error-prone.
As noted above, I agree on that. Sebastian |
I disagree, disobeying the rounding strategy seems very unexpected.
The advantage of infinities is that then you can use a min/max/clamp wrapper to set the right limits. Though we could require the author to explicitly add infinities into the list in order to avoid NaN. |
I’m not at all against solving this use case. Maybe In #2826 re PS: Contrarily, |
This use-case sounds potentially useful. I'd be willing to spec this if the WG thinks it's worthwhile.
I do think it's probably worth putting this under a new function name, rather than reusing round(); this is kinda rounding but not quite, especially if we allow ranges where the value is unchanged. This also avoids us having to be consistent with round() for the |
A separate function could work as well, yes. Another thing that I forgot to mention, but about which I was thinking when initially coming up with this proposal: two other proposals by @LeaVerou :
And another thought that I had: I can see similarities with type grinding, where a registered custom property for an |
Yeah, I don't think either of those issues directly impact this one; they're definitely for different features. For a name, I'm thinking The syntax of This probably precludes doing literal ranges, tho; we'd need a special syntax to indicate a range. (I suppose in theory we could allow |
I mentioned ranges as a possible extension to keep in mind during the design, but we can start with the basic version with discrete values. |
I think there is value in having a way to describe lists of numbers and pass them around, and maybe in the future even — gasp — get elements by index, and this could pave the way. Also, I don't think we need a new rounding function. Conceptually this still fits nicely within There's the open question of whether all lists should live under the same function, with an argument for the type of values:
Which would then allow expanding it to allow combining different values, or a dedicated function:
or
And then
Being able to express lists of numbers and lengths is also insanely useful for design systems, as there are many scales, and it would be very valuable to be able to a) pass them around as a whole, b) snap a value to one of them c) get the previous/next value from an existing value |
I keep coming back to this, and I have spent inordinate amounts of mental cycles trying to hack this together with CSS's existing primitives (e.g. I wonder if it would be easier to make progress on this if it were under a new function name, e.g. |
+1 on snap-to(), imo |
Background
This is a proposal that I briefly discussed with @fantasai at CSWG F2F in A Coruña this year. The problem it tries to solve came up when I was writing my latest article, so I finally found time to write it.
One of the motivations for this proposal is a problem that was, for example, mentioned by @matthiasott in his talk at CSS Day 2024 (a bit after 39:31), where he argues that using container query units for fluid type, while possible, leads to too many font-sizes on the page, making it not possible to create a harmonic type scale.
But what if we could augment the existing
round()
to help with this and other similar cases?Proposal
The current syntax of
round()
isround(<rounding-strategy>?, A, B?)
whereA
andB
are calculations that must share a type and resolve to any can resolve to any<number>
,<dimension>
, or<percentage>
.My proposal is to change it to
round(<rounding-strategy>?, A, B*)
— use*
instead of?
for the last argument.When only one value is provided as
B
,round()
will work the same as now.When multiple values are provided to
B
, the values would be treated as a scale of the only values that theA
should be rounded to.These values must be the only possible outcomes of the
round()
function: this is not rounding the value to either of the values in the regular sense, but using the provided values as a finite scale.If we were to think of a single B, it would represent an infinite linear scale, for example the default
1
argument there represents an infinite1 2 3 4 N
scale, but if we'd want to round to a finite scale, for example, to find the closest prime number to a certain list of them, we could doThis way, if we'd provide
--foo: 10
, it will round to the closest prime, resulting in11
. And if we will provide something that is equally close to two values, like--foo: 6
, then the<rounding-strategy>?
will come into play: we could choose how exactly we'd want to handle cases like it — round things to the closest, or ceil/floor it.Implementation-wise, this should not be too complex: something like a binary search of the first argument with the provided list could be good enough.
Other use-cases
Aside from the typographic scale and choosing the closest prime number, I remember stumbling upon many different use cases.
One prominent one: regular spacing scale. Various design systems can have a scale like
2px 4px 8px 12px 16px 20px 24px 32px 48px
for spacing, and similar to the Matthias's case with fluid typography, it would be great to evaluate some container query length units to the closest value from such a scale.There were other cases I encountered, but at the moment of writing this proposal I don't remember them: will update if I'll stumble upon them later, and if anyone had them as well — drop them in the comments, I'll update the proposal with them as well.
Current Workaround
Today, the simplest way we can attempt to approach this with the current CSS is by using a rather complicated complex conditional calculation which I described in my article. For the above list of primes (with some lower ones removed), it looks like this:
This works! But this is not something that is easy to write or maintain by hand.
Alternatives Considered
Initially, I was thinking about either introducing a new function, or looking if we could somehow do this with
clamp()
. For some reason, I was not thinking aboutround()
as a possible alternative, as I was stuck with it rounding to an infinite scale. Butclamp()
works very differently fromround()
, where it keeps the original value if it fits into the provided range. But we're really rounding it to a scale, and it was @fantasai that proposed to think about just augmentinground()
.Out of Scope
I think there is something about being able to specify an alternative, non-linear infinite list, maybe in a form of an equation similar to the one in the nth-child, but maybe a bit more complex.
It would be great to be able to round something to a scale like 2 4 8 16 32 64 etc, or be able to specify a fallback if the value falls outside of the chosen finite scale.
In the future, I think these would be nice to have, but I'd propose to have separate issues discussing them and bikeshedding their syntax. A finite scale is a common enough case, and seems to be simple enough to implement, that I'd want us to first focus on it.
The text was updated successfully, but these errors were encountered: