Webで縦書きコンテンツ〜Safariには気を付けろ！〜

この記事は[NIJIBOX Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nijibox)の掲載記事です。

## この記事で話すこと
**CSSで文章の縦書きを表現した場合のChromeとSafariの表示の違いとその回避策**

先日、初めてWebで**文字の縦書き**の実装が必要な案件に出会いました。
縦書き文章って紙媒体の小説や新聞では当たり前ですがWebではなかなか珍しいですよね。
と言っても文字を縦にするだけだしなんとかなるだろうと思っていたのですが、クロスブラウザチェック時に予期せぬ挙動を発見してしまい少し悩んだのでここに備忘録として書き残しておこうと思います。

※PCのモダンブラウザ対応を想定しています。
※2021/12/02時点での内容となります。
Google Chrome バージョン: 96.0.4664.55
Safari バージョン13.1.3 (15609.4.1)


## Webで縦書き、どうしよう？
さて、そもそもWebで縦書きをどう実現するかですが、こちらのCSSプロパティを使うことにしました。

https://developer.mozilla.org/ja/docs/Web/CSS/writing-mode

>writing-mode は CSS のプロパティで、テキストの行のレイアウトを横書きにするか縦書きにするか、ブロックのフロー方向を左向きにするか右向きにするかを設定します。

一部の特殊な値を除き、モダンブラウザすべてに対応しています。

https://caniuse.com/?search=writing-mode

今回は日本語を縦書きにする必要があったので、

```css
.text {
  writing-mode: vertical-rl;
}
```

このように`vertival`（縦書き）の`rl`（右から左へ）を指定してあげることにしました。



## うまくいった！と思いきや
画面内のコンテンツすべてを縦並びにしたいわけではなかったので、
文章を表示させる部分の最低限のタグに`writing-mode`を設定することにしました。

イメージはこのような感じです。（かなり簡略化しています。Chromeでご確認ください。）

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GRMgqMx" data-user="moena-hayasaka" 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/moena-hayasaka/pen/GRMgqMx">
  vertical-test-1</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


ちゃんと縦書きになってそうですね！

...と思いきやSafariで確認してみると...
![スクリーンショット 2021-12-02 10.34.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541140/08b538cd-9d2a-b091-ab37-8c7d89be309e.png)

Chromeとは明らかに違った表示になってしまいました。どうしてでしょう。
いくつかパターンをつくって検証してみることにします。


## Safariで挙動の違いをいくつか見てみた
※ここから先はサンプルコードをChrome, Safariで見比べていただければと思います。

### Gridパターン
縦書きコンテンツをラップする要素を `display: flex;`から `display: grid;`＆`grid: 1fr 100px;`の指定に変更してみました。
一見うまくいっていそうなのですが、小さい画面サイズで見るとChromeとSafariで挙動が異なっていることがわかります。Safariでは他のコンテンツが縦書きコンテンツの上に乗ってしまっています。


<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="mdByZpq" data-user="moena-hayasaka" 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/moena-hayasaka/pen/mdByZpq">
  Untitled</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

 `grid: 1fr 100px;`の `1fr`を固定値（`px`）に変えてみると、ChromeとSafariで表示がそろっていそうでした。

 <p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="ZEXYgpO" data-user="moena-hayasaka" 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/moena-hayasaka/pen/ZEXYgpO">
  vertical-test-3</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 横書きパターン
念のため`display: flex;`を使った例から `writing-mode: vertical-rl;`の指定を外し、横書きにしてみました。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="BawyXqa" data-user="moena-hayasaka" 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/moena-hayasaka/pen/BawyXqa">
  vertical-test-4</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

当たり前ではありますがChromeもSafariも表示崩れはなさそうです。

ここまでの内容だとどうやら**縦書きコンテンツの幅が成り行きで決まる時（固定値でない）**にSafariで不具合がありそう？という予測が立ちます。

### 縦書きコンテンツのみパターン

シンプルに縦書きコンテンツとそれをラップする要素のみで見てみます。
一方はラップ要素に`width`に固定値を指定したもの、もう一方はラップ要素に`width: fit-content;`を指定したものです。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="MWEwgmQ" data-user="moena-hayasaka" 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/moena-hayasaka/pen/MWEwgmQ">
  vertical-test-5</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

`width: fit-content;`を指定した方で、Safariではラップ要素が潰れてしまっていることが確認できます。やはり**縦書きコンテンツの幅が成り行きで決まる時（固定値でない）**とき不具合が起きています。


### writing-mode、他の値は？

**縦書き×幅成り行き**がダメだとしたら、`writing-mode: vertical-lr;`（左から右に文章が流れる）の場合もダメなのかと思い、試してみました。
また、`writing-mode`の指定自体が影響している場合もあるかもしれないということで、`writing-mode: hotizontal-tb;`（横書き）の場合も見てみます。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GRMJKQZ" data-user="moena-hayasaka" 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/moena-hayasaka/pen/GRMJKQZ">
  vertical-test-6</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

結果、やはり縦書きである`writing-mode: vertical-lr;`を指定した方のみがSafariで崩れていることが確認できました。


## Safari縦書き不具合の回避策
### `writing-mode`の指定を、縦書きにしたいコンテンツの一番上にする

一番初めの例では、縦書きにしたいコンテンツの一番内側の要素に`writing-mode: vertical-rl;`を指定していました。
この指定を一段階上の要素に移動させると、Safariでも想定した表示結果となります。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="Exwjxoj" data-user="moena-hayasaka" 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/moena-hayasaka/pen/Exwjxoj">
  vertical-test-7</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


また、`writing-mode`という名前に惑わされがちですがこのCSSプロパティはテキストのフロー方向だけではなく、ブロックのフロー方向の指定にも使えます。
ですので、横並びにしたい要素すべてを縦書きにしたい場合は、`display: flex;` や `display: grid;` を使わず一番外側の要素に`writing-mode: vertical-rl;`を指定してもいいと思います。
その場合はブロックの流れも右から左になるので、注意が必要です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="qBPdBvr" data-user="moena-hayasaka" 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/moena-hayasaka/pen/qBPdBvr">
  vertical-test-8</a> by moena-hayasaka (<a href="https://codepen.io/moena-hayasaka">@moena-hayasaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## おわりに
偶然見つけたSafari×縦書きの謎現象と、その（とりあえずの）回避策についてお話しました。
さらに詳しい情報や、記事の中に間違いなどありましたらコメントいただけますと幸いです。
この他にも業務の中でSafari対応に悩んだ箇所が多々あり、**"Safari is next IE"**を強く感じている2021年末今日この頃...。みなさま、良いお年を〜。