text-wrap と white-space プロパティが CSS Text Module Level 4 でどうなっているか

# はじめに

この記事は [2023 年の MDN 翻訳 Advent Calendar](https://qiita.com/advent-calendar/2023/mdn-translation-community-japan) 向けに作成したものです。

こんにちは。debiru です。「適用」と「適応」を混同するな委員会に所属しています。

今日は、[昨日の記事](https://qiita.com/debiru/items/0a349bee3669b776d8e2)の続きです。テキストの折り返しを制御する `text-wrap` について紹介していきます。

# `text-wrap` とは

[CSS Text Module Level 3](https://www.w3.org/TR/2012/WD-css3-text-20120119/#text-wrap) で提案されたものの [CSS Text Module Level 4](https://www.w3.org/TR/2015/WD-css-text-4-20150922/#text-wrap) に延期されたプロパティです。初期の版では `normal`, `nowrap` の他に `balance` プロパティ値が提案されていました。

`balance` はテキストが折り返して複数行になる場合に、各行ごとのテキストの長さをなるべく同じにするという折り返し方を実現するものです。

## `white-space` プロパティとの関係

CSS Text Module Level 4 では `white-space` プロパティが `text-space-collapse`, `text-wrap`, `text-space-trim` の[ショートハンドプロパティとして再定義](https://www.w3.org/TR/2015/WD-css-text-4-20150922/#white-space-property)されました。

`white-space: pre-wrap` と指定すると、`text-space-collapse: preserve; text-wrap: normal; text-space-trim: none` を指定したものと同値になるということです。なお、これらのプロパティ名は初期の版の CSS Level 4 のものです。後述しますが、これらのプロパティ名は最新版の CSS Level 4 では改名されています。

## 新しい text-wrap の仕様

後述しますが、CSS Level 4 の最新版では `text-wrap` プロパティがショートハンドプロパティとして再定義されるという仕様変更がされています。ここでは `text-wrap` プロパティがロングハンドプロパティであったときの定義について紹介します。

- https://www.w3.org/TR/2023/WD-css-text-4-20230329/#text-wrap

`text-wrap` プロパティの値として次のようなものがあります。

- `wrap`
    - 通常の折り返し。あるいはブラウザが定義するアルゴリズムによる最善の折り返し。ブラウザは、レイアウトを最適にするよりも処理速度を重視したアルゴリズムを採用してよい。なお、この値が指定されたとき、`balance` 相当のアルゴリズムを採用してはならない。
- `nowrap`
    - インラインレベルのコンテンツ（テキスト）は折り返されることなくレイアウトされる。
    - この値は `white-space-collapse: collapse` である場合、`white-space: nowrap` を指定したのと同値である。詳細は [white-space のショートハンドプロパティの対応表](https://www.w3.org/TR/css-text-4/#ref-for-shorthand-property)を参照のこと。
- `balance`
    - テキストが折り返して複数行になる場合に、各行ごとのテキストの長さをなるべく同じにするという折り返しを行う。なお、折り返されるテキストの行数が 10 行を超える場合、（バランスしたレイアウトの計算コストを抑えるために）この指定値を無視することができる。
- `stable`
    - テキストを編集するときにカーソルより前の内容が安定するように、折り返しを決定するときに後続行の内容を考慮しないように指定する。
- `pretty`
    - ブラウザが処理速度よりもレイアウトの最適化を重視するアルゴリズムを利用して、折り返しを決定するときに複数行の内容を考慮することを期待する。特に、行内に単語が孤立することを避けるような折り返しを実現する。

## Chrome 114 で balance が、117 で pretty が実装された

- [CSS text-wrap: balance  |  CSS and UI  |  Chrome for Developers](https://developer.chrome.com/blog/css-text-wrap-balance)
- [CSS text-wrap: pretty  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/css-text-wrap-pretty)
- [CSS テキストの折り返し: バランス  |  CSS and UI  |  Chrome for Developers](https://developer.chrome.com/blog/css-text-wrap-balance?hl=ja)
- [CSS テキストの折り返し: きれい  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/css-text-wrap-pretty?hl=ja)

なお、Chrome は `text-wrap` がロングハンドプロパティであった時点の仕様で実装しています。

<p class="codepen" data-height="600" data-default-tab="html,result" data-slug-hash="ExrBxjW" data-user="arcxor" style="height: 600px; 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/arcxor/pen/ExrBxjW">
  text-wrap property</a> by arcxor (<a href="https://codepen.io/arcxor">@arcxor</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

![text-wrap の指定例。pretty 値が指定されると、最終行に単語が 1 つだけ孤立することが避けられる。balance 値が指定されると、各行の長さが等しくなるように折り返される。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178936/78c29b69-508c-64c2-8e6a-71e7fbcd97b7.png)

上記は `text-wrap` の 4 つの値を指定した例を Chrome で実行した結果です。

(1.) では、`text-wrap: wrap` を指定しています。これはいわゆる「通常の折り返し」です。

(2.) では、`text-wrap: nowrap` を指定しています。これは、これまで `white-space: nowrap` として指定していた振る舞いと同じ結果が得られます。

(3.) では `text-wrap: pretty` を指定しています。(1.) と比較すると分かりますが、(1.) では日本語文、英文のそれぞれの最終行が「波。」、"consequant." となっているのに対して、(3.) では最終行が「の波。」、"commodo consequant." となっています。最終行に単語が 1 つだけ孤立しないような折り返しが行われています。

(4.) では、`text-wrap: balance` を指定しています。これは見れば一目瞭然ですが、各行のテキストの長さがなるべく等しくなるような折り返しがされ、左から右に書かれる言語のテキストでは、全ての行の右側に広いスペースが生じるような表示結果となります。

## 折り返しアルゴリズムの詳細

[CSS テキストの折り返し: きれい  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/css-text-wrap-pretty?hl=ja) のページからもリンクされていますが、折り返しアルゴリズムの詳細は [Koji Ishii 氏が作成した設計ドキュメント](https://docs.google.com/document/d/1jJFD8nAUuiUX6ArFZQqQo8yTsvg8IuAq7oFrNQxPeqI/edit?hl=ja#heading=h.cqq9czoal00g)があります。

少しだけその内容について紹介しておきます。

テキストの折り返しを行う場合、2 つの基本的なアプローチがあります。

- 単語が溢れそうになったら折り返す、Greedy（貪欲な）アルゴリズム
- 複数行の折り返し結果を考慮しながら折り返し位置を決める、段落レベルのアルゴリズム

Greedy なアルゴリズムは `text-wrap: stable` で詳しく説明されているものです。このタイプは高速で、現在のすべてのブラウザのデフォルトのアルゴリズムです。

段落レベルのアルゴリズムは、より良いタイポグラフィを実現するために段落内の他の行を考慮します。`text-wrap: pretty` はこのタイプのアルゴリズムを選択するためのプロパティです。このタイプは一般に、処理速度を犠牲にしながらも、より良いタイポグラフィを生成します。[TeX](https://ja.wikipedia.org/wiki/TeX) はこのタイプの最も有名なアプリケーションの一つです。

……といった感じの説明が書かれています。

## text-wrap プロパティをショートハンドプロパティにしようという動き

https://github.com/w3c/csswg-drafts/issues/9102

`text-wrap` プロパティの値 `wrap`, `nowrap`, `balance`, `stable`, `pretty` のうち、`wrap`, `nowrap` とそれ以外では値の意味合いのカテゴリが違うのではないかと提唱されています。まずは折り返すか折り返さないかを指定し、その上でどのようなアルゴリズムで折り返すのかを指定すべきというのです。

Chrome が既に `text-wrap` プロパティをロングハンドプロパティとして実装してしまっていることもあり意見が分かれましたが、結果的に、これらを分離しよう、つまり `text-wrap` プロパティをショートハンドプロパティとして再定義しようという運びになりました。

最新の仕様では、`text-wrap` プロパティは次のような値を取ることになっています。

- https://www.w3.org/TR/css-text-4/#text-wrap-shorthand

```
Value: <'text-wrap-mode'> || <'text-wrap-style'>
```

なお、二重バー `||` は、[1 つは必須だが、すべての対象が省略可能であること](https://developer.mozilla.org/ja/docs/Web/CSS/Value_definition_syntax#%E4%BA%8C%E9%87%8D%E3%83%90%E3%83%BC)を意味します。よく知られているものとして `border` プロパティの定義があります。

```
Value: <'border-width'> || <'border-style'> || <'border-color'>
```

### `text-wrap-mode` プロパティ

```
Value: wrap | nowrap 
```

### `text-wrap-style` プロパティ

```
Value: auto | balance | stable | pretty 
```

### `white-space` プロパティへの影響

`white-space` プロパティは CSS Level 4 ではもともと `white-space-collapse`, `text-wrap`, `white-space-trim` の[ショートハンドプロパティ](https://www.w3.org/TR/2023/WD-css-text-4-20230329/#white-space-property)でした。

```
Value: normal | pre | nowrap | pre-wrap | pre-line | <'white-space-collapse'>
       || <'text-wrap'> || <'white-space-trim'>
```

`text-wrap` がショートハンドプロパティとなったことで、[`white-space` プロパティの定義](https://www.w3.org/TR/css-text-4/#white-space-property)も変わりました。

```
Value: normal | pre | pre-wrap | pre-line | <'white-space-collapse'>
       || <'text-wrap-mode'> || <'white-space-trim'>
```

これは `white-space` プロパティ値として `balance` と `pretty` が使えなくなったことを意味します。しかし Chrome は古い仕様で既に実装してしまっています。この状況について 2023 年 11 月に [w3c/csswg-drafts リポジトリでは指摘](https://github.com/w3c/csswg-drafts/issues/9102#issuecomment-1807453214)がされています。

## 関連記事

- [text-wrap property - CodePen](https://codepen.io/arcxor/pen/ExrBxjW)
- https://drafts.csswg.org/css-text-4/#text-wrap-shorthand
- https://www.w3.org/TR/css-text-4/#text-wrap-shorthand
- https://www.w3.org/TR/css-text-4/#white-space-property
- https://developer.mozilla.org/en-US/docs/Web/CSS/text-wrap
    - MDN の `text-wrap` は一部の内容が古く意味不明になっていますが、[英語版の修正 Pull-Req は 2023 年 11 月に作成されている](https://github.com/mdn/content/pull/30435)ので近々更新される見込みです。
- https://developer.mozilla.org/ja/docs/Web/CSS/white-space

# さいごに

[昨日の `word-break`, `overflow-wrap`, `line-break` の記事](https://qiita.com/debiru/items/0a349bee3669b776d8e2) と今日の `text-wrap` の記事を併せてお読みいただいた方は、折り返しについてだいぶ詳しくなったのではないでしょうか。

[2023 年の MDN 翻訳 Advent Calendar](https://qiita.com/advent-calendar/2023/mdn-translation-community-japan) は明日以降、何を書けばいいのでしょうかね。CSS ネタが続いたので次は HTML ネタでも書きましょうか。

というわけで、折り返しについての記事第二段でした。

おわり。