vfm
vfm copied to clipboard
spec: Fenced blocks
Adopt fenced divs from Pandoc's Markdown, that allows special fenced syntax for div
blocks.
See the spec: Pandoc Extension: fenced_divs
... As with fenced code blocks, one can use either attributes in curly braces or a single unbraced word, which will be treated as a class name. The Div ends with another line containing a string of at least three consecutive colons. The fenced Div should be separated by blank lines from preceding and following blocks.
Example:
::::: {#special .sidebar} Here is a paragraph. And another. :::::
Fenced divs can be nested. Opening fences are distinguished because they must have attributes:
::: Warning :::::: This is a warning. ::: Danger This is a warning within a warning. ::: ::::::::::::::::::
Fences without attributes are always closing fences. Unlike with fenced code blocks, the number of colons in the closing fence need not match the number in the opening fence. ...
Discussion
This "fenced divs" syntax is very similar to the walled block in the current VFM draft. I prefer "fenced divs" because:
- naming: "fenced divs" is better, because the term "fenced" is consistent with "fenced code blocks".
- colons
:::
is better than===
, because===
is used for setext heading underline and will make confusion. - the attribute syntax, same as the fenced code attributes, enables adding arbitrary attributes
Extend to HTML semantic elements
I'd like to change the issue title "Fenced divs" to "Fenced blocks" because I want to extend it for HTML semantic elements.
Examples:
:::::: section {.classA #idA role="doc-abstract" aria-label="Abstract"}
The **Abstract** section has a summary of the principal ideas,
concepts and conclusions of the work.
::::::
will be converted to:
<section id="idA" class="classA" role="doc-abstract" aria-label="Abstract">
<p>The <strong>Abstract</strong> section has a summary of the principal ideas,
concepts and conclusions of the work.</p>
</section>
and
:::::: aside {role="doc-tip"}
### Tip
Helpful information that clarifies some aspect of the content
or assists in its comprehension.
::::::
will be converted to:
<aside role="doc-tip">
<h3>Tip</h3>
<p>Helpful information that clarifies some aspect of the content
or assists in its comprehension.</p>
</aside>
Update: syntax changed ::: {section .classA #idA…}
to ::: section {.classA #idA…}
because the tag name inside the curly braces might make confusion with attribute name without value specified.
Testing with pandoc, I found that ::: aside
is converted to <div class="aside">
(not <aside>
), but ::: section
is converted to <section>
(not <div class="section">
). This behavior is different from what the pandoc document says: "a single unbraced word, which will be treated as a class name". I want to fix this inconsistency.
Proposal: the unbraced word is treated as element name if it is a valid HTML element name, otherwise treated as a class name (same as {.className}
).
One of my concerns is that attribute list which we should allow (Obviously ::: {script="alert('XSS')"}
have to be omitted), but this spec seems to be very useful!
I had the same concern @spring-raining had. And that's why only classes are allowed in the original spec of walled blocks.
Too many freedoms sometimes could be harmful.
I like the naming of Fenced blocks
from @MurakamiShinyu. It sounds natural.
Proposal: the unbraced word is treated as element name if it is a valid HTML element name, otherwise treated as a class name (same as {.className}).
I prefer unbraced word is always considered as an element name, and braced words are considered as attributes (classes, IDs and attributes). It is consistent behavior. magical behavior would be useful, indeed. But it sometimes confuses readers.
I agree that relying on "valid HTML element names" is not a good idea. It will change by HTML spec updates. And also we should consider about Custom elements.
So here is my take.
- Suitable for CSS Theming.
- More frequent, fewer keystrokes.
- Fenced blocks are mainly for a specific block to have dedicated styles. plain declarations should always be tied with a class name. tagName is a nice alternative but keep in mind that scriptwriters should care about semantics for their manuscript and be agnostic as possible to its final HTML structure.
# Chapter 2
:::author
uetchy
:::
<h1>Chapter 2</h1>
<div class="author">
<p>uetchy</p>
</div>
.author {
padding: 5px;
border: 1px solid black;
border-radius: 4px;
}
role
support
Since we care about WCAG, role
property must have special treatment. so here it is. @
is used because of its resemblance to a roll. any thoughts?
:::@doc-tip
### Tip
:::
<aside role="doc-tip">
<h3>Tip</h3>
</aside>
[role="doc-tip"] {
background-color: yellow;
}
custom attributes
Maybe we need fine-grained control over these blocks, yet it's not so often. but it's always good to leave an option to users, isn't it?
:::alertbox {style="text-decoration: uppercase"}
Users beware!
:::
:::{#fig:signup}
1. Go to webiste
2. Click on `signup` button
:::
<div class="alertbox" style="text-decoration: uppercase">
<p>Users beware!</p>
</div>
<div id="signup" data-ref="fig">
<ul>
<li>Go to website</li>
<li>Click on <code>signup</code> button</li>
</ul>
</div>
latter uses cross-ref syntax.
What will be happen on VFM when omitting open/close notation?
Pattern A:
:::author
I forgot to close.
Pattern B:
I forgot to open.
:::
VFM just ignores them.
OK, so...
:::div1
alpha
:::::div2
beta
::::::::
- take div1, ignore div2
- take div2, ignore div1
- ignore both
Just FYI: The fenced code block in commonmark is closed by the end of the document if no closing code-fence found. https://spec.commonmark.org/0.29/#example-96
現状の vfm の Fenced blocks 実装とその課題、Pandoc との比較
Fenced blocks は、Prior Art である Pandoc の fenced_divs と互換性があるとよい。
Pandoc の fenced_divs の仕様
https://pandoc.org/MANUAL.html#extension-fenced_divs
- 開始は3つ以上の連続するコロンのあとに属性指定があること
- 属性指定のあとに、さらに任意の数の連続するコロンがあってもよい
- 属性指定の構文は次のいずれか:
- ひとつの名前だけでクラス名を表す
- 波括弧で囲んだ形式:
{#identifier .class .class key=value key=value}
- 終了は3つ以上の連続するコロン
- 前後のブロックとの間は空白行で区切ること
例:
::::: {#special .sidebar}
Here is a paragraph.
And another.
:::::
↓
<div id="special" class="sidebar">
<p>Here is a paragraph.</p>
<p>And another.</p>
</div>
次はネストの例。開始のフェンスには属性指定があることで終了のフェンスと区別される(3つ以上連続するコロンの数は関係ない):
::: Warning ::::::
This is a warning.
::: Danger
This is a warning within a warning.
:::
::::::::::::::::::
↓
<div class="Warning">
<p>This is a warning.</p>
<div class="Danger">
<p>This is a warning within a warning.</p>
</div>
</div>
内容が空の fenced div を試すと:
:::empty
:::
↓
<div class="empty"></div>
現状の vfm の Fenced blocks をテストすると
しかし、現状の vfm パーサーの実装では、Pandoc のドキュメントにある次の例が処理できない:
::::: {#special .sidebar}
Here is a paragraph.
And another.
:::::
↓
<p>::::: {#special .sidebar} Here is a paragraph.</p>
<p>And another. :::::</p>
Fenced blockへの {…}
での属性指定は未サポート。ではクラス名だけを書く記法ではどうか:
::::: sidebar
Here is a paragraph.
And another.
:::::
↓
<p>::::: sidebar Here is a paragraph.</p>
<p>And another. :::::</p>
どうも連続するコロンの数が3つよりも多いと fenced block として処理されないようである。コロンを3つに直したら:
::: sidebar
Here is a paragraph.
And another.
:::
↓
<div class="sidebar">
<p>Here is a paragraph.</p>
<p>And another.</p>
</div>
ネストの例ではどうか。
::: Warning ::::::
This is a warning.
::: Danger
This is a warning within a warning.
:::
::::::::::::::::::
↓
<p>::: Warning :::::: This is a warning.</p>
<div class="Danger"><p>This is a warning within a warning.</p></div>
<p>::::::::::::::::::</p>
これを次のように直したところ現状の vfm で処理できるようになった:
::: Warning
This is a warning.
:::: Danger
This is a warning within a warning.
::::
:::
↓
<div class="Warning">
<p>This is a warning.</p>
<div class="Danger">
<p>This is a warning within a warning.</p>
</div>
</div>
内容が空の Fenced block を試すと、Fenced block として処理されない:
:::empty
:::
↓
<p>:::empty :::</p>
ここまでで分かった問題点
現状の vfm のパーサーでは
-
{…}
での属性指定が未サポート - 連続するコロンの数はネストの外側では必ず3つ
:::
- 属性指定のあとにコロンがあるとだめ。(
::: Fenced :::::::::::
のようにフェンスらしくできない) - ネストするごとに1つずつコロンの数を増やさなくてはならない
- 属性指定のあとにコロンがあるとだめ。(
- 内容が空だとだめ
見出しとの組み合わせの場合
Pandoc では、fenced div 内が見出しではじまる場合、fenced div で div ではなく section 要素が生成される。
例:
::: appendix
## Appendix A {#aaa .bbb data-ccc=ddd}
This is an appendix.
:::
↓
<section id="aaa" class="bbb appendix" data-ccc="ddd">
<h2 class="bbb" data-ccc="ddd">Appendix A</h2>
<p>This is an appendix.</p>
</section>
pandocに --section-divs
オプションを指定した場合の結果は:
<section id="aaa" class="level2 bbb appendix" data-ccc="ddd">
<h2 class="bbb" data-ccc="ddd">Appendix A</h2>
<p>This is an appendix.</p>
</section>
--section-divs
オプションの有無にかかわらず、fenced div内が見出しではじまる場合は section 要素で囲まれ、見出しに指定された属性はsection要素にコピーされる。
class属性に levelN
(N は見出しのレベル) が追加されることだけ --section-divs
指定があると違う。
現状のvfmでは
::: appendix
## Appendix A {#aaa .bbb data-ccc=ddd}
This is an appendix.
:::
↓
<div class="appendix">
<section id="aaa" class="bbb" data-ccc="ddd">
<h2>Appendix A</h2>
<p>This is an appendix.</p>
</section>
</div>
vfm では見出しで自動的に section 要素が作られるが、Fenced block の div 要素の中にその section 要素ができる。あまりスマートでない。
role 属性について
現在の VFM の独自拡張で次のような記法で DPUB-ARIA の role 属性を指定できる:
:::@appendix
# Appendix A
:::
↓
<section role="doc-appendix" id="appendix-a">
<h1>Appendix A</h1>
</section>
この独自拡張仕様の見直しについては spec: WAI-ARIA role syntax (#28) にコメントする。 その要点は以下:
- role 属性のための
@
をプレフィックスする記法(例:@appendix
)は廃止したい - クラス名に DPUB-ARIA で定義された名前を使うことを推奨し、そのようなクラス名が指定されたら自動的に role 属性も付加するようにする
- 例:
::: appendix
または{.appendix}
→<section class="appendix" role="doc-appendix">
- 例:
- スタイルシートでは role 属性ではなくクラス名を使う(
[role="doc-appendix"]
ではなく.appendix
を使うこと)- role 属性はアクセシビリティ用途、クラス名はスタイルシート用途、と区別
- role 属性によって自動的に生成する HTML 要素を変えるようにはしない
- ある role 属性値を指定するのに適切な HTML 要素はひとつと限らない
- HTML 要素名を明示的に指定できるしくみがあったほうがよい(このあとの節で提案)
HTML 要素名を明示的に指定可能にする拡張案
(このコメントは長過ぎだったので次のコメント https://github.com/vivliostyle/vfm/issues/5#issuecomment-777996312 に移動)
昨日の開発者会議を受けた #67 のとおり、本機能は v2 へ見送ります。
VFM v2 は remark-parse
v9 対応を想定しているので、その際は本機能を自前で実装せずに Pandoc 互換となる以下を採用するのがよさそう。
HTML 要素名を明示的に指定可能にする拡張案
(前のコメント https://github.com/vivliostyle/vfm/issues/5#issuecomment-768119623 が長過ぎだったので分割した)
Pandoc の fenced_divs では、基本は div 要素が作られ、最初の要素が見出しであれば div の代わりに section 要素が作られる。
これをさらに拡張して、ほかの HTML 要素名も指定可能にしたい。
拡張案:
-
:::
のあとの名前が HTML 要素名として有効なもの(例: header, footer, main, nav, article, aside, section)の場合、その名前を HTML 要素名とクラス名の両方にする -
:::
のあとに HTML 要素名、そのあとに波括弧囲みの属性指定{…}
という構文も可能にする
例:
::: aside
Hello
:::
↓
<aside class="aside">
<p>Hello</p>
</aside>
属性の指定もある場合:
::: article {#aaa .bbb data-ccc="ddd"}
# The Article
:::
↓
<article id="aaa" class="article bbb" data-ccc="ddd">
<h1>The Article</h1>
</article>
波括弧囲みのクラス名指定だけの場合はそれを HTML 要素名にはしない:
::: {.aside}
Hello
:::
↓
<div class="aside">
<p>Hello</p>
</div>
::: {.aside}
# Hello
:::
↓
<section class="aside" id="hello">
<h1>Hello</h1>
</section>
Fenced block の :::
のあとの名前を HTML 要素名にすると不正な HTML になるような場合には、その名前を HTML 要素名とはしないでクラス名にする
::: span
# Hello
:::
↓
<section class="span" id="hello">
<h1>Hello</h1>
</section>
Fenced block の内容にインラインのテキストも可能にする拡張案
Fenced block の :::
のあとの名前が HTML 要素名で、その内容に段落(p)要素を持つことができないものである場合、Fenced block 内のテキストを段落要素にしないでインラインのテキストの扱いにする
::: small
This is a small notice.
:::
↓
<small class="small">This is a small notice.</small>
Fenced block の終了を分かりやすくする拡張案
Fenced block の終了の :::
のあとに任意で /名前
を書けるようにすることで、ネストした fenced block の開始と終了のペアを分かりやすくできたらよいかもしれない。例:
::: aside
:::: Warning
This is a warning.
::::: Danger
This is a warning within a warning.
::::: /Danger
:::: /Warning
::: /aside
@akabekobeko
VFM v2 は
remark-parse
v9 対応を想定しているので、その際は本機能を自前で実装せずに Pandoc 互換となる以下を採用するのがよさそう。
remark-fenced-divs の readme に次の記述:
If you don't need Pandoc compatibility you should consider using remark-directive.
remark-fenced-divs あるいは remark-directive を検討するとよさそうですね。
:::main{#readme}
で
<main id="readme">
になります。これは私の HTML 要素名を明示的に指定可能にする拡張案 と似ています。私の案では、
:::main{#readme}
は
<main class="main" id="readme">
となるのですが、要素名と同じものをclassにも出力するのは実績がある Pandoc となるべく互換にしようとしたためです。 しかし、remark-directive の拡張が多くの人が利用する標準になっていくのであれば、Pandoc互換にこだわる必要はありません。
この記法は #67 により v1.0 では見送りとなりました。v2.0 で検討する場合は↑の @MurakamiShinyu さんコメントにあるとおり parser 変更があるため Pandoc 互換の benabel/remark-fenced-divs を検討予定です。
v2.0 で検討する場合は↑の @MurakamiShinyu さんコメントにあるとおり parser 変更があるため Pandoc 互換の benabel/remark-fenced-divs を検討予定です。
Pandoc 互換ではない remarkjs/remark-directive も検討するとよいです。
はい。両方を候補にしましょう。ただし択一にしたいですね。
Pandoc の fenced_divs では、基本は div 要素が作られ、最初の要素が見出しであれば div の代わりに section 要素が作られる。 これをさらに拡張して、ほかの HTML 要素名も指定可能にしたい。
MarkdownはHTMLの軽量マークアップ言語であり、HTMLによるドキュメント生成において比較的頻繁に用いられるセマンティックタグやタグ構造を、ドキュメントの執筆者にとって見やすく、書きやすいように設計されたものです。
:::main ... :::
がその哲学に則っているかというとどうでしょう。多くのMarkdownユーザーにとって一般的でないし、他のMarkdown拡張と互換性がない上、<main>
~</main>
と書くのと記述量として大差がありません。
単純にPandoc互換を目的とするなら、Pandoc記法のサブセットにすべきかと。
Fenced blocksによって解決されると期待されている問題:
- #171
Pandoc 互換ではない remarkjs/remark-directive も検討するとよいです。
はい。両方を候補にしましょう。ただし択一にしたいですね。
Pandoc互換の https://github.com/benabel/remark-fenced-divs は、"ARCHIVED: This plugin has never been completely adapted for remark 13+." となっているので、今後 remark 13+ に移行予定のvfmには使えなさそう。
vfm v1→v2の仕様変更で、見出しのsection囲みでの属性の扱いについてPandoc互換をやめていることもあるし、Pandoc互換よりも remarkjs のエコシステムでポピュラーで使いやすいものを採用するのがよさそう。