豪鬼メモ

一瞬千撃

Fakebook: ゼロから作るSNS その9 縦書きのWebページは有りか無しか

縦書きのWebページやブログというのは実用になるのか。この疑問をちゃんと解決するために、縦書き用のCSSをかなり真面目に書いてみた。

希少性

英語などの欧米の言語は、横書きが普通である。つまり、1行を左から右に読み、折り返して少しずつ上から下にスクロールしていく。しかし、日本語や中国語や朝鮮語は、本来は縦書きである。つまり、1行を上から下に読み、折り返して少しずつ右から左にスクロールしていくものだ。紙の本では今でも縦書きが多く、日本語の小説のほとんどは縦書きで組版したものが出版されていると言っていいだろう。

Web関連技術は欧米の文化圏の影響が強く、HTMLのデフォルトのスタイルは横書きだ。横書きの中でも、英語のように左から右に読むLTR方向だけではなくアラビア語ヘブライ語のように右から左に読むRTL方向があり、そのサポートはCSSで切り替えることができる。Googleなどの超大手Webサービスは、LTRだけじゃなくRTLもサポートしていることが多い。CSSでは縦書きのサポートもある。「writing-mode: vertical-rl」というスタイルを定義すると、その要素は縦書きかつ右から左にスクロールする方式にできる。しかし、残念ながら、縦書きへの切り替えをまともにサポートしているWebサービスは見たことがない。

その理由は簡単だ。縦書き表示は、需要が小さい割に、実装と調整がめちゃくちゃ大変だからだ。幅を基準にして調整していたレイアウトを全て高さを基準に調整しなおす必要があり、全てのマージンやパディングのX方向とY方向を転置する必要がある。さらに、HTMLのレンダリングのモデルのかなり多くの部分が上から下に進むことを前提に作られていて、writing-modeを変えるだけでは追従しきれない様々な問題が生じる。

デモ

とはいえ、めちゃくちゃ頑張って、自作SNSの外部ブログ公開機能にて、縦書きモードをサポートしてみた。それを見る前に、まずは、横書きモードで「走れメロス」を御覧いただきたい。newspaperというデザインテーマで、新聞っぽいちょっとレトロな雰囲気のデザインにしてみた。

専らWebサイトや技術書で文字情報を摂取している技術者諸氏や理系出身者の人々は、横書きで文学を読むのに何の抵抗もないかもしれない。しかし、文学好きや文系出身者の人々は、横書きで文学作品を読むことに違和感を感じるだろう。太宰は縦書きの原稿用紙を使っていただろうし、縦書きで組版されて出版されることを想定して執筆していたはずだ。縦書きと横書きでは、文章から感じる格調やらリズムやらが変わってくる。フォントやら文字サイズやらのスタイルの違いとは比べ物にならないくらい、縦書きと横書きの違いは読書感に大きな影響を及ぼす。個人的な感覚を言わせてもらうと、横書きだと小説なのに日記やらブログやらの軽い文章であるように感じてしまい、没入感に劣る気がする。

では、いよいよ縦書きモードで「走れメロス」を御覧いただきたい。データ構造は横書きとほぼ同じだ。shimbunというデザインテーマで、横書きのテーマとフォントと色使いを同じにしてある。

一目で、文学っぽい、小説っぽい雰囲気を感じていただけただろう。ウィンドウが幅が広すぎるとどこを読んでいるか見失いがちになるので、ウインドウ幅を狭めて、視界に入る行数を20行未満にしていただきたい。スマホ等の細長い画面で見ると、むしろ読みやすくなる。というか、むしろスマホでこそ使って欲しい。

個々の記事の画面だけではなく、ブログとしてのサイトのトップページも縦書きにしている。このデザインは新聞の三行広告っぽくしている。1画面で100記事まで表示できるので、過去記事を一覧するのにとても便利だ。タブでリッチ表示とプレーン表示を切り替えるインターフェイスを縦書きで表現するのは結構大変だったが、ここだけ横書きにするわけにもいかないので、頑張った。

効果

実際に読んでみると、やはり没入感が高い気がする。文庫本と同じく1行に38文字程度が収まるようにしていて、視線移動が1回程度で行全体の文字が追えるようになっている。実際には眼球はサッケードと呼ばれる急激な動きと停留を繰り返しているので、視線移動を「1回」「2回」と数えるのは難しいのだが、ストレスを感じずにサッケードを重ねて行方向に視線を動かすと同時に内容を認知する操作を便宜上1回と数えることにする。新聞のように1行の文字数を12文字とかまで減らして縦の視線移動を不要にする策を講じることもできるが、速読向けの調整をすると逆に没入感が下がる。眼球移動が1行1回だと、次の行に移るための眼球移動と組み合わせると、読書のリズムがすこぶる良いのだ。「下、斜め上、下、斜め上、下、斜め上、…」とN字の動きを繰り返すことになる。そして、じわじわと横スクロールしながら読んでいくと、ブラウザを操作している感覚を忘れて、物語に没頭することができる。

「それって別に横書きで1行の文字数を減らしても同じなのでは?」「Z字の動きだって良いリズムなのでは?」と突っ込まれるだろう。それ対しての反論を私は持たない。慣れの問題に過ぎないと私は思っている。眼球を横に動かすのと縦に動かすので疲れ方が違うとか、目が横に並んでいるとかの影響で、生理学的な効率が若干違う可能性はあるが、慣れと個人差の影響が大きすぎて統計的に妥当な見解を出すのは難しそうだ。単に縦書きで文学を読むのに慣れているから、縦書きだと没入しやすいというだけだろう。横書きで文学を読むことが多いなら、逆に縦書きだと違和感を覚えるだろう。とはいえ、多くの日本人にとっては、いわゆる「情報もの」は横書きで摂取し、小説や随筆などの長文は縦書きで摂取することが多い。よって、一般論として、縦書きの方が没入しやすい傾向にあると言うのはあながち間違いではないだろう。

Webで文章を公開するサービスを運用するならば、そこで小説や論考などの長文を公開する人もいるだろうから、縦書きのスタイルを選べるようにするというのは、一定の価値がある。需要は確かに小さいかもしれないが、他にない独自性が出せるわけで、刺さる人には刺さる機能になるかもしれない。

1行の文字数

横書きだと1行45文字に設定しているのに、なぜ縦書きでは38文字なのか。それは、私が試してみて読みやすいと感じたからだ。1行の文字数が少ないほどに折り返しの際の視線移動が楽になるが、その分だけ折り返しの頻度が増えるので、総合的なストレスが最も低いのは中庸のどこかだ。ところで、視線を折り返す際には、元の行のすぐ隣りの行に視線を移動させることになる。横の視野の方が縦の視野より広いので、横書きの折り返しの方が、視線が行単位でずれて迷うリスクが上がらない文字数の上限が高いと考えられる。このことだけから考えると、横書きの方が縦書きよりも読みやすいとも言えそうだが、視線が迷わないとしても視線移動量が多いと疲れるので、横書きの方が総合的に楽だとは言い切れない。ではなぜ横書きの方が1行の文字数を多くしているのかというと、図などを並べる際に表示欄が広めの方が都合が良いからだ。長文の快適さだけを考えるなら、横書きでも38文字かそれ未満のほうが楽だと思う。

結局のところ、1行何文字が最適なのかについてのコンセンサスは世の中にはない。文庫本でも、出版社によって違うし、同じ出版社でも作品によって違う。縦書きの小説の場合、38文字から42文字くらいにばらけている。横書きの場合、文芸以外のものが多くなるのでもっとばらけるのだが、30文字から45文字くらいのものが多い気がする。横書きに短いものがあるというのは、横書きの方が1行の文字数を増やしやすいという私の仮説と対立しているようにも見える。しかし、それは主に紙面の都合だろう。縦に細長い紙面に横書きすれば、当然1行に置ける文字数は減る。オライリーの技術書とかは幅広のB5変形判が多く、1行は45文字とかで長い。

余白も重要だ。視線を往復させる際に、無用な刺激があると、没入感が下がる。文章を読む際には、文章以外のものを視界に収めるべきではないのだ。ページ端ギリギリにある文字を読もうとすると、どうしても背景が目に入ってしまう。紙の本がページいっぱいに文字を詰め込まずに余白を大きく取っているのは、印刷の都合というよりは、視界遮蔽のためだ。縦書きの場合、左右はスクロールするからいくらでも余白は勝手に大きくなるので、上下の余白が重要だ。1回のサッケードとそれに伴う停留で読み取れる文字数は5文字から8文字と言われている。集中視界の大きさは半径2.5文字から4文字ということになる。つまり、折り返し後に本文の上端に視線移動してから停留している状態で、余白が2.5emから4emくらいがないと、本文以外のものが集中視界に入ってしまうことになる。安全係数を取って6emくらいの余白は欲しいところだ。縦書きの場合、横方向はスクロールするのでいくらでも大きく余白を取れるが、縦方向の余白は画面の高さに制限される。今回は、上下に1.5emを設けることにした。地味にしているサービスヘッダはほぼ余白と同等の役割をするので、それと合わせると上の余白は6em程度になる。下の余白は画面の高さの余った部分全てだ。

課題

Web上で縦書きの文章を表現するにあたっては、紙の本で縦書きの文章を表現する際と全く同じ課題がある。典型的なのは、数値や英字の表現方法が横書きと縦書きで異なることだ。「3000人」という文字列を縦書きにすると、「3000」という半角数字を横倒しにしてから「人」をそのまま書くという不格好なことになる。それが嫌なら、「3000人」と全角数値にすると横倒しにならなくなる。「三千人」と漢字で書いてもよい。英字の場合「SQL」などと半角にすると横倒しになるので、「SQL」と全角英字にするか、「エスキューエル」と読み下すかを選択することが多い。しかし、「PostgreSQL」とかの長い英単語だとダサい。「PostgreSQL」として縦に並べても、「ポストグレスキューエル」と読み下しても不格好だ。横文字のダサさに関してはどうしようもない。

投稿記事内で横文字をどう扱うかは、その著者が決めるべきことだ。一方で、ブログシステムとしてのUIで使われる横文字をどう扱うかは、開発者が決めなければならない。例えば最新記事の欄につける「Recent posts」というラベルをどうするか。ひとつの解法は、ローカライズだ。ブラウザの言語設定が日本語であれば、「最新記事」と漢字に直してしまえば良い。しかし、これは抜本的な解決策ではない。なぜならブラウザの言語設定が英語の人も日本語の縦書き記事を読むからだ。その場合、英字のままで、縦書きをして統一感を出したい。英語圏でも縦長の看板は縦書きで書くことが普通にあるので、慣習的にも問題ないだろう。とはいえ、ローカライズ対象の言語毎に縦と横のカタログデータを持たせるのは保守コストの観点で許容できない。仕方ないので、ここだけJavaScriptで介入して、縦書きの際にはUIパーツのラベルを全角英数字に自動変換する機能を入れた。

数式や整形済みテキスト(pre)をどう扱うかも悩んだ。数式に関しては、横倒しをぜず、RTLの横書きのまま表現することにした。横書きだと幅をとってダサいが、読みにくいと話にならないので、ダサさを許容することにした。preに関しては、地の文と同様に、半角英数字は横倒しになるが、仮名や漢字などの全角文字はそのまま表示されるようにした。結果として、ソースコードは全体が横倒しになるが、縦書きにするとかえって見づらいので、横倒しの方がマシだ。

画像のフロート表示とグリッド表示も、縦書きとの親和性を高めるにはかなり苦労した。STGYのヘルプ文書を横書きモード縦書きモードで比べるとわかるが、等幅で画像を表示するグリッドのスタイルを全て等高で表示するように書き換えるスタイルに直す必要があった。

横書きの場合、紙メディアではインデントで段落の区切りを表現していたが、Webでは広めのマージンで表現するのが一般的になっている。紙面を節約するという概念から解放されているので、それは妥当な進化だと言えよう。しかし、縦書きで同じことをすると、妙に不格好になる。縦書きはそもそも行間が広めなので、マージンでさらに広くしてしまうと、台詞の掛け合いなどの段落が多いシーンでは視界がスカスカになってしまう。よって、一般的な縦書きの新聞や小説と同様に、段落間と行間の間隔はほぼ同じにして、インデントで段落区切りを示すようにした。読みやすさの根拠を読者の慣れに置く以上、一般的な慣習に従うのが合理的だろう。

「段落を1文字分字下げする」だけなら簡単なのだが、日本の出版業界では「天付き」と呼ばれる例外規定がある。「段落が鍵括弧で始まる場合、字下げをしない」ということだ。これは慣習に過ぎないのだが、「正しい原稿用紙の使い方」として小学校でも教えられていて、それに違反すると減点される程度には強制力がある。よって、新聞社などのサイトでは、字下げしたい段落だけ全角空白で始めるという手段を取っている。その方法は柔軟ではあるが、構造化文書の観点からは許容できない。スタイルシートで解決すべき問題なのだ。しかし、現状のCSSの仕様では、子孫の最初のテキストノードが特定のパターンを持つかどうかを判定することはできない。したがって、事前にASTを走査してその判定をしておいて、HTML生成時にdata-mode属性として伝搬させる必要があった。その値が「mixed」のものを拾って字下げをキャンセルすることで、慣習的な体裁を保てるようになる。

 彼は陽気に散歩していた。
「こんにちは」と突然、若い女性に声をかけられた。
「いつもこの時間散歩していらっしゃいますね。」彼女は言う。

さらに面倒くさい問題もある。一つの発話だけからなる会話文の段落は、冒頭の括弧を天付きにしつつ、2行目以降を字下げする「ぶら下がり」が行われる。鍵括弧で始まっていても、その発話が途中で終わって地の文を含む場合、2行目で最初の発話が続いていたとしても、ぶら下がりはしない。つまり、段落が鍵括弧で始まっていて、中間に鍵括弧閉じを含まずに、鍵括弧閉じで終わる段落は、ぶら下がりの対象になる。HTML生成時にdata-mode属性の値「quote」をつけておいて、それをCSSが拾うことで実装する。マージンで全体を下げてから冒頭に字上げ(負の字下げ)を施すのだ。なお、ぶら下がりは天付きと同様に一般的な慣行だが、学校の指導内容にはない。おそらく、2つ理由がある。一つは、手書きの原稿用紙だと、後で地の文の有無を変えたくなった時に段落全体を書き換えるのが辛すぎるからだ。もう一つは、採点する際に段落全体を読んでからバックトラックして冒頭を確認するのが辛すぎるからだ。

 彼は陽気に散歩していた。
「こんにちは」と若い女性に声をかけられた。突然のこと
だったので彼は驚いた。
「いつもこの時間散歩していらっしゃいますね。そんなに
 お散歩が好きなんですか?」

改めて考えると、これらの例外規定は論理と感性の衝突の好例だ。論理的に考えると、全ての段落を字下げすべきで、そうすることで段落区切りを確実に判定できる。一方で、感性や美観を重視するなら、鍵括弧というスカスカな文字と字下げが重なるとダサいので、字下げしたくない。しかし、字下げをしないと、前の行がたまたま表示欄の下端近くで終わっていると、段落区切りなのかそうでないのかの区別ができなくなってしまう。38字詰めなら、1/38の確率でそれが起こるので、決して珍しくはない。メロスの例を見ればわかるように、台詞の開始が段落区切りではないことも普通にある。原稿用紙の作法って、原稿なのに天付きという組版のルールが中途半端に混入している。原稿と組版の関係は構造化文書とスタイルの関係と同様に分離されるべきはずなのに、それでもなお原稿用紙で天付きをしてしまうほどに、スカスカ感は嫌悪されるということだろう。理系(自然科学)的な思考をすると、曖昧性があるのは許せない気もするが、文系(人文科学・社会科学)的な思考をするなら、曖昧だろうが美的に向上している方が良いということになる。個人的には理系派閥に与したいところだが、そこを曲げて、一般的な慣習に従うという判断をした。

以下の例で、左は例外規定を適用してないもので、右は適用したものだ。読みやすさの違いは一目瞭然だろう。純粋な会話文が続く場合、例外無く1文字下げる方法だと、1行目の内容文字と2行目以降の内容文字の高さ2文字分も違うのに対し、天付きとぶら下がりを両方適用すると、内容文字の高さが全て揃う。これに慣れると、そうでない組版に対して違和感を超えて苛立ちすら覚えるだろう。

ここまでの組版ルールは、出版業界で使うAdobe InDesignとかで当然のように実装されている機能ではあるが、Webサイトで真面目にやっているところは見たことがない。小説家になろうとかでは、作家自ら全角空白を入れることで字下げを調整している。小説家になろうには縦書きPDFダウンロード機能があるが、手動字下げと手動天付きをやっているおかげで、そこそこ読みやすい組版結果が得られている。しかし、手動で空白を挿入する方法だとレスポンシブデザインで適切にぶら下がりを表現することができない。よって、機械的なモード判定を基本とし、全角空白での字下げは廃れるべきだ。ただし、最近のラノベは、機械的に判定しづらい表現も出てきている。例えば、丸括弧でモノローグを表す場合、それは会話文なのか地の文なのか、作者の意図が判然としない。「「「了解!」」」みたいに、鍵括弧を重ねて多人数が唱和することを表す慣習も出てきているが、それをどう組版するかにコンセンサスはまだない。よって、機械的な判断を基本としつつも、モードをメタデータとして指定できることが望ましい。Markdownであれば、適当な記法を設けることでそれが可能になる。

縦書きに限った話ではないが、長文をじっくり読むにあたっては、文字と背景のコントラストは高すぎない方が良い。コントラストが高いほうが視認性が高いが、視認性が十分に達した範囲では、コントラストが低めなほうが目が疲れにくい。よって、文字色を#111111にし、背景色を#f8f6f4にしている。これは17.516:1程度のコントラストがあり、WCAG推奨のAAA最低コントラストの7:1よりも遥かに高い。背景色を微妙に黄ばませているのは、紙っぽくするためだ。さらに、紙繊維の模様を模倣すべく、視認できるかできないかのギリギリの濃度で乱数調のテクスチャを入れている。そうすることで、文字が浮き出る感覚が抑えられて、紙や電子ペーパーの質感に近くなる。テクスチャのコントラストは最大1.09:1なので、文字の認知を阻害するマスキング効果はほぼ無い。下記で、上がテクスチャ無し、下がテクスチャ有りの例だ。これを見ると、私の言わんとしているところが分かっていただけると思う。テクスチャ有りの方が文字の輪郭にギラつきがなく、マイクロサッケードのストレスも小さい、気がする。思い込みかもしれないし、私だけかもしれないが、少なくとも私の主観的体験としては何度比較しても再現性がある。

乱数調のテクスチャは、以下のCSSで生成している。15度傾けた周期2.7ピクセルのグラデーションと、62度傾けた周期3.5ピクセルのグラデーションと、83度傾けた周期5.3ピクセルのグラデーションを重ねている。総合的な周期はLCM(27,35,53)/10 = 5008.5pxになるので、実質的に画面内に同一パターンが表れないことになり、ピクセル単位やサブピクセル単位の丸めも相まって、あたかも乱数模様であるかのように感じるという仕掛けだ。周期を非整数かつ大きめにすることは、モアレを抑制するのにも重要だ。

background-color: #f8f6f4;
background-image:
  repeating-linear-gradient(15deg, #f4f4f2 0, #f4f4f2 1px, transparent 1px, transparent 3px),
  repeating-linear-gradient(62deg, #fbfbf8 0, #fafaf8 1px, transparent 1px, transparent 3px),
  repeating-linear-gradient(83deg, #ffe 0, #ffe 0.5px, transparent 0.5px, transparent 2px);
background-size: 2.7px 2.7px, 3.5px 3.5px, 5.3px 5.3px;

テクスチャを入れるとなぜ文字が浮き出ないのかは私もよくわかっていない。ノイズがあることで脳内ローパスフィルタが働くという仮説を持っているのみだ。つまり、高周波の模様から有意な情報を読み取ることを前注意的に諦めて、部首などの低周波の模様からゲシュタルト構造を読み取ることに集中できるのではないかと考えている。映像表現でも、低周波構造に注意を向けるためにノイズ添加やシルエット化で高周波成分を隠す技法がよく使われる。ただし、テクスチャ自体が認知できてしまうとかえって気が散るので、模様と濃度が重要だ。アンチエイリアシングにも似たような効果があるのだが、手法が真逆だ。文字をぼかすのがアンチエイリアシングであり、背景を乱雑化するのが背景テクスチャだ。ジャギーを隠して低周波構造に注意を向けさせるという点で、背景テクスチャはアンチエイリアシングを補完する効果があると考えられる。さらに言うと、ジャギー的刺激の緩和の他に、視線移動の際の迷いが減る効果もあるような気がしている。背景が単色だと、文字しか視線の基準がないので、サッケードに伴う微細な視線のずれでホワイトアウト的な迷いが生じやすい。それに対し、微細な模様があると、いかなる視線移動に対しても移動検出の基準があることが保証されるので、視線移動のストレスが減る効果があるのではないか。

なお、SVGフラクタルノイズを使うと、より乱数的で予測困難な模様を生成できる。しかし、以下の例で分かるように、紙の繊維っぽくないし、汚い印象になる。繊維の影というよりは、不純物っぽいノイズなのだ。文字の浮き出しを抑える効果と視線移動を安定させる効果は重層グラデーションと同じくらいだ。つまり、予測困難性が一定以上高ければパターン認識の対象からは除外され、その閾値よりも予測困難性を高めても得がないということだろう。新聞紙の小汚さに近づけるならフラクタルノイズを薄く乗せると良いかもしれないが、読みやすくするという意図に対しては不適である。

background-color: #fcfaf4 !important;
background-image:
      url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2764%27%20height%3D%2764%27%3E%0A%20%20%3Cfilter%20id%3D%27n%27%3E%0A%20%20%20%20%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.8%27%20numOctaves%3D%273%27%20stitchTiles%3D%27stitch%27%2F%3E%0A%20%20%20%20%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%0A%20%20%3C%2Ffilter%3E%0A%20%20%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20filter%3D%27url%28%23n%29%27%20opacity%3D%27 0.3 %27%2F%3E%0A%3C%2Fsvg%3E") !important;

「サイドバー」を縦書きで表現するのも苦労した、「サイド」には余白ができないので、「ボトムバー」として実装しなおす必要があった。ウィンドウの高さが70em以上である場合、本文に45emくらい使った余りの25emくらいの領域に、サイトプロファイルや最新記事リストなどの情報を埋め込むのだ。埋め込む情報もまた縦書きにして、ラベルを全角に直すなどの処理も入れている。おかげで、横書きモードと全く同じ機能性を維持することができている。保守の手間が最小化するように、HTMLの構造は全く変えずにCSSだけで頑張った。

縦書きだと横スクロールしながら本文を読んでいくことになる。トラックパッドやタッチデバイスの場合、左右のスワイプで横スクロールができる。。スワイプでスクロールする場合、ちょっとした問題がある。記事の末尾(左端)でさらに右スワイプして左に進もうとすると、ブラウザのオーバースクロールジェスチャー処理が発火して、履歴上の前のページに遷移してしまう。同様に、記事の冒頭(右端)でさらに左スワイプして右に進もうとすると、履歴上の次のページに遷移してしまう。それを抑制するために、「html:has(.pub-theme-dir-vert) { overscroll-behavior-x: contain; }」などというCSSを書いて、横方向のオーバースクロールの履歴移動を無効化した。しかし、ジェスチャーによる履歴移動を完全に無くすと不便すぎる。よって、画面端に既に0.5秒以上居る状態で強め(100px分)にスワイプすると履歴移動するように再実装した。これなら、意図せぬ履歴移動を抑止しつつ、明確な意思のあるジェスチャーをすれば、既存の操作感をほぼ変えずに履歴移動できる。それでも、前のページに戻りたい場合に記事全体を左スクロールで横断しなきゃならないので、だるいことがある。その救済策として、下スワイプで上に進もうとすると履歴上の前ページに遷移して、上スワイプで下に進もうとすると履歴上の次ページに遷移する機能もつけた。ただし、この機能は誤動作しないように130px分のかなり強いスワイプを閾値にしている。

マウスで横スクロールしたい場合、シフトを押しながらマウスホイールを回すという操作ができることになっているが、そんな面倒くさいことをする人はそうそういないだろう。ボタンを押すなら、カーソルの左右キーを押したほうが楽だ。もちろん、スクロールバーを掴んで横スクロールすることもできるので、それを使っても良い。マウスホイール単体でのスクロールが使えないのは、それを日常的に使っている人にとっては結構苛つく問題だ。しかし、良い代替策がない。マウスホイール単体で横スクロールさせることも技術的には可能だが、直感に反しすぎるので、やめた。中途半端な操作系なら無い方がマシだ。標準機能で不満であれば、横チルト対応マウスホイールとか、サイドホイール付きマウスとか、マルチボタンマウスのキーバインドで横スクロールを割り当てるとか、デバイス側で工夫してもらう。

ブログトップページにある過去記事の一覧を見ながら記事をチラ見していくとして、記事ページに行ってからブラウザバックで戻った際には、一覧ページでのスクロール位置は記事ページに行く前の位置になっていることが望ましい。ページ冒頭に戻ってしまうと、また該当記事のところまでスクロールするのがだるいからだ。ところで、縦長のページの途中でリンクをクリックしてからまた戻ると、ブラウザの機能で、縦スクロール位置は復元される。STGYの内部の投稿一覧はCSRなので、ブラウザ元来の縦スクロール位置復元機能が働かず、自前のスクロール復元機能を用意する必要があった。外部公開ページはSSRなので、ブラウザ元来の縦スクロール復元機能が働く。しかし、縦スクロール復元は働くが、横スクロール復元は働かない。横書き前提の罠がここにもある。しかたないので、SSRなのに、自前で横スクロール位置復元機能を実装した。一覧ページに戻ってきた際に、さっき見ていた記事の右端が画面の右から40%の位置に来るようにするのだ。

縦書き表示は外部公開用のデザインテーマでのみの対応であることに注意されたい。SNSとしての機能には一切関係ない。もしSNSのUIの全てに縦書き表示を実装してしまうと、工数が2倍じゃ済まなくなる。投稿記事を外部公開するモーダルでのみ縦書き対応をするなら、Markdownの構造化文書を縦書き表示することと、サイドバーやページネーション等のナビゲーション機能の縦書き対応をするだけで済むので、追加の工数が現実的な範囲で収まる。

フォント

縦書きの文章に適したフォントは何か。Macの標準であるヒラギノ角ゴとヒラギノ明朝に加えて、Google Fontsで使える人気のあるフォントを並べて比較してみよう。

Hiragino Kaku Gothic ProN
Noto Sans JP
BIZ UDGothic
Hiragino Mincho ProN
Noto Serif JP
BIZ UDMincho

一般論としては、縦書きには明朝体が適していると言われている。明朝体では縦線が太くなるので、縦方向に視線を誘導する効果があるからだ。1段目のゴシック体の3フォントと2段目の明朝体の3フォントはそれぞれかなり似た骨格で装飾だけ違うものになっている。それぞれを別タブで開いて切り替えながら見比べていただきたい。やはり、Hiragino Kaku Gothic ProNよりもHiragino Mincho ProNの方が、縦に視線を動かしやすくなっている。同様に、Noto Sans JPよりもNoto Serif JPの方が、またBIZ UDGothicよりもBIZ UDMinchoの方が、縦書きに向いていると言えそうだ。逆に言えば、横書きに明朝体を使うとその特性は副作用になる。

明朝体のHiragino Mincho ProNとNoto Serif JPとBIZ UDMinchoの中でどれが良いかと言われたら、断然、Hiragino Mincho ProNである。横書きでHiragino Kaku Gothic ProNが最強だったのと同様に、横書きではHiragino Mincho ProNが最強である。しかし、それらはMacでしか使えない。Windowsでは、Hiraginoと同じ会社が作ったYu GothicとYu Minchoが代替になる。システム非依存で使える中で選ぶなら、Google Fontsで利用できるNoto Sans JPとBIZ UDMinchoのどちらが良いかという話になる。字形が美しく、漢字や平仮名が読みやすいのはNoto Sans JPの方だが、視認性が良いのはBIZ UDMinchoだ。また、横倒しの半角英数字が混ざった場合のガタツキが少ないのはBIZ UDMinchoの方だ。BIZ UDMinchoは、ユニバーサルデザインと名乗るだけあって、横書きでも違和感がないように縦横の線の比率が近くなっていて、横書きでも使えるのが利点だ。

結論としては、横書きのデフォルトであるdefaultテーマでは、Hiragino Kaku Gothic ProN、Yu Gothic、Noto Sans JPを優先度順に指定し、縦書きのデフォルトであるtategakiテーマでは、Hiragino Mincho ProN、Yu Mincho、Noto Serif JPを優先度順に指定することにした。新聞を模した横書きのnewspaperテーマではBIZ UDGothicを、新聞を模した縦書きのshimbunテーマではBIZ UDMinchoを使うことにした。HiraginoとYuとNotoを使い分けるということはシステム毎に使われるフォントが異なるということになるが、その複雑性は許容する。SNSの内部のUIではIBM Plex Sans JP固定で統一して保守性を確保するとともに、UIが単純な外部公開記事のデザインテーマではベストエフォートで読みやすいフォントを利用するのだ。

縦書きにはブロック体より明朝体が適することを実感するために、横書きのデフォルトフォントにしてあるブロック体のIBM Plex Sans JPで縦書きを表現した例も見てみよう。視線が横方向に逸れようとして、縦方向の移動が引っかかる感じがするだろう。縦書き表示により読書体験を最適化するにあたって、フォントの選択がいかに重要かを分かっていただけるかと思う。

Google Fontsでは他にも明朝体のフォントがある。また、手書きフォントのいくつかも縦書きに使えそうだ。それらの使用感も見てみよう。

Shippori Mincho
Zen Old Mincho
Zen Antique
Hina Mincho
Klee One
Zen Kurenaido

Shippori Minchoは、Noto Serif JPと字形がかなり似ているのだが、平仮名や片仮名が漢字に比べて小さいのが特徴的だ。初代ロマンシング・サガとか信長の野望全国版とかで使われていた昔のドットフォントを彷彿させる。Zen Old MinchoもNoto Serif JPと字形が似ているが、懐(口とか日とかの箱型)がすぼまっていて、より手書きに近い字形になっている。両方とも面白い試みではあるが、読みやすいかというとそうでもないので、今回採用するには至らない。その他のものは、読みやすさというよりは、飾りに特化したものだ。Zen Antiqueは、縦線をめちゃ太くして、迫力を出している。通称エヴァンゲリオンフォントのマティスを彷彿させる。Hina Minchoは、パッケージとかの短い説明文でよく出てきそうな可愛らしい形だ。Klee Oneは、手書き風なのにどこか教科書体を彷彿させる読みやすいフォントで、横書きのいくつかのデザインテーマで採用している。Zen Kurenaidoは、手書きっぽさをさらに強調して書き殴った印象すら抱かせるものだ。

プロポーションとカーニング

プロポーショナルフォントであれば、英数字はプロポーショナルすなわち可変幅で描写される。しかし、日本語の仮名や漢字やプロポーショナルフォントであっても、デフォルトでは等幅で描写される。単語毎に意味を読み取る英語などの言語の文字は、なるべく詰まっている方が読みやすいので、プロポーショナルであることが必須だ。一方、文字毎に意味を読み取る日本語などの言語の文字には、文字の位置が等間隔である方が文字認識や視線移動の負荷が小さくなるので、等幅の方がむしろ読みやすい。それにプロポーショナルにしたとしても、「i」とか「l」みたいに極端に細長い文字はそんなにないので、空間効率向上の利点が小さい。よって、プロポーショナルフォントなのに等幅で描写するのが日本語表現のベストプラクティスになっている。カーニングとは、文字間を調整することだ。等幅描写の利点である文字位置の等間隔性を保証するには、当然カーニングも無効化されねばならない。

本格的な日本語フォントは、プロポーショナル代替幅(palt = proportional alternative width)というメタデータと、カーニング(kern = kerning)のメタデータを備えている。それらはブラウザのデフォルトスタイルで無効化されているだけであり、CSSで簡単に有効化できる。font-feature-settings: "palt" 1, "kern" 1; とか書くだけだ。以下のスクショは、上がデフォルトのpaltとkernを無効化にしたもので、下が両者を有効にしたものだ。フォントは両方とも游ゴシックである。

縦書きでも似たようなことができる。書くべきCSSは font-feature-settings: "vpal" 1, "vkrn" 1; だ。縦書きでpaltとkernを使うと、横方向に文字の位置を調整してしまう結果、中心軸が左右にぶれるので、害悪になる。vpalとvkrnならば、縦方向にだけ位置を調整するので、中心軸は保たれる。以下のスクショは、左がデフォルトのvpalとvkrnを無効化にしたもので、右が両者を有効にしたものだ。フォントは両方とも游明朝である。

比較すれば明らかなように、日本語は等幅の方が読みやすい。デフォルトでpaltとkernが無効化されているのは頷ける。じゃあ何のためにpaltやkernがあるのかというと、主に、バナーやヘッダなどのように短いラベル的な文字列を凝縮して配置するためだ。よって、各種のデザインテーマでは、サイト名の部分でpaltとkernを有効化している。さらに、サイドバーのスニペットでもpaltとkernを有効化している。上がサイドバーでpaltとkernを無効化にしたもので、下が両者を有効にしたものだ。

紙の本でも、横書き縦書きにかかわらず、等幅(等高)組版をすることが多い。ただし、鍵括弧や句読点などの約物だけを半角にする約物半角というスタイルも広く行われている。約物半角だと、鍵括弧で始まる段落の内容の開始点のずれが1文字分ではなく0.5文字分になるので、ぶら下がりのずれも0.5文字にする。また、字下げも0.8文字分くらいにする。そうすると、少なくとも、美観はかなり向上する。以下の例の行頭のレイアウトに注目いただきたい。上の例がデフォルトで、下の例が約物も含めて全てをプロポーショナルにしたものだ。

約物半角の美観向上効果はそこそこ高いが、内容の文字はプロポーショナルにはしたくない。残念ながら、CSS約物だけを指定する方法がないので、約物半角の導入は見送った。JavaScript約物を検出してspanを付けまくるという方法もとれるが、保守コストが割に合わないので諦めた。約物半角をすると行末がずれるという欠点もある。ジャスティファイ(両端揃え)をすれば解決できるが、そうすると文字間隔を一定にするという利点が台無しになる。全てはトレードオフだ。

原稿用紙の作法では、鍵括弧の直前に句点がある場合、両者を同じマスに入れるというのがある。組版でも、プロポーショナルフォントとカーニングの効果でそれは実現される。それもまたpaltとkernを有効化したくなる理由ではあるが、現代ではその実用上の効果が薄いのも事実だ。なぜなら、戦後の出版業界の常識として、鍵括弧の前の句点は省略することになっているからだ。走れメロスの時代には「まだ陽は沈まぬ。」と律儀に句点を書いていたが、現代の出版物では「まだ陽は沈まぬ」と書く。学校では句点を省略しないように指導されるのに、大人の世界ではそうなっていない。決めの問題なので、どちらが良いというわけじゃないのだが、どちらかにしてくれよと思う。

デザインテーマの追加

縦書きのデザインテーマは他にも作ってある。ホワイトボードを模したhakubanと黒板を模したkokubanである。双方とも、会社の会議や学校の授業で手書きで説明をしている場面を再現している。Zen Old Minchoをサイト名のバナーで使い、Klee Oneを本文で使っている。

横書き用も含めて、もうちょいファンシーなデザインテーマも用意すべきと思っている。STGYは知的クリエイター層に奉仕するサービスであるが、クリエイター層はエンドユーザ層に奉仕する人々である。外部公開した記事を読むのはエンドユーザだ。例えば、童話作家に使ってもらうとして、彼らが外部公開した記事を読むのはクリエイターではなく、子供達だ。そうすると、新聞やら会議やら授業やらの雰囲気を借りてくるのは不十分だ。パステル風味、ポップ風味、ファンタジー風味、ナチュラル風味などなど、記事の内容とエンドユーザの趣向に合わせたデザインテーマを順次追加していく必要がある。それらでは、Hina Minchoだけでなく、Kaisei Opti、Yomogi、Mochiy Pop Oneといった装飾系のフォントも活かせるだろう。

情報と構造に着目してMarkdown中間言語にしていることがここで生きてくる。内容とプレゼンテーションが完全に分離されているからこそ、縦書きでも横書きでも、柔軟なデザインテーマを適用できる。WYSIWYGポリシーならフォント名を記事データに埋め込むことになるが、その自由を制約するからこそ、後で表示方法に合わせたフォントが自由に割り当てられるのだ。記事毎にデザインテーマを割り当てる方法もいずれ考えるかもしれない。

入稿用Markdownの工夫

縦書き特有の話ではないのだが、日本語の文学作品を公開するにあたっては、ルビ対応は必須だ。特に古めの作品の場合、国文学専攻でもない一般人が初見で読むのは難しい漢字表現が多い。それをいちいち辞書で調べていてはまともに読書ができないが、読み方が分からないまま進んでいくと内容が頭に入って来づらくなる。ルビがあるとその問題は解決される。Markdownにルビ記法を導入するのは必須の要件だった。「{{邪智暴虐|じゃちぼうぎゃく}}」などと書くだけで簡単にルビが振れる。横書きの場合にはルビで行間が変わらないようにCSSの工夫が必要だったが、縦書きの場合にはルビを入れても全く違和感がない。

個々の記事を作品として公開する場合、ページの著者のメタデータとしてサイト運営者が載ってしまうと、索引を作る検索エンジンやシェア先のSNSが誤解してしまう。サイトの運営者は私であっても、走れメロスを書いたのは太宰治だ。有名作品であれば誤解する人は少ないだろうが、そうでもない作品を紹介する際に誤解されてしまうと、思わぬトラブルに発展するかもしれない。よって、記事内に著者名をセマンティックに記述できる記法を設けた。単一アカウントで複数の著者を表現できると、リレー小説とかトリビュートとか部誌とかいった合作用途にも使える。ついでに、出版日時を指定できる記法も設けた。

# 走れメロス

-@author 太宰治
-@date 一九四〇年五月

メタデータとして指定された文字列は、HTMLの標準meta要素やそのDublin Core拡張やOGP拡張に反映される。日時に関してだが、その記事を公開した日時と、その記事の内容である作品が公開された日時は、別であることに留意すべきだ。全角数字や年月の漢字を使ってもISO日時表現に正規化する処理を書くのにかなり苦労した。メタデータを正確に記述することは、情報科学というより情報学的な観点で重要なことだ。

<title>国文学のブログ: 走れメロス</title>
<meta name="author" content="太宰治">
<meta name="description" content="メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した...">
<meta name="dc:title" content="走れメロス">
<meta name="dc:date" content="1940-05-01T00:00:000.000Z">
<meta name="dcterms:created" content="1940-05-01T00:00:000.000Z">
<meta name="dcterms:issued" content="2025-02-28T15:22:08.000Z">
<meta property="og:title" content="走れメロス">
<meta property="og:description" content="メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した...">
<meta property="og:site_name" content="国文学のブログ">
<meta property="article:published_time" content="2025-02-28T15:22:08.000Z">
<meta property="article:author" content="太宰治">
<meta name="twitter:creator" content="太宰治">
<meta name="twitter:title" content="走れメロス">
<meta name="twitter:description" content="メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した...">

descriptionにタイトルやメタデータの文字列が入ると冗長なので、単にMarkdownからメタ文字を取り除くのではなく、ASTを構築してから本文だけを抽出するという面倒なこともしている。検索エンジンやシェア先からの流入はWebコンテンツの生命線なのでここを手抜きするわけにはいかない。

デザインテーマ次第ではあるが、著者名などのメタデータは下揃えかつ下に1文字分マージンを設けるようにしている。また、題名などのヘッダは上から2文字分マージンを設けるようにしている。これは出版業界ではなく学校教育での原稿用紙の使い方の指導方法に則ったものだ。紙の本では題名や著者名は同じページには置かないのが普通だ。学習指導要綱を完全再現するには性と名の間に空白を入れなきゃならないのだが、機械的にやるのが難しいのと、出版慣行と対立するので、諦めた。というか、性と名を空白で分けるとかいう、学校での採点でしか使わないゴミルールが国語教育で残存している意味がわからない。

青空文庫からの移植

余談だが、デモサイトを作るにあたり、著作権切れの文学作品をWeb上で公開してくれている青空文庫からいくつかの短編小説を移植した。その際、いくつか苦労したことがある。以前紹介したHTMLのリッチテキストをコピペで入稿する機能によって、青空文庫XHTML形式のデータをコピペすればかなり簡単に移植できる。ヘッダやルビも含めて情報を失わずにコピペできるようになっている。

文書構造を考えなければ、このコピペ一発の作業で移植は完了する。しかし、この構造には問題がある。コピペ機能に問題があるわけじゃなく、青空文庫の元々のHTMLがWYSIWYG的な見た目のみを考えて作られていて、文書構造がきちんと定義されていないからだ。以下が実際のデータの抜粋である。

<body>
<div class="metadata">
<h1 class="title">羅生門</h1>
<h2 class="author">芥川龍之介</h2>
<br />
<br />
</div>
<div id="contents" style="display:none"></div><div class="main_text"><br />
 ある日の暮方の事である。一人の<ruby><rb>下人</rb><rp></rp><rt>げにん</rt><rp></rp></ruby>が、<ruby><rb>羅生門</rb><rp></rp><rt>らしょうもん</rt><rp></rp></ruby>の下で雨やみを待っていた。<br />
 広い門の下には、この男のほかに誰もいない。ただ、所々<ruby><rb>丹塗</rb><rp></rp><rt>にぬり</rt><rp>)...<br />
 何故かと云うと、この二三年、京都には、地震とか<ruby><rb>辻風</rb><rp></rp><rt>つじかぜ</rt><rp></rp></ruby>とか火事とか...<br />

タイトルがh1なのはいいけど、著者がh2ってのがおかしい。bodyの直下にbrを置いてマージンを調整しているのもおかしい。そして、最も本質的な欠点は、段落をpで囲むのではなくbrで区切ることで表現していることだ。これは構造化文書ではない。見た目としては、改行があって、次の行が全角スペースで字下げされているので、段落っぽい雰囲気がある。しかし、改行や字下げが単にレイアウト調整のために行われることもあるので、それが段落区切りであることは、機械的には分からない。

青空文庫は底本の見た目に忠実であろうとするWYSIWYGポリシーを実践していて、構造を「再解釈」することを嫌っていると推測される。印刷された底本にはレイアウト情報しかないのだから、どこからどこまでが段落なのかを入力者が解釈するのは僭越とも言える。それを控えてWYSIWYGに徹する姿勢はむしろ正しい。それが構造化文書のポリシーに反するからって、文学好きが文学を楽しむのに何も問題はない。しかし、構造化文書として取り込むという私の目的からすると、構造の再解釈に踏み込まざるを得ない。よって、上述したヒューリスティックを使ってメタデータ判定や段落判定をする簡単なスクリプトを書いて移植作業を行った。

青空文庫のもう一つの問題は、文字コードSJISであることだ。これに関しては、プロジェクト発足当時に日本市場でSJISが支配的だったことからすると仕方ないことだが、今となっては完全に時代遅れの仕様である。SJISに収録されていない文字を表現するために画像データと「※(「にんべん+參」、第4水準2-1-79)」とかいう独自マークアップを併用しているのだが、JIS漢字集合の対応表を見て正しい文字に置換する必要がある。この欠点があるからといって青空文庫の社会への貢献が曇ることは少しもないが、不便であることは確かだ。いずれ棚卸しをしてUTF-8として書き換えることが望まれるが、コストベネフィットを考えるとなかなか難しい話でもある。

段落スタイルの補足

Webで主流の段落をマージンで表現するスタイルが小説に不適であるという度合いは、ラノベ文化の浸透でさらに加速している。ラノベはとにかく段落が多いのだ。特に一人称視点だと地の文と主人公のモノローグが区別されないので、全てが会話文の様相で段落が分けられる。以下が典型例だ。これらのpに全てマージンを設けていると、地の文なのに視界がスカスカになってしまう。

<p>ドリンクバーに向かおうとした俺は慌てて座り直す。 </p>
<p>油断した。隣の席のカップルは同じ高校の生徒。それどころかクラスメイトだ。 </p>
<p>叫び声の主は八奈見杏菜。ゆるふわカワイイ系のクラスでも人気の女子だ。</p>
<p>向かいに座るのは袴田草介。こっちも目立つ明るいイケメンだ。いつも二人一緒にいたが、やはり付き合っていたのか。 </p> 
<p>しかしなんでまた、こんなところで痴話喧嘩をしているのか。俺は文庫本に目を落としつつ、耳をそばだてる。</p>

もし三人称視点で書くなら、こんな風に3段落程度で済む。人によっては1段落にまとめるかもしれない。

<p>ドリンクバーへ向かおうと腰を浮かせた温水は、慌てて席に座り直した。</p>
<p>油断した、心中で舌打ちをする。隣の席に座っているカップルは同じ高校の生徒であり、あまつさえクラスメイトだったのである。叫び声を上げたのは八奈見杏菜という女子生徒だった。彼女は所謂「ゆるふわカワイイ」系として、クラス内でも人気を集めている人物である。その向かいに座っているのは袴田草介。彼もまた人目を引く、明るい美男子であった。学校では常に二人が一緒にいる姿が見受けられたが、やはり交際していたのだろうか。</p>
<p>しかし、なぜまたこのような場所で痴話喧嘩をしているのか。温水は手元の文庫本に視線を落とすふりをしながら、隣席の会話に耳をそばだてた。</p>

三人称視点にしてしまうと主人公と読者の一体感を出すことが難しくなるから、この作品が一人称視点を選択するのは必然的だ。さりとて、モノローグに相当する部分に括弧をつけていたら読みにくくてしょうがない。よって、あたかも会話文であるかのように段落を切っていくのは必須だ。

マージンではなく字下げによって段落分けを表現することは、紙メディアで紙面を節約するという効果だけではなく、段落同士の連続性の感覚を制御する意味もある。段落のスタイルは作品が読者に与える印象すら変えてしまうので、それを選択できることは重要だ。一方で、縦書きでもマージンで段落を区切りたいという要望もあるかもしれない。もしその声を聞いた際には、そのようなデザインテーマを追加することになるだろう。

コントラストの補足

文字と背景のコントラストを計算する方法は結構面倒くさいので、ここにメモしておく。まず、#f8f6f4と#111111のコントラスト比は、(0xF8 + 0xF6 + 0xF4) / (0x11 + 0x11 + 0x11) = 14.47 という式で計算できるほど単純ではない。いくつか面倒くさい変換式を適用してから比較しないといけないのだ。CSSの色コードはsRGB値なので、RGBのそれぞれのチャンネルの値を線形RGB値に変換する必要がある。具体的には v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4 という式だ。#f8f6f4ならR=0.922, G=0.922, G=0.905で、#111111ならR=0.006, G=0.006, G=0.006となる。次に、Rec709が規定する輝度の算定式で、RGBチャンネルの値を合成して輝度値を算出する。具体的には 0.2126 * R + 0.7152 * G + 0.0722 * B という式だ。#f8f6f4なら0.920で、#111111なら0.006だ。そして、一般的なディスプレイのブラックポイント(#000000の実際の輝度)とホワイトポイント(#ffffffの実際の輝度)の比率を模式するために、それぞれに0.05を足す。これは簡便法にすぎないが、液晶からの光漏れやノイズや映り込みでブラックポイントの実際の輝度は決して0にならないという事実を反映する必要がある。0.920+0.05=0.970と0.006+0.05=0.056だ。最後にその2つ値の比率を出せば、コントラスト比17.516が出る。以下のPythonコマンドを用意しておくと便利だ。

#!/usr/bin/env python3

import sys

def srgb_to_linear(c_srgb) -> float:
  if c_srgb <= 0.04045:
    return c_srgb / 12.92
  return ((c_srgb + 0.055) / 1.055) ** 2.4

def hex_to_rgb(hex_color):
  s = hex_color.strip()
  if s.startswith("#"):
    s = s[1:]
  if len(s) != 6:
    raise ValueError("hex_color must be in '#RRGGBB' or 'RRGGBB' format")
  r8 = int(s[0:2], 16)
  g8 = int(s[2:4], 16)
  b8 = int(s[4:6], 16)
  return (r8 / 255.0, g8 / 255.0, b8 / 255.0)

def main():
  if len(sys.argv) == 2:
    hex_color = sys.argv[1]
    r, g, b = hex_to_rgb(hex_color)
    print(f"R={r:.3f}, G={g:.3f}, G={b:.3f} in sRGB")
    rl = srgb_to_linear(r)
    gl = srgb_to_linear(g)
    bl = srgb_to_linear(b)
    print(f"R={rl:.3f}, G={gl:.3f}, G={bl:.3f} in liner RGB")
    y = 0.2126 * rl + 0.7152 * gl + 0.0722 * bl
    print(f"Luminance={y:.3f}")
  elif len(sys.argv) == 3:
    r1, g1, b1 = hex_to_rgb(sys.argv[1])
    rl1 = srgb_to_linear(r1)
    gl1 = srgb_to_linear(g1)
    bl1 = srgb_to_linear(b1)
    y1 = 0.2126 * rl1 + 0.7152 * gl1 + 0.0722 * bl1
    r2, g2, b2 = hex_to_rgb(sys.argv[2])
    rl2 = srgb_to_linear(r2)
    gl2 = srgb_to_linear(g2)
    bl2 = srgb_to_linear(b2)
    y2 = 0.2126 * rl2 + 0.7152 * gl2 + 0.0722 * bl2
    print(f"Luminance: {y1:.3f} vs {y2:.3f}")
    contrast = (max(y1, y2) + 0.05) / (min(y1, y2) + 0.05)
    print(f"Contrast: {contrast:.3f}")
  else:
    raise ValueError("usage: check_luminance.py RRGGBB [#RRGGBB]")

if __name__ == "__main__":
  main()

良い機会なので、各デザインテーマの地の文のコントラストを表にまとめておく。文字と背景のコントラストは全てWCAGの推奨値の2倍以上で、文字の視認性に全く懸念はない。背景テクスチャのコントラストも1.1未満であり、文字の可読性に影響することは無い。コントラストが閾値を下回ると可読性が下がるが、コントラストが高ければ高いほど可読性が上がるわけじゃないことには留意されたい。それでもなおかなり高めのコントラストになるように色合いを設定しているのは、余裕のためだ。ディスプレイの輝度を下げるなどして実用上のコントラストを下げることは簡単だし、表示端末を外の明るい場所で使うと映り込みでコントラストが下がるので、低くて困ることはあっても高くて困ることはあまりない。ただし、閾値より高くしても可読性が上がるわけではない。

テーマ名 文字色 背景色 文字背景
コントラスト
背景テクスチャ
コントラスト
default, tategaki #000000 #ffffff 21.000 1.000
dark #eeeeee #000000 18.100 1.000
newspaper, shimbun #111111 #f8f6f4 17.516 1.090
whiteboard, hakuban #111111 #ffffff 18.883 1.043
blackboard, kokuban #f8f8ff #00221a 15.970 1.036

可読性とは文字を判別するのに必要な労力の少なさとも言い換えられるが、飽和が早く、十分に可読性が良い閲覧環境同士で読書体験に優劣をつけるのは難しい。文章の読解速度は文章の難易度と読者の読解力に依存する部分が大きい。十分に良い閲覧環境をさらに改善したとしても、読解速度は変わらない。ゆえに、読解速度を測定して閲覧環境の良し悪しや読書体験の良し悪しを測ろうとするのは無理筋だ。それでも、私はデザイン上で可読性をかなり大事にしている。私が用意したデザインテーマは全て十分に可読性が高いと自負している。その上で、さらに良い体験を追求して、縦書きをサポートし、フォントや文字色や背景色や背景テクスチャについて選択肢を提供している。好みの問題に優劣をつけることにあまり意味はなく、それよりは選択ができることが重要だ。

背景テクスチャが読書体験を向上させる可能性ついては、いずれ何らかの形で決着をつけたい。現状では仮説に過ぎないので、デフォルトのテーマには背景テクスチャを採用しないでいるが、本当に効果があるなら採用したい。おそらく速読性能には全く影響がないだろうから、集中力の持続時間の向上とか、眼精疲労やその他のストレス指標の測定とか、主観的な読書体験の指標化とかに頼ることになるだろう。STGYの運用が起動に乗った暁には、A/Bテストで離脱率を比較することが可能だろう。そこでもし優位な違いが出たなら、金と手間をかけて詳細な調査をしたいところだ。

まとめ

縦書きのWebページやブログは、有りよりの有り、だと思う。日本語で小説などの長文を発表するサイトとしては、縦書きモードをサポートすると、一定の効用があることを示した。CSSを工夫すれば縦書きの組版は十分に可能で、入稿のMarkdownを工夫すれば文学作品の構造表現も簡単にできる。

縦書きは他のサイトではまず見ない方式なので、初見の読者は戸惑うだろう。しかし、それでも敢えて導入したいという著者がいるなら、その気持ちに応えることには意味がある。青空文庫で読める文学作品の作者はもう存命でないので気持ちを確かめる術はないが、当時縦書きで発表された作品は、今後も縦書きで読める環境を提供することが、彼らに敬意を払うことになるのではないか。