Skip to content

Commit 7b6b43f

Browse files
committed
[css-values-4] Add mod() and rem() per CSSWG resolution. Closes #2513.
1 parent be33b1f commit 7b6b43f

File tree

1 file changed

+115
-6
lines changed

1 file changed

+115
-6
lines changed

css-values-4/Overview.bs

+115-6
Original file line numberDiff line numberDiff line change
@@ -2574,12 +2574,13 @@ Comparison Functions: ''min()'', ''max()'', and ''clamp()''</h3>
25742574

25752575

25762576
<h3 id=round-func>
2577-
Rounding Values: ''round()''</h3>
2577+
Stepped Value Functions: ''round()'', ''mod()'', and ''rem()''</h3>
25782578

2579-
The rounding function ''round()''
2580-
adjusts the value of a [=calculation=]
2581-
to a nearby multiple of a given precision [=calculation=],
2582-
giving several options for choosing which nearby multiple to use.
2579+
The stepped-value functions,
2580+
''round()'', ''mod()'', and ''rem()'',
2581+
all transform a given value
2582+
according to another "step value",
2583+
in different ways.
25832584

25842585
The <dfn lt="round()">round(<<rounding-strategy>>?, A, B)</dfn> function
25852586
contains an optional rounding strategy,
@@ -2654,6 +2655,99 @@ Rounding Values: ''round()''</h3>
26542655
since block sizes are always non-negative,
26552656
''to-zero'' and ''down'' would be identical.)
26562657

2658+
The modulus functions <dfn lt="mod()">mod(A, B)</dfn>
2659+
and <dfn lt=rem()>rem(A, B)</dfn>
2660+
similarly contain two [=calculations=] A and B,
2661+
and return the difference between A
2662+
and the nearest integer multiple of B either above or below A.
2663+
The argument [=calculations=] can resolve to any <<number>>, <<dimension>>, or <<percentage>>,
2664+
but must have the <em>same</em> [=determine the type of a calculation|type=],
2665+
or else the function is invalid;
2666+
the result will have the same [=CSSNumericValue/type=] as the arguments.
2667+
2668+
The two functions are very similar,
2669+
and in fact return identical results
2670+
if both arguments are positive or both are negative:
2671+
the value of the function is equal to the value of A
2672+
shifted by the integer multiple of B
2673+
that brings the value into the range [0, B)
2674+
(that is, possibly including zero, but excluding B itself).
2675+
2676+
<div class=example>
2677+
For example, ''mod(18px, 5px)'' resolves to the value ''3px'',
2678+
because subtracting ''5px * 3'' from ''18px'' yields ''3px'',
2679+
which is the only such value between ''0px'' and ''3px''.
2680+
2681+
Similarly, ''mod(-140deg, -90deg)'' resolves to the value ''-50deg'',
2682+
because adding ''-90deg * 1'' to ''-140deg'' yields ''-50deg'',
2683+
which is the only such value between ''0deg'' and ''-90deg''.
2684+
2685+
Evaluating either of these examples with ''rem()'' yields the exact same results.
2686+
</div>
2687+
2688+
Their behavior diverges if the A value and the B step
2689+
are on opposite sides of zero:
2690+
''mod()'' (short for “modulus”)
2691+
continues to choose the integer multiple of B
2692+
that puts the value into the [0, B) range
2693+
(guaranteeing that the result will either be zero
2694+
or share the sign of B, not A),
2695+
while ''rem()'' (short for "remainder")
2696+
chooses the integer multiple of B
2697+
that brings the value into the range [0, -B),
2698+
avoiding changing the sign of the value.
2699+
2700+
<div class=example>
2701+
For example, ''mod(-18px, 5px)'' resolves to the value ''2px'':
2702+
adding ''5px * 4'' to ''-18px'' yields ''2px'',
2703+
which is between ''0px'' and ''5px''.
2704+
2705+
On the other hand, ''rem(-18px, 5px)'' resolves to the value ''-3px'':
2706+
adding ''5px * 3'' to ''-18px'' yields ''-3px'',
2707+
which has the same sign as ''-18px''
2708+
but is between ''0px'' and ''-5px''.
2709+
2710+
Similarly, ''mod(140deg, -90deg)'' resolves to the value ''-40deg''
2711+
(adding ''-90deg * 2'' to ''140deg'',
2712+
bringing it to between ''0deg'' and ''-90deg''),
2713+
but ''rem(140deg, -90deg)'' resolves to the value ''50deg''.
2714+
</div>
2715+
2716+
<details>
2717+
<summary>When should I choose ''mod()'' vs ''rem()''?</summary>
2718+
2719+
Typically, users of this operation
2720+
are in control of the step value (B),
2721+
and are modifying an unknown value A.
2722+
As a result, it's <em>usually</em> more expected
2723+
that the result is between 0 and B,
2724+
regardless of A's sign,
2725+
meaning ''mod()'' should be chosen.
2726+
2727+
For example,
2728+
if an author wants to know whether a length
2729+
is an even or odd number of pixels,
2730+
''mod(A, 2px)'' will return either ''0px'' or ''1px''
2731+
(assuming the value is a whole number of pixels to begin with),
2732+
regardless of the value of a.
2733+
''rem(A, 2px)'', on the other hand,
2734+
will return ''0px'' if A is an even number of pixels,
2735+
but will return <em>either</em> ''1px'' or ''-1px''
2736+
if it's odd,
2737+
depending on whether A is positive or negative.
2738+
2739+
The opposite situation does sometimes occur,
2740+
however,
2741+
and so ''rem()'' is provided to cater to that.
2742+
As well, ''rem()'' is the behavior of JavaScript's <code highlight=js>%</code> operator,
2743+
so if an exact match between CSS and JS code is desired,
2744+
''rem()'' can be useful.
2745+
</details>
2746+
2747+
Note: ''mod()'' and ''rem()''
2748+
can also be defined directly in terms of other functions:
2749+
''mod(A, B)'' is equivalent to ''calc(A - round(down, A, B))'',
2750+
while ''rem(A, B)'' is equivalent to ''calc(A - round(to-zero, A, B))''.
26572751

26582752
<h4 id=round-infinities>
26592753
Argument Ranges</h4>
@@ -2673,6 +2767,19 @@ Argument Ranges</h4>
26732767
if A is 0⁺ or positive finite,
26742768
and 0⁻ if A is 0⁻ or negative finite.
26752769

2770+
In ''mod(A, B)'' or ''rem(A, B)'',
2771+
if B is 0,
2772+
the result is NaN.
2773+
If A is infinite,
2774+
the result is NaN.
2775+
2776+
In ''mod(A, B)'' only,
2777+
if B is infinite
2778+
and A is non-zero and has opposite sign to B,
2779+
the result is NaN.
2780+
2781+
Note: All other "infinite B" cases are valid,
2782+
and just return A immediately.
26762783

26772784

26782785
<h3 id=trig-funcs>
@@ -3274,6 +3381,8 @@ Syntax</h3>
32743381
<<max()>> = max( <<calc-sum>># )
32753382
<<clamp()>> = clamp( <<calc-sum>>#{3} )
32763383
<<round()>> = round( <<rounding-strategy>>?, <<calc-sum>>, <<calc-sum>> )
3384+
<<mod()>> = mod( <<calc-sum>>, <<calc-sum>> )
3385+
<<rem()>> = rem( <<calc-sum>>, <<calc-sum>> )
32773386
<<sin()>> = sin( <<calc-sum>> )
32783387
<<cos()>> = cos( <<calc-sum>> )
32793388
<<tan()>> = tan( <<calc-sum>> )
@@ -3413,7 +3522,7 @@ Type Checking</h3>
34133522
is «[ "angle" → 1 ]».
34143523
* The [=CSSNumericValue/type=] of a ''pow()'', ''sqrt()'', ''log()'', or ''exp()'' expression
34153524
is «[ "number" → 1 ]».
3416-
* The [=CSSNumericValue/type=] of a ''hypot()'' or ''round()'' expression
3525+
* The [=CSSNumericValue/type=] of a ''hypot()'', ''round()'', ''mod()'', or ''rem()'' expression
34173526
is the result of [=add two types|adding the types=]
34183527
of its comma-separated [=calculations=].
34193528

0 commit comments

Comments
 (0)