[CSS]Vivliostyle-themeのCSS変数と`@property`ルールの相性を考える
CSS組版アドベントカレンダー2024の23日目の記事です。
vivliostyle/themes
VivliostyleはCSS組版を、WebブラウザのレンダリングをベースにJavaScript(TypeScript)処理で達成するプロダクトです。初学者には先ず2つのハードルがあり、1つがHTMLのマークアップ、もう1つがCSSの記述です。Vivliostyleのコミュニティはこのハードルに対し、前者にVivliostyle Flavored Markdown(VFM)簡易記法とその処理系、後者にVivliostyle themesという定義済みCSSセットのライブラリ提供というアプローチをしています。
扨、Vivliostyle themeはユーザによる簡易的な設定変更の手段としてCSS変数による調整方法を提供しています。微調整;本文フォントの変更や全体のページマージンの変更といった調整は対応するCSS変数を変更するだけで完了するわけです。
:root {/*変数の宣言*/
...
--vs--html-font-size: var(--vs-font-size);/*変数の値を別の変数の値で定義*/
...
}
html {
font-size: var(--vs--html-font-size);/*プロパティに対し変数の値をvar()で適用*/
}
上のCSSによって、theme利用者が、--vs-font-size
の値を追加で指定すると(位置や優先度を正しい位置で指定する必要があるが、多分configで指定すればこの場合はいい感じになる)、その内容がhtml
セレクタのfont-size
プロパティに適用されます。
一次的な値と二次的な値
先程もfont-size
に指定する--vs--html-font-size
も宣言箇所で別の変数を使って書かれていたように、変数の内容に変数を使えます。上のケースではあまり嬉しさは分かりませんが。
@page {
@top-left-corner {
...
margin-top: calc(var(--vs-page--bleed) * -1);
}
}
calc()
を使って、CSS変数の値を計算した結果がmargin-top
に適用されます。CSS変数に直接指定した値と、その変数の値から二次的な値が登場することになります。
line-height
とかを安易に変数にするのは厄介
自分やチームだけが使用するなら「これはここで使う変数なので~」となあなあで済むのですが、不特定の他者が使うとなればそうもいかないことがあります。
長さ系のプロパティは多くの場合絶対値も相対値も指定可能です。そして、プロパティによっては相対値にしても「単位付き」「単位無し」の両方が指定可能なのです。line-height
とか。
これが厄介なのは、こうしたプロパティに指定する値を変数化して二次的な値を作るのに使うときです。
/* --line-height=12ptのとき 12pt*1.5 = 18pt */
--line-height: 12pt
--val: calc(var(--line-height) * 1.5);
/* --line-height=1.5のとき 1.5*1.5 = 2.25 */
--line-height: 12pt
--val: calc(var(--line-height) * 1.5);
評価されたline-heightの実際の値であれば1.5 * (font-size)でちゃんとdimensionが出てくるのですが、CSS変数の状態だと解決されないままです。評価できない値を抱えた処理系はフォールバックに走り、露出したCSS変数だけを見ている状態の初学者は「よくわからないけどこわれた」なんて思ってしまうかもしれません。
@property
ルール
CSS変数の世界に型、基、シンタックスの制限とフォールバックをもたらすのが@property
ルールです。モダンブラウザは対応していますが、ブラウザを使わないCSS組版処理系では現在ほぼ対応はなさそう。
/*lengthに限定*/
@property --line-height-length {
syntax: "<length>";
inherits: false;
initial-value: 18pt;
}
/*numberに限定*/
@property --line-height-num {
syntax: "<number>";
inherits: false;
initial-value: 1.5;
}
つまり、二次的な値に使う変数については@property
ルールを使った宣言をしておくことで変数の中身のミスマッチでcalc()
不可能な状態を防ぐことが可能になったりするといいな。
Discussion