Tags: CSS

Web小説を縦書きかつ縦スクロール操作で読みたい

これは 株式会社ピーアールオー(あったらいいな!を作ります) Advent Calendar 2019 3日目の記事です。昨日は koinori さんの「2019年俺的技術トレンドを振り返る 上期」でした。


はじめに

Web小説、読んでますか?

現在数多のWeb小説投稿サイトが存在し、多くのサイトにおいてその文章は横書きで表示されています。
しかしWeb上の文章の中でも小説ジャンルにおいては「縦書きで読みたい」という需要が根強く存在し、一部サイトでは「縦書きで読む」機能が以前から実装されています。
2019年12月現在、タテ書き小説ネット さんのようにPDF出力する、縦書き文庫 さんのように「縦書きかつページ送りで読み進めさせる」、Pixiv さんのように「縦書きで横スクロールさせる」などの実装例が存在します。(注:Pixivさんの横スクロールはスマートフォン版で確認しましたが、PC版の表示手法を確認できておりません。もしかしたらPC版は横スクロールではないかもしれません。)

もちろんどれも良い案なのですが

小説を縦書きで読みたいがPDF出力はスマートフォンでうまく動かないことがあるので嫌だ。
ページ送りは面倒なのでスクロールで読み進めたい。
横方向のスクロールは体が受け付けないので縦方向のスクロールがいい。

と思いませんか? 私は思います。

そのため今回は CSSで縦書き&段組みする という手法で私個人の「あったらいいな!」に挑戦していきたいと思います。

注意事項
本記事の縦書き縦スクロール実装は実用に足る完成度に至ることができませんでした。予めご了承ください。

実装

この後の実装では小説本文として青空文庫 より 夏目漱石「こころ」 を使用いたします。
また動作確認は Windows10 の Chrome で行いました。

STEP0 枠を作る

縦書き縦スクロールとは関係ないのですが、Web小説を読む画面を模した状態で実現しないと意味がないのでヘッダーとフッター程度の枠を最初に作っておきます。
なんでもいいと思うのですが今回はCSS Grid Layout (※1, 2) で作りました。

共通HTML

<!DOCTYPE html>

<head>
  <title>こころ</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
  <div class="grid-container">
    <div class="grid-header">ヘッダー</div>
    
    <div class="grid-main">
      <div class="contents">
        <p>
             <ruby><rb>私</rb><rp>(</rp><rt>わたくし</rt><rp>)</rp></ruby>はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を<ruby><rb>憚</rb><rp>(</rp><rt>はば</rt><rp>)</rp></ruby>かる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を<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>)</rp></ruby>である。その時私はまだ若々しい書生であった。暑中休暇を利用して海水浴に行った友達からぜひ来いという<ruby><rb>端書</rb><rp>(</rp><rt>はがき</rt><rp>)</rp></ruby>を受け取ったので、私は多少の金を<ruby><rb>工面</rb><rp>(</rp><rt>くめん</rt><rp>)</rp></ruby>して、出掛ける事にした。私は金の工面に<ruby><rb>二</rb><rp>(</rp><rt>に</rt><rp>)</rp></ruby>、<ruby><rb>三日</rb><rp>(</rp><rt>さんち</rt><rp>)</rp></ruby>を費やした。ところが私が鎌倉に着いて三日と<ruby><rb>経</rb><rp>(</rp><rt>た</rt><rp>)</rp></ruby>たないうちに、私を呼び寄せた友達は、急に国元から帰れという電報を受け取った。電報には母が病気だからと断ってあったけれども友達はそれを信じなかった。友達はかねてから国元にいる親たちに<ruby><rb>勧</rb><rp>(</rp><rt>すす</rt><rp>)</rp></ruby>まない結婚を<ruby><rb>強</rb><rp>(</rp><rt>し</rt><rp>)</rp></ruby>いられていた。彼は現代の習慣からいうと結婚するにはあまり年が若過ぎた。それに<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>)</rp></ruby><ruby><rb>日数</rb><rp>(</rp><rt>ひかず</rt><rp>)</rp></ruby>があるので鎌倉におってもよし、帰ってもよいという境遇にいた私は、当分元の宿に<ruby><rb>留</rb><rp>(</rp><rt>と</rt><rp>)</rp></ruby>まる覚悟をした。友達は中国のある資産家の<ruby><rb>息子</rb><rp>(</rp><rt>むすこ</rt><rp>)</rp></ruby>で金に不自由のない男であったけれども、学校が学校なのと年が年なので、生活の程度は私とそう変りもしなかった。したがって<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>)</rp></ruby>な方角にあった。<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>)</rp></ruby>ぶり返った<ruby><rb>藁葺</rb><rp>(</rp><rt>わらぶき</rt><rp>)</rp></ruby>の<ruby><rb>間</rb><rp>(</rp><rt>あいだ</rt><rp>)</rp></ruby>を通り抜けて<ruby><rb>磯</rb><rp>(</rp><rt>いそ</rt><rp>)</rp></ruby>へ下りると、この<ruby><rb>辺</rb><rp>(</rp><rt>へん</rt><rp>)</rp></ruby>にこれほどの都会人種が住んでいるかと思うほど、避暑に来た男や女で砂の上が動いていた。ある時は海の中が<ruby><rb>銭湯</rb><rp>(</rp><rt>せんとう</rt><rp>)</rp></ruby>のように黒い頭でごちゃごちゃしている事もあった。その中に知った人を一人ももたない私も、こういう<ruby><rb>賑</rb><rp>(</rp><rt>にぎ</rt><rp>)</rp></ruby>やかな景色の中に<ruby><rb>裹</rb><rp>(</rp><rt>つつ</rt><rp>)</rp></ruby>まれて、砂の上に<ruby><rb>寝</rb><rp>(</rp><rt>ね</rt><rp>)</rp></ruby>そべってみたり、<ruby><rb>膝頭</rb><rp>(</rp><rt>ひざがしら</rt><rp>)</rp></ruby>を波に打たしてそこいらを<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>)</rp></ruby>の<ruby><rb>間</rb><rp>(</rp><rt>あいだ</rt><rp>)</rp></ruby>に見付け出したのである。その時海岸には<ruby><rb>掛茶屋</rb><rp>(</rp><rt>かけぢゃや</rt><rp>)</rp></ruby>が二軒あった。私はふとした<ruby><rb>機会</rb><rp>(</rp><rt>はずみ</rt><rp>)</rp></ruby>からその一軒の方に行き<ruby><rb>慣</rb><rp>(</rp><rt>な</rt><rp>)</rp></ruby>れていた。<ruby><rb>長谷辺</rb><rp>(</rp><rt>はせへん</rt><rp>)</rp></ruby>に大きな別荘を構えている人と違って、<ruby><rb>各自</rb><rp>(</rp><rt>めいめい</rt><rp>)</rp></ruby>に専有の<ruby><rb>着換場</rb><rp>(</rp><rt>きがえば</rt><rp>)</rp></ruby>を<ruby><rb>拵</rb><rp>(</rp><rt>こしら</rt><rp>)</rp></ruby>えていないここいらの避暑客には、ぜひともこうした共同着換所といった<ruby><rb>風</rb><rp>(</rp><rt>ふう</rt><rp>)</rp></ruby>なものが必要なのであった。彼らはここで茶を飲み、ここで休息する<ruby><rb>外</rb><rp>(</rp><rt>ほか</rt><rp>)</rp></ruby>に、ここで海水着を洗濯させたり、ここで<ruby><rb>鹹</rb><rp>(</rp><rt>しお</rt><rp>)</rp></ruby>はゆい<ruby><rb>身体</rb><rp>(</rp><rt>からだ</rt><rp>)</rp></ruby>を清めたり、ここへ帽子や<ruby><rb>傘</rb><rp>(</rp><rt>かさ</rt><rp>)</rp></ruby>を預けたりするのである。海水着を持たない私にも持物を盗まれる恐れはあったので、私は海へはいるたびにその茶屋へ<ruby><rb>一切</rb><rp>(</rp><rt>いっさい</rt><rp>)</rp></ruby>を<ruby><rb>脱</rb><rp>(</rp><rt>ぬ</rt><rp>)</rp></ruby>ぎ<ruby><rb>棄</rb><rp>(</rp><rt>す</rt><rp>)</rp></ruby>てる事にしていた。
          </p>
        </div>
    </div>
    <div class="grid-footer">フッター</div>
  </div>
</body>

</html>

CSS

.grid-container {
  display:grid;
  grid-template-rows: 50px 1fr 50px;
  grid-template-columns: 50px 1fr 50px;
}

.grid-header {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
  background-color: gray;
}

.grid-main {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
}

.grid-footer {
  grid-row: 3 / 4;
  grid-column: 2 / 3;
  background-color: gray;
}

仕上がり

STEP0の仕上がり図

特に問題ないです。

STEP1 縦書きする

ではまず縦書きします。

writing-mode: vertical-rl;

という指定を与えることで縦書きになります。

CSS

.grid-container {
  display:grid;
  grid-template-rows: 50px 1fr 50px;
  grid-template-columns: 50px 1fr 50px;
}

.grid-header {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
  background-color: gray;
}

.grid-main {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
}

.grid-footer {
  grid-row: 3 / 4;
  grid-column: 2 / 3;
  background-color: gray;
}

.contents {
  writing-mode: vertical-rl;
}

仕上がり

STEP1の仕上がり図1

まず左に寄っています。
また横書きのときとは違って文章の幅が固定になっており、ブラウザ幅を変えると文章が見えなくなってしまいます。

STEP1の仕上がり図2

この辺りは結構複雑な問題なので後で取り組みます。

STEP2 段組みする

次に縦書きのまま段組みします。
特殊な指定は必要なく、横書きのときの段組みと同じです。(※3)
今回はこのような指定にします。

.contents {
  column-width: 15rem;
  column-rule: solid 1px #ccc;
  column-gap: 2.5rem;
}

CSS

.grid-container {
  display:grid;
  grid-template-rows: 50px 1fr 50px;
  grid-template-columns: 50px 1fr 50px;
}

.grid-header {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
  background-color: gray;
}

.grid-main {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
}

.grid-footer {
  grid-row: 3 / 4;
  grid-column: 2 / 3;
  background-color: gray;
}

.contents {
  writing-mode: vertical-rl;
  column-width: 15rem;
  column-rule: solid 1px #ccc;
  column-gap: 2.5rem;
}

仕上がり

STEP2の仕上がり図

相変わらず左に寄っており幅も固定ですが、段組み自体は問題ないです。

STEP3 レスポンシブ対応の問題

ブラウザ幅を変えても文章が途切れないよう width: 100%; 指定をつけてみましょう。

CSS

.grid-container {
  display:grid;
  grid-template-rows: 50px 1fr 50px;
  grid-template-columns: 50px 1fr 50px;
}

.grid-header {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
  background-color: gray;
}

.grid-main {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
}

.grid-footer {
  grid-row: 3 / 4;
  grid-column: 2 / 3;
  background-color: gray;
}

.contents {
  writing-mode: vertical-rl;
  column-width: 15rem;
  column-rule: solid 1px #ccc;
  column-gap: 2.5rem;
  width: 100%;
}

仕上がり

STEP3の仕上がり図1

なぜか文章が右に寄りました。
でも幅は変わるようになりました……ちょっと幅を狭くして様子を……

STEP3の仕上がり図2

!!!!?????
フッターの位置がおかしいですね!!!!?????

CSS素人のため原因はよくわからなかったのですが、どうやら幅と高さが可変の状態で縦書き段組みすると段組み部分のdivの高さ計算が期待通りにならないようです。

また致命的なことにブラウザ幅を変えたときルビの表示がおかしくなります。

STEP3の仕上がり図3

この画像1段目~2段目にかけてのように、ルビが縦半分に切れて次の行に続いたりします。

更に文章が右に寄る件を解決するためにtext-alignを指定する方法 (※4) を実装してみたのですが、幅と高さ可変の状態ではそれもうまくいかず、全く表示が変わりませんでした。

STEP4 レスポンシブ対応(暫定措置)

調べてみたものの、どうしても幅と高さ可変のままでは解決できませんでした。そのため暫定措置として

  • ブラウザ幅が適当な幅より大きくなったらコンテンツの幅を固定する
  • 文章の高さを固定しはみ出した部分をスクロールする
  • 中央寄せのCSS (※4) も適用する

という方針で実装を修正しました。

CSS

@media screen and (max-width: 850px) {
  .grid-container {
    display:grid;
    grid-template-rows: 50px calc(100vh - 100px) 50px;
    grid-template-columns: 50px calc(100vw - 100px) 50px;
  }
}

@media screen and (min-width: 850px){
  .grid-container {
    display:grid;
    grid-template-rows: 50px calc(100vh - 100px) 50px;
    grid-template-columns: 1fr 800px 1fr;
  }
}

.grid-header {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
  background-color: gray;
}

.grid-main {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
  text-align: center;
}

.grid-footer {
  grid-row: 3 / 4;
  grid-column: 2 / 3;
  background-color: gray;
}

.contents {
  writing-mode: vertical-rl;
  column-width: 15rem;
  column-rule: solid 1px #ccc;
  column-gap: 2.5rem;
  width: 100%;
  overflow-y: scroll;
  display: inline-block;
  text-align: left;
}

仕上がり

STEP4の仕上がり図

フッターが本文と被る問題と右寄りになる問題は解決しましたが依然として問題は残っています。
ですが私のCSS力ではこの辺りが限界でした。

まとめ

幅や高さを固定できて文章の長さも決まっている場合は縦書き段組みCSSは簡単だと思います。
しかし「ヘッダーやフッターがあって、幅も高さも可変でレスポンシブ対応したい」「文章の長さが可変で、不特定の箇所にルビがついている」など、Web小説を読むことを想定するとちょっと難しいことがわかりました。
本記事の実装で現在わかっている課題は下記のとおりです。

今回STEP4でやったようにコンテンツ幅を固定する方法では可変長さの小説に対応しきれない

そもそも段組みCSSには「なるべく各段の長さが均等になるように割り付ける」という性質があり、文章が短くなれば文章の幅も短くなります。
したがってコンテンツ幅を固定値にしても短い小説は右に寄ってしまいます。

残りの問題の図

実用上は「ある程度の長さの文章までは段組みしないで表示する」などの制御が必要かもしれません。

ルビの表示がおかしい

どうすれば解決できるのかまだ検討がついていません。
できればブラウザ側で、ルビとルビが振ってある行を一体として扱っていただきたいところです……。
やれるとすれば、そもそもルビがある行と無い行で行間が異なるのが微妙なので調整して行間を同じにし、幅を計算するなどでしょうか。
しかし読書UIですから当然ブラウザ拡大率(または文字サイズ)などをユーザーに変えられることを想定する必要があり、うまくいかない気がします……。

ブラウザによる挙動の違い

STEP4で作ったものをEgdeで見たら盛大に崩れていました。 対象ブラウザ以外でもそれなりに見せるためには工夫が必要そうです。

以上です。長々とお読みいただきありがとうございました!
明日は s_arakawa626 さんが何か書くそうです。


※参考文献

  1. CSS Grid Layout を極める!(基礎編) - Qiita
  2. CSS Grid Layout を極める!(場面別編) - Qiita
  3. 段組みを使って縦書きの文章をレスポンシブ対応させる方法 | Webクリエイターボックス
  4. 縦書きレイアウトとCSSテクニック|縦書きWeb普及委員会