VFMは、VivliostyleでCSS組版するHTMLを出力するためのMarkdownです。HTMLを直接書くことで何でもできるのがMarkdownの利点ではあるのですが、商業出版物の規模になるとHTMLを書くことによる複雑さが許容できなくなってきます。できるだけMarkdown側の記法でCSS組版に適したHTMLを出力できると助かります。VFM v2.5ではVivliostyleとぜひ組み合わせたい機能を追加しています。
VFMでは、記法(ブロックレベル)はfigure+img+figcaptionに変換されます。以前はこの変換のimgとfigcaptionの順番は固定でしたが、v2.5ではオプションimgFigcaptionOrder: 'img-figcaption' | 'figcaption-img'によってこの順番を制御することができます。キャプションが先の本文フォーマットをCSSに起こすのが一手簡単になる機能です1。
$ echo '' | npx --yes @vivliostyle/vfm@2.5 --partial
<figure>
<img src="./img.png" alt="caption">
<figcaption aria-hidden="true">caption</figcaption>
</figure>
$ echo '' | npx --yes @vivliostyle/vfm@2.5 --partial --img-figcaption-order figcaption-img
<figure>
<figcaption aria-hidden="true">caption</figcaption>
<img src="./img.png" alt="caption">
</figure>
VFMのコードブロックはMarkdownで一般的に用いられている``` ... ```または~~~ ... ~~~です。QiitaやZenn風のlang:title記法と、VuePress風のlang title=" ... "2記法の両方に対応しており、どちらもfigcaptionを生成します。VFM v2.5ではさらに、見出しや画像で以前から使用できた{ ... }記法でcode要素にHTML属性を加えることができます。
$ npx --yes @vivliostyle/vfm@2.5 --partial <<'EOF'
```plaintext:caption
Hello
```
EOF
<figure class="language-plaintext">
<figcaption>caption</figcaption>
<pre class="language-plaintext"><code class="language-plaintext">Hello</code></pre>
</figure>
$ npx --yes @vivliostyle/vfm@2.5 --partial <<'EOF'
```plaintext:caption {#id .class data-other="your attr"}
Hello
```
EOF
<figure class="language-plaintext">
<figcaption>caption</figcaption>
<pre class="language-plaintext"><code id="id" class="language-plaintext class" data-other="your attr">Hello</code></pre>
</figure>
1つめの新機能でfigcaptionを制御しやすくなり、2つめの機能でコードブロックにもHTML属性、特にid属性を振りやすくなりました。CSS組版におけるid属性の重要な用途は相互参照の生成です。CSS関数target-text()を駆使することで、CSSカウンターで生成した図版番号を本文中で使用することができます。
しかしここまでのところ、{ ... }記法によるid属性はimgやcodeといったコンテンツそのものに付きます。これは自然ではあるものの、figcaptionの::before疑似要素で作る「図x.x」「リストx.x」を取り出すには不利です。そこでVFM v2.5にはid属性をfigcaption側につけるためのオプションassignIdToFigcaption: booleanを追加しています。
$ echo '{#img0}' | npx --yes @vivliostyle/vfm@2.5 --partial --img-figcaption-order figcaption-img --assign-id-to-figcaption
<figure>
<figcaption aria-hidden="true" id="img0">caption</figcaption>
<img src="./img.png" alt="caption">
</figure>
$ npx --yes @vivliostyle/vfm@2.5 --partial --assign-id-to-figcaption <<'EOF'
```plaintext:caption {#list0}
Hello
```
EOF
<figure class="language-plaintext">
<figcaption id="list0">caption</figcaption>
<pre class="language-plaintext"><code class="language-plaintext">Hello</code></pre>
</figure>
VFM v2.5はすでにVivliostyle CLIにも取り込まれており、新オプションはvivliostyle.config.jsのvfmプロパティに指定することで利用できます。
// @ts-check
import { defineConfig } from '@vivliostyle/cli';
export default defineConfig({
title: "VFM v2.5 New Features",
entry: ["manuscript.md"],
vfm: {
imgFigcaptionOrder: "figcaption-img",
assignIdToFigcaption: true
}
});
figcaptionをimgの前に置き、画像やソースコードの番号を本文中から拾いたい場合、このように書くことができます。VFM v2.5の機能なしで同じことを記述した例と比べて簡単に書けるようになりました。
---
lang: ja
link:
- rel: stylesheet
href: style.css
---
<a href="#img"></a>を参照。
{#img width=80%}
<a href="#lst"></a>を参照。
```js title=プログラムリスト {#lst}
console.log("Hello");
```
style.css
@page {
size: A5;
}
html {
counter-reset: img 0 lst 0;
}
a::before {
content: target-text(attr(href url), before);
}
figure figcaption::before {
margin-inline-end: 1em;
}
figure:has(img) {
counter-increment: img;
}
figure:has(img) figcaption::before {
content: "図" counter(img);
}
figure:has(pre) {
counter-increment: lst;
}
figure:has(pre) figcaption::before {
content: "リスト" counter(lst);
}
以前の例
---
lang: ja
link:
- rel: stylesheet
href: style.css
---
<a href="#img"></a>を参照。
<!-- figcaptionとimgの順番は固定なのでHTMLで書くしかない -->
<figure>
<figcaption id="img">画像</figcaption>
<img width="80%" src="./img.png" />
</figure>
<a href="#lst"></a>を参照。
<!-- figcaptionにidを付けたければHTMLで書くしかない -->
<figure>
<figcaption id="lst">プログラムリスト</figcaption>
```js
console.log("Hello");
```
</figure>
-
順番の入れ替えはフレックスボックスレイアウトでも実現できるのですが、可能なところではCSSをシンプルにすることを優先するのがバグを踏み抜かないためのコツでもあります。 ↩
-
https://vuepress.github.io/guide/markdown.html#code-title ただし現在のところダブルクオートのエスケープはできません。 ↩
