CSSで値を段階的に表現するround()、mod()、rem()関数

## はじめに
2024年にNewly availableとなったCSS関数として`round()`、`mod()`、`rem()`があります。これらは[`Stepped Value Functions`](https://drafts.csswg.org/css-values/#round-func)と呼ばれており値を段階的に表現します。

値を段階的に調整するとは、ある値を指定された単位や基準（以後ステップ値と呼ぶ）に基づいて、その値を段階的な形式に変換するプロセスを指します。

例えば、任意の数値を指定した桁に基づいて丸め込む四捨五入は値を段階的に調整していると言えます。

## round()
`round()`は指定した戦略を元に段階的に調整した値を返します。
パラメータとして、丸め込みに対する戦略と対象の値、ステップ値の3つを持ちます。

丸め込みについての戦略は`up`、`down`、`nearest`、`to-zero`の4つで`nearest`をデフォルト値としています。
- `up`は常にステップ数の上の倍数で丸める
- `down`はステップ数の下の倍数で丸める
- `nearest`は上か下の倍数で近い方の値に丸める
    - ちょうど真ん中の時は上の倍数に丸める
- `to-zero`はゼロに近い値の倍数へ丸める
    - 正の値の時は下の倍数に、負の値の時は上の倍数に丸める(上下は数直線上の概念)

対象の値やステップ値は[`<number>`](https://drafts.csswg.org/css-values/#numbers)や[`<dimensions>`](https://drafts.csswg.org/css-values/#dimensions)、[`<percentages>`](https://drafts.csswg.org/css-values/#percentages)を割り当てられます。

以下が変換例です。
```css
* {
  flex-shrink: round(1.5, 1); /* 2 */
  font-size: round(nearest, 1.33rem, 0.25rem); /* 1.25rem */
  height: round(up, 101px, 100px); /* 200px */
  width: round(down, 77%, 10%); /* 70% */
  margin-right: round(to-zero, 105px, 10px); /* 100px */
  margin-left: round(to-zero, -105px, 10px); /* -100px */
}
```

`round`を利用する実用的な例として、た`0.25remを`単位としたフォントサイズを提供する例を紹介します。

```css
.any {
  font-size: round(var(--unknown-font-size), 0.25rem);
}
```
動的な`var(--unknown-font-size)`を最も近い`0.25rem`の倍数に丸めるように、整然とした文字サイズで提供します。

以下の例では`--unknown-font-size`が`22px`ですが、実際の値はそれに近い`24px`になります(ここではChromeのデフォルトに従い`1rem`を`16px`の場合で考えいます。`1rem`の基準に応じて調整するフォントサイズも変わります)。

<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="emOYGqK" data-pen-title="Untitled" data-user="kokisakano" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kokisakano/pen/emOYGqK">
  Untitled</a> by KokiSakano (<a href="https://codepen.io/kokisakano">@kokisakano</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

この他にも、デザイントークンとして一定のステップ値が定義されている値は`round`を使うことで、任意の値をそれに従った値として反映できます。



## mod()とrem()
`mod()`と`rem()`は1つ目のパラメータを2つ目のパラメータで割った余りを返します。
`mod()`は2つ目のパラメータの符号を取った余りの値を、`rem()`は1つ目のパラメータの符号を取った余りの値を返します。

例えば、`mod(22, -3)`は余りが負になるような計算$-3\times-8-2$により$-2$を、`rem(22, -3)`の場合は余りが正になるような計算$-3\times-7+1$により$1$を返します。

また、`mod(-22, 3)`は余りが正になるような計算$3\times-8+2$により$2$を、`rem(-22, 3)`の場合は余りが負になるような計算$3\times-7-1$により$-1$を返します。

より細かい計算が気になる場合はこちらを参照してください(JavaScriptで簡単に計算を模しただけなので、境界条件等が実際の値と異なる可能性があります)。
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="gbYOmJE" data-pen-title="Stepped Value Functions" data-user="kokisakano" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kokisakano/pen/gbYOmJE">
  Stepped Value Functions</a> by KokiSakano (<a href="https://codepen.io/kokisakano">@kokisakano</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

数式では以下のように表されます。

```math
\begin{eqnarray}
&\mathrm{mod}(x, y) = x - \mathrm{sign}(y)\times \mathrm{round}(\mathrm{down}, x\times \mathrm{sign}(y), y) \\
&\mathrm{rem(x, y)} = x - \mathrm{round}(\mathrm{to–zero}, x, y) \\
\end{eqnarray}
```

$\mathrm{round}(x, y)$は最初に紹介した関数と同等のもの、$\mathrm{sign}(x)$は$x$の符号を求める関数です($\mathrm{sign}(3)=1$、$\mathrm{sign}(-55)=-1$になります)。

## おわりに
CSSで値を段階的に表現するround()、mod()、rem()関数を紹介しました。
CSSで計算可能な範囲が広がることで新たな表現や、JavaScriptを用いないマークアップなどの発展を感じます。今後の動向にも期待したいです。