Vivliostyleが拓くCSS組版の可能性

CSSフレームワークVivliostyle Themeで簡単にページデザインを編集する

CSSにまだ詳しくなかったり、ページデザインよりも本文の執筆に注力したい人のために、Vivliostyleでは簡単にデザインを適用できるVivliostyle Themeが用意されています。第3回では、Vivliostyle Themeの使い方や、Vivliostyle Themeを実現するためのCSSの機能、自分でVivliostyle Themeを作って公開する方法について紹介します。

Vivliostyle Themeについて

Vivliostyle Themeは、Vivliostyleワークフロー上で簡単にページデザインを適用するための仕組みです。CSS組版を実現するVivliostyleは、ページデザインをCSSで実現できることが最大のメリットですが、逆説的に言うとCSSを知らないとデザインの変更ができないとも言えます。CSS自体は広く普及しており、CSSを学ぶ環境も十分に整えられてはいるものの、Vivliostyleを扱う人すべてがCSSに詳しいとは限りません。そのため、Vivliostyle Themeではいくつかの主要なユースケースに沿ったデザインのプリセットを「テーマ」として提供しています。

Vivliostyle公式では、文庫本用のテーマBunko@vivliostyle/theme-bunkoや技術ドキュメントに適したTechbook@vivliostyle/theme-techbookなどいくつかのテーマが用意されています。これらのテーマファイル自体もCSSでできており、実態としてはCSSのプリセット集といえます。また、これらのCSSファイルはすべてブラウザ標準の機能を使って実現していることも注目すべき点です。Vivilostyleという名前は冠していますが、実際にはVivliostyle ThemeはVivliostyleの機能と密接に結合しているわけではなく、そのままブラウザのスタイルシートとして読み込ませることも、他のCSS組版エンジンに読み込ませることすらも可能です。

Vivliostyle Themeの使い方

それではVivliostyle Themeを実際に使っていきます。第1回で用意したVivliostyleサンプル『植物一日一題』⁠⁠牧野富太郎) に対してテーマを適用してみます。

Vivliostyle Themeを使用する前に、すでに本文に適用されているスタイルシートを解除しておきます。複数のテーマやスタイルシートを適用させることも可能ですが、スタイルが競合することを避けるため、今回はVivliostyle Themeのみを使うようにします。以下のように、本文ファイルのフロントマターからlink:を削除します。

---
title: 植物一日一題
author: 牧野富太郎
lang: ja
---

この状態でプレビュー画面を表示すると、以下のスクリーンショットのように、ブラウザデフォルトのスタイルシートのみが適用された状態になるはずです。

図1 ブラウザデフォルトのスタイルシートのみが適用された状態のプレビュー画面。
図1

この状態から、Vivliostyle Themeを使用します。Vivliostyle CLIでは、--theme-Tオプションでテーマを指定できます。文庫本用のテーマ「Bunko」を適用する場合、以下のように指定します。

$ vivliostyle preview shokubutsu_ichinichi.md --theme @vivliostyle/theme-bunko

すると、以下のようにテーマが適用された状態でプレビューされます。

図2 文庫本用のテーマが適用された状態のプレビュー画面。文庫本のように文章が縦書きで表示されている。
図2

この例ではテーマを@vivliostyle/theme-bunkoのように名前で指定しましたが、ローカル環境やWeb上のテーマを直接指定することもできます。また、前述した通りVivliostyle ThemeはCSSファイルをテーマとして扱うため、CSSファイル自体を直接指定することもできます。

$ vivliostyle preview shokubutsu_ichinichi.md --theme ./css/styles.css
$ vivliostyle preview shokubutsu_ichinichi.md --theme https://unpkg.com/markdown-splendor/css/splendor.css

このように、Vivliostyle Themeは全くCSSを書くことなくページのスタイルを変えることができます。もちろんこのままでも十分にVivliostyle Themeを活用できていますが、以降の章で紹介する簡単なCSSを用意することで、スタイルの内容をカスタマイズすることもできます。

テーマのカスタマイズ

公式が提供するVivliostyle Themeの大部分では、CSS変数を使ったテーマのカスタマイズをサポートしています。例えば、文章全体のフォントを変更したい場合は、--vs-font-familyというCSS変数が使用できます。テーマの指定オプションに加えて、以下のように--cssオプションで直接CSSを与えてみてください。

$ vivliostyle preview shokubutsu_ichinichi.md \
    --theme @vivliostyle/theme-bunko \
    --css ':root {--vs-font-family: YuGothic;}'

これで、文章全体のフォントが游ゴシック体に変化しているはずです。--vs-font-family以外にも、Vivliostyle Themeでは様々な値がカスタマイズ可能になっており、これらのCSS変数は--vsで始まる名前で用意されています。例として、以下のようなCSS変数が設定可能です。

--vs-font-size
文章全体で基準となるフォントの大きさ(CSSのfont-sizeプロパティに相当)
--vs-line-height
文章全体で基準となる行の高さ。通常のline-heightプロパティとは異なり、この値には単位のない<number>を指定する必要があることに注意してください
--vs-columns
段組の数や幅の大きさ(CSSのcolumnsプロパティに相当)
--vs-hanging-punctuation
句読点のぶら下げに関する指定(CSSのhanging-punctuationプロパティに相当)
--vs-text-spacing
約物の詰めや和欧文間のスペースに関する指定 (CSSのtext-spacingプロパティに相当)
--vs-writing-mode
縦書き・横書きの指定(CSSのwriting-modeプロパティに相当)
--vs-widows-orphans
一つの段落が複数のページ・段組にまたがるときに確保する最小の行数(CSSのwidowsプロパティorphansプロパティに相当)
--vs-spacing-rlh
pultableなどのブロック要素が確保するマージンの大きさ。<length>の値を指定します

これらのカスタマイズ可能な設定は、Baseテーマを介して定義されています。Baseテーマについては後ほど紹介します。

他にも、テーマによってはそのテーマ特有のカスタマイズ可能なオプションが用意されていることがあります。BunkoテーマのREADMEには、--vs-theme--num-of-line--vs-theme--num-of-characterなどの利用可能なCSS変数が紹介されており、このCSS変数の内容をカスタマイズすることで1ページあたりの行数と一行の文字数が設定できることがわかります。コマンドで指定するにはちょっと長いので、別のCSSファイルmy-style.cssを作成して、そこに上書きするCSS変数を書いていきます。

my-style.css
:root {
  --vs-font-family: YuGothic;
  --vs-page--size: JIS-B6;
  --vs-theme--num-of-line: 14;
  --vs-theme--num-of-character: 40;
}

以下のように作成したCSSファイルを--styleオプションで指定してください。

$ vivliostyle preview shokubutsu_ichinichi.md \
    --theme @vivliostyle/theme-bunko \
    --style ./my-style.css

このCSS変数の指定により、フォントが游ゴシック体に、ページの大きさがJIS-B6に、1ページあたりの行数が14、一行の文字数が40になります。

図3 my-style.cssが適用された状態のプレビュー画面。CSSで指定したとおりにフォントやページの大きさなどが変更されている。
図3

このように、Vivliostyle CLIの--cssオプションや--styleオプションは、既存のVivliostyle Themeに簡単なカスタマイズを加えるのに便利です。もし、より複雑なカスタマイズをしたり、カスタマイズしたスタイル自身をテーマとして再利用したい場合は、後述する手順で新しくテーマを作ることをおすすめします。

NoteVivliostyle Themeを指定するとthemesという名前のディレクトリが作られていることにお気づきかもしれません。このファイルはVivliostyle CLIが自動でインストールしたテーマファイルを保存するディレクトリです。このディレクトリの中のファイルはVivliostyle CLIによって書き換えられる可能性があるため、このディレクトリの中のCSSファイルは直接編集せず、別の場所にCSSファイルを作成して編集するようにしてください。

柔軟なデザインを支えるBaseテーマ

Vivliostyle Themeを知るためには、実際にテーマがどのように作られているかを見るのが一番です。例えば、BunkoテーマのCSSファイルは以下のようになっています。

@import url(../theme-base/theme-all.css);

/**
 * Theme variables
 */
:root {
  --vs-theme--num-of-line: 15;
  --vs-theme--num-of-character: 39;
  --vs-theme--page-top-left-content: counter(page) ' ' env(doc-title);
  --vs-theme--page-top-right-content: counter(page);
  --vs-theme--subsection-text-indent: 3rem;
  --vs-theme--anchor-color-body: darkblue;
}

:root {
  --vs-border-width: 0.5px;
  --vs-font-family: '游明朝', 'YuMincho', serif;
  --vs-font-size-on-print: 83.33%;
  --vs-line-height: 2;
  --vs-spacing-rlh: 0;
  --vs-writing-mode: vertical-rl;
  .....
}
.....

上記のコードは実際のCSSの抜粋ではありますが、それでもBunkoテーマのためのCSSは全体で187行 (執筆時点) に収まっています。実際にBunkoテーマが実現していることに対して、この分量はとても少なく感じませんか?

この理由を知るためには、もう少しCSSについて踏み込んで見ていく必要があります。CSSの1行目に@import url(../theme-base/theme-all.css);という箇所がありますが、これは Baseテーマ@vivliostyle/theme-base という別のテーマを読み込ませるための宣言になっています。Baseテーマは、ページレイアウトに関する様々な設定について、CSS変数を介してカスタマイズできるように用意されたVivliostyle Themeで、Bunkoテーマをはじめとした大部分のVivliostyle ThemeはこのBaseテーマを内部で読み込んでいます。

Baseテーマ自体のCSSは、meta-properties.cssbasic.cssなど、いくつかのCSSファイルに分かれて用意されています。それぞれのCSSファイルの役割については、BaseテーマのREADMEを参照してください。

CSS変数を介して設定する意義

Baseテーマが提供するCSS変数は、部分的に独自のカスケーディングのルールにしたがって定義されています。例えば、Baseテーマでは見出し要素 (h1〜h6) に対して heading というグループがあります。このグループに対して、以下のように一括でスタイルを適用したり、その中の特定の要素だけに別のスタイルを適用する、といったことができるようになっています。

:root {
  /* 見出しh1〜h5にフォントAを適用 */
  --vs--heading-font-family: A;
  /* h6にだけフォントBを適用 */
  --vs--h6-font-family: B;
}

ただ、CSSに詳しい人であれば、わざわざこのようなルールを作らなくても、以下のように:root以外のセレクターを用意して直接フォントを指定すれば良いと思うかもしれません。このようにしなかった理由はなぜでしょうか?

h1, h2, h3, h4, h5 {
  --vs-font-family: A;
}
h6 {
  --vs-font-family: B;
}

CSSでは、複数のスタイルが重複して設定された場合、セレクターの詳細度に応じて優先度を決めるというルールがあります。この特徴は、正しく管理されたCSSの範囲では柔軟に制御できる機能である一方、外部のCSSがそのスタイルを再利用するためには、元のCSSのスタイルを上書きするためにより高い詳細度を設定する必要があります。この制約は、再利用元のCSSの内容に依存することになり、結果として詳細度を上げるための無意味なセレクターや!importantの使用といったCSSのバッドプラクティスに繋がります。特に、Vivliostyle Themeのように一つのCSSファイルが数多くのテーマから参照される場合、注意深くセレクターを設定しなければいけません。実際に、CSS変数を使用していなかった以前のVivliostyle Themeでは、この問題が原因でBaseテーマのような再利用可能なCSSを作ることができませんでした。

Baseテーマは、このような詳細度に関する問題を避けつつも、独自のカスケーディングのルールを用意することで、文章を主体としたページのレイアウトを簡単にする取り組みです。もちろん、CSS変数の名前だけでセレクターを代替しようとするこの方法は、現実世界のWebサイトで求められるような複雑なセレクターの表現は難しいでしょう。しかし、Vivliostyleが対象とするような構造化されたコンテンツに対しては、CSSの再利用をより柔軟にする方法だと思いませんか?

CSSカウンターの使用

Baseテーマの導入でVivliostyle Themeがより使いやすくなった一例として、CSSカウンター の例について紹介します。CSSカウンターとは、ページ内の要素に対して決まったルールでカウントし、その要素の出現回数に応じて番号を増加・減少させることができるCSSの機能です。Vivliostyleでは、ページ番号をはじめとした多くの場面でCSSカウンターを利用できます。

CSSカウンターを使うためには、通常以下のようなCSSを用意します。

body {
  /* my-counterのカウンター値をリセット */
  counter-reset: my-counter;
}
h1::before {
  /* h1要素が出現するたびにカウンターの値を1つ増加させる */
  counter-increment: my-counter;
  /* h1要素のテキストにカウンターの値に応じて「第n章」というテキストを追加する */
  content: '第' counter(my-counter) '章';
}

ただ、このようにCSSカウンターを直接使用する方法には若干問題があります。単純にcounter-resetcounter-incrementを設定することが面倒という点もありますが、それよりも問題なのはcounter-resetcounter-incrementが他のCSSで上書きできてしまうという点です。例えば、上記のCSSとは別にbody { counter-reset: another-counter; }のようなCSSを後から読み込ませた場合、元のmy-counterのカウンターリセットは無効化されてしまいます。この仕様は、Vivliostyle Themeの再利用性を実現する上で問題です。

Baseテーマを利用するVivliostyle Themeでは、この問題を回避するための手段を提供しています。まず、見出し番号や図表の番号といったよく用いられるカウンターについては、標準で利用可能な状態にしています。試しに、以下のようなCSS変数を設定してみてください。

:root {
  --vs-section--marker-display: inline;
}

すると見出しの先頭に見出し番号が表示されます。--vs-section--marker-displayは見出し番号の表示に関する設定で、デフォルトのnoneからinlineに変更することで非表示の状態だった見出し番号を表示させるように指定しています。見出し番号を始めとしたカウンターがどのように利用できるかを紹介します。

見出し番号のカウンター

見出し、小見出しといった章に関するカウンターは section.css で管理されています。

カウンター名 説明
vs-counter-sections 見出しを持つセクションのカウンター
vs-counter-sec-h1vs-counter-sec-h6 h1h6を持つセクションのカウンター

このとき、見出し番号のカウンターはh1h2といった見出しに対してインクリメントされるのではなく、見出しを持つセクションに対してインクリメントされることに注意が必要です。ここで言う「見出しを持つセクション」とは、⁠最初の子要素にh1h6を持つsection要素」のことで、HTMLで表すと以下のような構造になります。

<section>
  <h1>1 大見出し</h1>
  <section>
    <h2>1.1 見出し</h2>
    <p>.....</p>
  </section>
  <section>
    <h2>1.2 見出し</h2>
    <p>.....</p>
    <section>
      <h3>1.2.1 小見出し</h3>
      <p>.....</p>
    </section>
  </section>
</section>

なお、第2回で紹介した通り、VFMが出力するHTMLは自動的に見出しごとにセクション分けされます。また、この仕様を利用して意図的に見出し番号を表示させないこともできます。VFMでは、行頭に#をつけて設定した見出しに対して同じ数の#を行末につけることでセクションを作らずに見出しを作成することができる ので、これにより見出し番号のカウンターをインクリメントせずに見出しを作ることができます。

また、デフォルトではすべての見出しに対して階層的な見出し番号を表示していますが、この動作を変えることもできます。以下に見出し番号を制御する例を紹介します。

h1⁠h2要素だけ見出し番号を表示

:root {
  --vs-section--h1-marker-display: inline;
  --vs-section--h2-marker-display: inline;
}

h1の見出し番号のスタイルを変更

:root {
  --vs-section--marker-display: inline;
  --vs-section--h1-marker-content: counter(vs-counter-sections, upper-roman);
}

セクション単位でリセットされていない通算の番号を表示

:root {
  --vs-section--marker-display: inline;
  --vs-section--h2-marker-content: counter(vs-counter-sec-h1) '.' counter(vs-counter-sec-h2);
  --vs-section--h3-marker-content: counter(vs-counter-sec-h1) '.' counter(vs-counter-sec-h2) '.' counter(vs-counter-sec-h3);
}

図表カウンター

図表や引用に関するカウンターは crossref.css で管理されています。

カウンター名 説明
vs-counter-fig 図のカウンター
vs-counter-tbl 表のカウンター
vs-counter-cite 引用のカウンター

それぞれのカウンターは、以下の要素に対してインクリメントされます。

vs-counter-fig
内部にimgpicture要素を持つfigure要素、もしくはfigクラスが設定されたfigure要素
vs-counter-tbl
内部にtable要素を持つfigure要素、もしくはtblクラスが設定されたfigure要素
vs-counter-cite
cite-itemsクラスが設定されたol要素の子要素であるli要素

それぞれのカウンターの番号を表示するには、以下のCSS変数を設定します。図と表のカウンター番号を表示するためには、figure要素の中にfigcaption要素、もしくはtable要素の中にcaption要素を用意する必要があります。

:root {
  --vs-crossref--marker-display: inline;
}

文中に図表を参照する番号を挿入するには、以下のように空のa要素を追加します。hrefには参照したい図表のid属性、data-refにはfigtblなどのカウンターの種類を指定します。

<a href="#fig-1" data-ref="fig"></a>

ページ⁠ドキュメント⁠チャプターカウンター

Vivliostyleでは、ページ番号を表すための特別なカウンターpageが最初から使えるようになっていますが、Vivliostyle Themeではこれ以外にもドキュメント単位やチャプター単位のカウンターが用意されています。

Vivliostyle CLIでは、入力として単一のMarkdownファイルを指定する方法以外にも、vivliostyle.config.jsファイルを使って複数のMarkdown/HTMLファイルを一つにまとめる方法があります。ドキュメント単位のカウンターでは各Markdown/HTMLファイルを「ドキュメント」として、ドキュメントの出現ごとにインクリメントされたカウンターvs-counter-docが利用できます。また、特別なページであるchapterpartを用意することで、⁠部」「章」のようなより大きな単位のカウンターが使えます。

カウンター名 説明
vs-counter-doc ドキュメントのカウンター
vs-counter-chapter chapterページのカウンター
vs-counter-part partページのカウンター

chapterおよびpartは、html要素かbody要素にchapterpartというクラス名を設定するか、role属性にdoc-chapterdoc-partを設定することでそのページがchapter/partページになります。VFMを使っている場合、フロントマターに以下のようにclassを設定することで簡単にchapter/partページを設定できます。

---
class: chapter
---

これらのカウンターを使って、以下のようにページマージンなどのcontentに追加することができます。

:root {
  /* ページ上部に節番号、章番号、ドキュメントのタイトルを表示 */
  --vs-page--mbox-content-top-center: counter(vs-counter-chapter) '.' counter(vs-counter-doc) ' ' env(doc-title) ;
  /* ページ下部にページ番号を表示 */
  --vs-page--mbox-content-bottom-center: counter(page);
}

自分でカウンターを追加する

前に述べた通り、直接counter-resetを設定すると他のcounter-resetの設定を上書きしてしまうおそれがありました。そのため、Vivliostyle Themeでは自分でカウンターを追加するためのCSS変数も用意しています。

:root {
  /* 各ドキュメントのrootでリセットされるカウンターを設定 */
  --vs-document-root-counter-reset: my-document-counter;
  /* 最初のページでリセットされるカウンターを設定 */
  --vs-first-page-counter-reset: my-chapter-counter;
}

--vs-document-root-counter-reset--vs-first-page-counter-resetで指定されたカウンターは、それぞれ:root@page :firstが出現する箇所でリセットされるようになっています。このとき、他のカウンターリセットを上書きすることはないため、この変数を介して指定することで、安全にカウンターをリセットすることができます。

テーマを作成⁠公開する

CSS変数を使ったVivliostyle Themeのカスタマイズに慣れてきたら、カスタマイズした内容を実際に新しいテーマとして公開することを検討してみてください。以下のコマンドを実行することで、ローカル環境にテーマのテンプレートを作成することができます。

$ npm create vivliostyle-theme <作成するテーマの名前>

作成されたテンプレートには、以下のようなファイルが用意されています。

vivliostyle-theme-my-book
├── LICENSE
├── README.md
├── example
├── package-lock.json
├── package.json
├── theme.css
└── vivliostyle.config.js

ファイル構成からも分かる通り、Vivliostyle Themeのテーマファイルはnpmのパッケージとして配布することができます。package.jsonに対して、以下のようにvivliostyleプロパティを追加することで、そのパッケージがVivliostyle Themeであることを示しています。これらの設定の詳細は、公式のドキュメント も参考にしてください。

{
  "vivliostyle": {
    "theme": {
      "name": "My book theme",
      "author": "spring-raining",
      "style": "./theme.css",
      "category": "novel",
      "topics": []
    }
  }
}

テーマの作成が完了したら、通常のnpm package公開の手順に従ってPublishをしてください。パッケージがnpmレジストリに登録されると、公開したテーマの名前で誰でも利用することができるようになります。また、package.jsonのkeywordsにvivliostyle-themeという名前のキーワードを追加しておくことで、そのテーマを利用可能なVivliostyle Themeとしてリストアップさせることができます。例えばCreate Bookでは、リストアップされたテーマの中から選ぶことで、そのテーマがあらかじめ設定されたVivliostyleプロジェクトのひな形を簡単に作ることができます。

まとめ

Vivliostyle Themeについて、そのままテーマを適用する方法をはじめ、テーマをカスタマイズしたり、カスタマイズした内容を別のテーマとして公開する方法について紹介しました。これまでVivliostyleに興味を持つものの、CSSによるページデザインについて敷居が高いと感じていた人にとって、Vivliostyle Themeはそのハードルを下げる存在になると思います。

さらに、CSS変数を活用したBaseテーマの導入により、Vivliostyle Themeを使う人だけでなく作る人にとっても開発環境が改善されました。共通のCSS変数を参照することで、テーマの利用者が後からカスタマイズしたり、カスタマイズしたテーマを新たな派生テーマとして公開するといったように、Baseテーマを起点としたエコシステムが構築されるようになりました。Vivliostyle Themeを作成する上でBaseテーマを利用することは必須ではありませんが、このようなメリットを考えると、個人的にはぜひBaseテーマのCSS変数を活用することをおすすめしたいと思います。

おすすめ記事

記事・ニュース一覧