はじめに
自分の記事をスマホで開いたら、表が読めなかった。
何が起きていたか
ユニバーサルデザイン7原則の非対称に、8つのゴールを並べた3列の表がある。ゴール名、内容、層。スマホで開くと、どの列も語の途中で折り返した。3列目は「身」「体・」「知覚」まで潰れ、5行目のゴール名は「Understanding(理」「解)」で割れる。切れた断片をつなぎ直しながら読むことになり、いちばん高い行は本文4行分の高さになる。行が伸びた分だけ画面に収まる行数は減って、表を見渡すという読み方ができない。

横スクロールできる箱は、最初から用意してあった。overflow-x: auto の div で包んで、キーボードでも送れるように tabindex="0" も付けてある。表そのものに overflow を付けると display が変わって表として読まれなくなるので、外側の div で受ける形だ。用意はしてあったのに、スクロールバーは一度も出なかった。
原因は表の幅指定だった。table { width: 100% } と書いてある。これは「箱に収まれ」という意味になる。そして日本語は、どの文字の間でも改行できる。列が要求する最小の幅は、1文字分まで下がれる。つまり、はみ出す前に潰れる。はみ出さないので、横スクロールの箱は最後まで仕事をしない。
英語なら単語の途中で割れないから、ある幅から先は潰れずに素直にはみ出して、横スクロールになる。同じ CSS で、日本語だけが静かに壊れていた。
潰すか、はみ出させるか
横スクロールを出すのは後退のような気がしていた。調べたら逆で、表はもともと例外の側にいた。
1.4.10 リフローは、320px 相当の幅で2方向のスクロールを求めないことを要求する。ただし「利用や意味のために2次元の配置が必要な部分」は除く、と書いてある。行と列の交差そのものが情報になっている表は、その代表例だ。横スクロールする表は、それだけでは違反にならない。
やってはいけない側は、はっきりしている。狭い画面で display: block を当てて、1行を1枚のカードに組み替えるやり方だ。見た目は収まる。代わりに table・tr・td の display が変わって、表としての役割が落ちる。「何列目の値か」という読み上げの手がかりが消えるので、収まったのは見た目だけになる。
そこで、狭い画面では潰す代わりにはみ出させることにした。
@media (width < 44rem) {
table {
width: max-content;
min-width: 100%;
max-width: 160vw;
}
th,
td {
min-width: 6em;
}
}
max-content で内容が要求する幅を優先し、min-width: 100% で短い表は今までどおり幅いっぱいに広げる。max-width: 160vw は横スクロールの量の上限で、画面1.6個分を超える分は折り返しに回す。長い文が1行のまま何画面も伸びるのを止めるためだ。セルの min-width は、その折り返しが起きたときに列が再び潰れないための下限。
名前のないタブ位置
スクロールできる箱には tabindex="0" が要る。ポインタを持たない人が、キーボードだけで横に送るための足がかりだ。
ただ、これだけだと別の問題が残る。役割も名前もない div にタブが止まる。読み上げの利用者には「何かに止まったが、それが何なのか分からない」状態になる。タブが止まる以上、名前と役割が要ると考えるのが自然だ(4.1.2 名前・役割・値)。
名前をどこから取るか。表の題を置く要素は caption で、これは表そのものの名前として読まれる。題を段落や見出しで書いても、表と題の結びつきは支援技術に伝わらない。ところが Markdown の表に caption を書く記法がない。
なので、表の直前に [8つのゴール] だけの行を置くと caption になる記法を足した。番号は出てきた順に振って「表1: 8つのゴール」になる。この下にある表も、そうやって題を付けている。
スクロールできることが、目で分からない
はみ出すようになったあとで、もっと素朴なことに気づいた。スクロールできると、どうやって分かるのか。
止まっている画面に残る手がかりは「右端でセルが切れている」だけだ。しかもそれは、文字が右端で折り返しているだけの見た目と区別がつかない。
スクロールバーは、環境によって出かたが違う。
| 環境 | 止まっているとき |
|---|---|
| Windows・Linux のデスクトップ | 出ている(場所を取る) |
| macOS | 出ない(重ね表示。システム設定で常時表示にはできる) |
| Android Chrome | 出ない |
| iOS Safari | 出ない(変える設定もない) |
つまりスマホでは、指を離した時点で手がかりが消える。
CSS だけで解けるかを探したが、行き止まりだった。「いま、はみ出しているか」を CSS が知る手段は長らく無く、最近入った @container scroll-state() は Chromium 系にしか来ていない。
結局、2つ重ねた。
1つは、スクロールバーを出しっぱなしにすること。Chromium と WebKit は ::-webkit-scrollbar にスタイルを当てると重ね表示をやめて、場所を取るバーに切り替わる。overflow-x: auto のままなので、はみ出していない表にバーは出ない。嘘の合図にはならない。
もう1つは、スクロールできる向きの端をぼかす JS。端まで送ったら、その側のぼかしを消すようにした。出しっぱなしにすると「まだ続きがある」という嘘になる。
バーが出ている環境では、この2つが同時に見える。

問題はバーが出ない側だ。iOS Safari のように ::-webkit-scrollbar が効かない環境で何が残るかを見たかったので、バーを消して撮ったのが下の1枚。表は左端にいるので、ぼけているのは右側だけ。この1つが、まだ続きがあることを示す唯一の合図になる。

穴は残っている。JS が止まればぼかしは消え、バーの出ない環境と重なれば、セルの切れ目だけに戻る。
※ 画像はダークテーマでの表示。出かたは手元で確かめた範囲のもので、iOS の実機では未確認。
達成基準が無いところ
直し終えてから、今回触ったものを達成基準に並べ直してみた。
横スクロールそのものは 1.4.10 の例外で守られている。キーボードで送れることは 2.1.1 キーボードが要求する。名前と役割は 4.1.2 で拾える。では「スクロールできることが、見て分かるか」はどこにあるか。見つからなかった。
近いものはある。2.4.7 フォーカスの可視化は、いまどこにいるかが見えることを要求する。1.4.11 非テキストのコントラストは、UI 部品の輪郭が背景から見分けられることを要求する。けれどスクロールバーはブラウザが出す部品で、こちらが置いた UI 部品ではない。どちらの射程にも素直には入らない。
前の記事で、読解に関わる基準が AAA と参考文書に置かれている話を書いた。検証できる形に落ちるものが必須の側に来て、読み手によって割れるものが外に残る。今回も同じ形だった。「スクロールできることが伝わるか」は、ページを機械で見て判定できない。判定できないものは、達成基準にならない。
表は直った。スマホで開いても読める。ただ、直ったと言える根拠は「自分がスマホで見て読めた」でしかない。一周して、また同じ壁の前にいる。