@@ -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>
26592753Argument 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