document outline algorithm と h1 要素
HTML, Accessibility没ネタの供養。
TL;DR
h1
を複数置けるわけじゃない- アウトラインを生成するアルゴリズムはブラウザで実装されてない
- 見出しレベルを自動調整するように動いていたが頓挫した
section
お前は何者だ
document outline algorithm について
"document outline algorithm" とは、HTML のアウトラインを生成する都市伝説のこと。
アウトラインとは、見出しを持ったセクションで構成される枠を指す。
HTML5 より前は、このアウトラインを表現する方法がなく、広義な wrapper の div
が採用されていた。
見出し要素もセクションごと、というよりは文書の階層にかかるもので、兄弟としてのフラットな構造だった。
HTML5 から、 section
や nav
などで囲みアウトラインを生成する謎のアルゴリズム "document outline algorithm" が登場した。
詳細は WHATWG の spec にある。
4.3.11 Headings and sections, 4.3.11.1 Creating an outline - HTML Standard
"sectioning content" は section
や nav
などのセクションを構成する要素、"sectioning root" はそれらのルートに当たる body
などの要素を指す。
これらを起点に、暗黙のセクション・サブセクションを生成する。
セクション・サブセクションは、1 つの見出しを持ち、ネスト可能なアウトラインのパーツのようなもの。
例えば、下記のようなマークアップで、section
を起点とした暗黙のアウトラインが生成される。
"sectioning" とか呼ばれることもある。
html
<body>
<h1>ページのタイトルだよ</h1>
<main>
<section>
<h1>セクション 1 だよ</h1>
<p>セクションの文章だよ</p>
</section>
<section>
<h1>セクション 2 だよ</h1>
<p>セクションの文章だよ</p>
<section>
<h1>セクション 2 だよ</h1>
<p>セクションの文章だよ</p>
</section>
</section>
<section>
<h1>セクション 3 だよ</h1>
<p>セクション 3 の文章だよ</p>
<h2>セクション 3 サブタイトルだよ</h2>
<p>セクション 3 の文章その 2 だよ</p>
<h3>セクション 3 サブタイトルだよ</h2>
<p>セクション 3 の文章だよ</p>
</section>
</main>
</body>
これによって section
内では h1
から始めても良く、むしろ h1
だけでも OK などのが伝承が広まった。
Sections may contain headings of any rank, but authors are strongly encouraged to either use only h1 elements, or to use elements of the appropriate rank for the section's nesting level. (訳: セクション内には任意のランクを含めることができますが、著者は h1 要素のみを使用するか、セクションのネストに応じたランクの見出しを記述することを推奨します。) - 4.3.11 Headings and sections on HTML Standard
現実
2020 年 10 月時点、このアルゴリズムは全てのブラウザや支援技術で実装されていない。
実際にアウトラインなるものは確認できず、見出しはフラットな構造を保ったまま。
W3C ではこのアルゴリズムに従わないよう警告が記されていたけど、WHATWG から消えてしまった。
4.3.9.1. Creating an outline - HTML 5.2
それに対して Steve Faulkner 氏が 2015 年に Issue を立て、現在も議論が続いている。
Suggest adding a warning about outline algorithm · Issue #83 · whatwg/html
仕様と矛盾した警告を載せるのではなく、アルゴリズム自体を消すか実装するかの問題に変わり、今も掲載されてない。
MDN には掲載されているので、それなりに周知されているのかもしれない。
Using HTML sections and outlines - Developer guides | MDN
実装しようとしてもアルゴリズム自体が複雑で、これといった対応がないまま 10 年以上は停滞している様子。
近年の動き
近年、Anne van Kesteren(annevk)氏を中心に少し動きがあり、「アウトラインを生成すること」ではなく「"sectioning content" 内の見出しレベルを調整すること」に焦点が当てられた。
annevk 氏は James Craig 氏の提案をベースに、見出しの調整に関する Polyfill を作成した。
実装は非常にシンプルで、h1
か hgroup
(後で説明) の "sectioning content | root" をたどり、ネストされたぶんレベルを加算して aria-level
属性に設定するもの。
html-heading-level-polyfillより
function determineLevel(el) {
let level = 1;
// Arguably the parentNode being null check can be removed as it will never be null when this and
// the function below are run on a document, as is the case.
//
// This is "sectioning content" and "sectioning roots"
while (el.parentNode && (el = el.parentNode.closest("article,aside,nav,section,blockquote,details,dialog,fieldset,figure,td"))) {
level += 1;
}
return level;
}
スクリーンリーダーユーザーを対象にした WebAIM の調査によれば、全体の 68.8% のユーザーが「見出しをナビゲートに使用する」と回答し、計 86.1% のユーザーが見出しレベルを「役に立つ」か「やや役に立つ」と回答していて、見出しが重要な要素を占めていることがわかる。(どのページにも見出しはあるから、それはそうだけど)
机上の空論を掲載し続けるより、今あるナビゲートの最適化を選ぶのは筋が通ってる。
そこから事が進み、sectioning の概念を廃止する PR が提出された。
:heading
擬似クラスを追加、文書のルートとなる h1
を持たなければならないなどの特徴がある。
議論では headinglevelstart
という「見出しレベル調整の基準となる値」を設定する闇の属性も提案されている。
Add heading-focused outlines and :heading by annevk · Pull Request #3499 · whatwg/html
Consider adding a headinglevelstart attribute · Issue #5033 · whatwg/html
悲しいことに、この構想自体は後方互換の問題があったようで、実装が頓挫してしまった。
Heading levels — Anne’s Blog
Intent to prototype: heading levels
ただ、見出しをベースとしたアルゴリズム再構築の試みとして、まだ何らかの動きがありそう。
Add heading-focused outlines and :heading by annevk · Pull Request #3499 · whatwg/html
長年滞っていた問題にメスが入り sectioning 廃止の PR が出たりして、今後の動向に少し期待してる。
一方、これまで h1
だった見出しが 3 にも 4 にもなりそうで、破綻に慣れてしまったユーザーにとってこれが良いのか悪いのかはわからない。
h要素について
h
要素というのもしばしば言及される。
これは section
要素と組み合わせ、見出しレベルをいい感じにするため提案される要素。
html
<section>
<h>見出しだよ</h>
<p>こんにちは</p>
</section>
h1-6
というレベルは HTML の祖先にあたる GML から存在していて、それがそのまま引き継がれている。
(実際には GML は h0-6
で、今のレベルになったのは後継の SGML から)
後方互換性を捨てた XHTML2.0 ではこれらのレベルを deprecated とし、かわりに section
要素と h
要素を用いて、見出しレベルの設定を UA 側に委ねる提案があった。
しかし、 h1-6
要素は残されたままで、XHTML 2.0 自体も放置され、謎だらけのまま終わってしまった。
XHTML Block Text Module
HTML5 でも、document outline algorithm の問題を解決するために h
要素が提案された。
Do not recommend using nested sections with h1 · Issue #169 · w3c/html
Add h element · Issue #774 · w3c/html
ニーズが現代 Web 事情に合致している。顧客が本当に求めているもの。
やるとしても新しい要素になるので、UA 側の対応・マッピングや、後方互換性の問題が懸念されていたりする。
あと「悔しかったら暗黙のセクション生成してみろよ」のようなマークアップで対応ができないこともある。
html
<aside>
<!-- 実は h5 かもしれない -->
<h>タイトル</h>
<p>こんにちは</p>
<h>タイトルですよね</h>
<p>そうですよね</p>
<!-- h6 かも -->
<h>タイトルですよね</h>
<p>そうですよね</p>
</aside>
ちなみに h
要素が初めて言及されたのは 30 年近くも前のことになる。時代が早すぎた。
Re: status. Re: X11 BROWSER for WWW - Tim Berners-Lee
section 要素について
h
要素の wrapper として機能する section
だけど、今の section
要素は一体何者なのか。
この要素は HTML5 から追加された要素で、"sectioning content" のひとつ。
また、region
ランドマークを持つことがある。
"generic section of a document or application(ドキュメントまたはアプリケーションの一般的なセクション)" というあまりにも曖昧な何かを示す時に使う。
HTML5 より前から section
自体は構想として出てきたものの、汎用的な div
要素に代わられていた。
document outline algorithm と sectioning の概念ができることで、ようやく人権を得た。
「section
には見出しを入れるべき」という教えは、sectioning の概念から来ているのかもしれない。
しかし、その恩恵を受けられるアルゴリズムが実装されていない現状、役割は闇に覆われている。
WHATWG の section
要素の説明では、中に h1-6
要素を追加することについて "typically" と表現していて、 "must" や "should" はない(言葉の綾というか揚げ足)。
ARIA 1.2 にある region
の説明では、 "SHOULD" の表現が用いられているだけで、何の役にも立たない。
HTML-AAM だと、section
要素は accessible name
が存在しないと role を持たない、とある。
section - HTML Accessibility API Mappings 1.0
accessible name
については、下記が参考になる。
WCAG に出てくる「名前」とは? | Accessible & Usable
実際に、下記のマークアップを試してみる。
html
<section aria-label="section要素が本当にsectionなのかをテストするsection">
sectionだよ
</section>
すると、section
要素は識別可能な「名前」を持った region
(日本語だと「地域」)role となる。
これはランドマークとして支援技術にも追加され、ナビゲート可能なもの。
注意点は、section
が必ずしも role を抱えるわけではないということ。
なぜなら、section
は region
かどうか以前に、セクションを構成する要素だから。
識別可能なランドマークとしての section
と、意味論としての section
は、役割が別となる。
しかし現実は「見出しを入れて、識別可能にするべき」のようなごちゃ混ぜ説明が多く、混乱しがち。
The Generic Section element - HTML: HyperText Markup Language | MDN
そしてアルゴリズムがない現状は、section
内に見出しがあっても嬉しいことはない(意味がないわけではなくて、あるけど何もしてくれない)。
それどころか、ネストされた section
に入る h1-6
要素には、芳しくない特徴がある。
例えば、section
内に section
がネストされた場合。
html
<section>
<h1>セクション 1-1 だよ</h1>
<p>セクション 1-1 の文章だよ</p>
<section>
<h1>セクション 1-2 だよ</h1>
<p>セクション 1-2 の文章だよ</p>
</section>
</section>
Chrome で、上記のマークアップを確認してみる。
すると、同じ h1
であるにもかかわらず、「セクション 1-1」にネストされた「セクション 1-2」の見出しは、「セクション 1-1」のそれよりフォントサイズが小さくなる。
これは UA が下記のような style を持つため。
css
:-webkit-any(article,aside,nav,section) :-webkit-any(article,aside,nav,section) h1 {
font-size: 1.17em; /* 元の h1 は 1.5em */
margin-block-start: 1em;
margin-block-end: 1em;
}
バグだと揶揄されることもあるけど、WHATWG の 15.3.6 Sections and headings をもとにしたもの。
もし、document outline algorithm があれば、この実装は適切だと言える。
でも実際は style が視覚的な変更のみを提供するのみで、あまり健全ではない。
現状、この要素は「ドキュメントまたはアプリケーションの一般的なセクション」を表す曖昧な何かでしかない。
「section
には見出しを入れるべき」というカーゴ・カルトなマークアップがすべてを隠蔽してきた。
役に立てなかったのは事実なようで、遠回しに section
の使用をやめさせようとする、ロックな記事もある。
Why You Should Choose HTML5 article Over section — Smashing Magazine
ただ、アルゴリズム再考の試みや、h
要素の案などもあるので、見捨てるにはまだ早いかも。
hgroup 要素について
おまけに hgroup
要素について。
この要素は HTML 5.1 で廃止されたものの、WHATWG では廃止されていない。
そして、document outline algorithm のためにあるような要素なので、現在は使っても機能しない。
hgroup
は、見出しのグループ化をする要素。
html
<hgroup>
<!-- レベル 1 の見出しとして扱われる(はずだった) -->
<h1>grgr-dkrkのブログ</h1>
<h2>備忘録です</h2>
</hgroup>
これは小見出しなどによって、意図しないサブセクションが生成されるのを防ぐためにある。
サブセクションが存在しない今も、小見出しを表現する用途で使われることがある(大抵は div
が使われる)。
現在は h1
と h2
が謎の wrapper に入ってるのとほとんど変わらない。
これが本来の役割を得るとなると、今まで 2 つ以上認識された見出しが 1 つとして扱われることになる。
つまり、見出しアルゴリズムによって、既存ページに影響を及ぼしやすい。
しかも、hgroup
はスタイル当ての wrapper として使われることもあるので、余計ややこしい。
近年の動きで触れた Polyfill では hgroup
を h1
と同等に扱うアプローチだった。
要素に role="heading"
と aria-level="n"
を追加し、h1
の見出しと同じように調整するもの。
しかし下記のような hgroup
だと、見出しレベル 1 として扱われてしまう。
html
<h1>grgr-dkrkのブログ</h1>
<main>
<h2>記事一覧</h2>
<p>略</p>
<!-- aria-level="1" になる -->
<hgroup>
<h2>おすすめ記事</h2>
<h3>厳選しました</h3>
</hgroup>
<p>略</p>
</main>
h1
は sectioning の要で、それが hgroup
要素に入るケースがある以上、h1
相応の扱いをすることは肯けるものの、それだけだとすでにあるユースケースと互換性が保てない。
見出しアルゴリズムにおける hgroup
の扱いについては、様々な意見が交わされた。
「小見出しをどう解釈するか」が論点となっている様子。
- 子に
h1
があればhgroup
要素そのものをh1
として扱い、それ以外は無視する。 hgroup
そのものを無視する。中の見出しと小見出しを含めてそのままにする。- 子から最上位の見出しを
aria-level
で調整し、他の見出しにgeneric
role を付ける。
特に最後の項目は、メインの見出しレベルのみに調整が及び、後方互換性のメリットはある。
ケースとしては下記のような怪文書が来た時など…。
html
<!-- これが丸ごと h1 になる -->
<hgroup>
<h1>grgr-dkrkのブログ</h1>
<h2>いろいろなことを書くブログ</h2>
<div>
<a href="https://www.dkrk-blog.net/">リンク</a>
</div>
<p>ブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文ですブログの説明文です</p>
</hgroup>
hgroup
をどうするのかはまだわかっていない。
今は使っても意味がないし、アルゴリズムの実装にも支障が出ていて、何かとかわいそうな要素。
終わり
この記事は h1
の乱用や section
要素の使用を批判するわけではない。
意味論というより UA の問題なので、むしろ一概に警鐘を鳴らすものではないと思う。
問題に対しての取り組みがしっかり行われていることや、少しでもダークネスめいた仕様が伝われば、と思ってここに供養した。
資料(ページ名順)
- A decade of heading backwards by steve faulkner on CodePen
- Alternative take on hgroup · Issue #5002 · whatwg/html
- Are section elements in examples intended to be landmarks? · Issue #562 · w3c/aria-practices
- Do we need a new heading element? We don't know - JakeArchibald.com
- ibm :: 370 :: DCF :: SH20-9160-0 DCF Generalized Markup Language GML Users Guide Jul1978
- JAWS, IE and Headings in HTML5 | Articles | Accessible Culture
- Screen Reader User Survey #8 Results - WebAIM
- The Document Outline Dilemma | CSS-Tricks
- There Is No Document Outline Algorithm | Adrian Roselli
- WCAG に出てくる「名前」とは? | Accessible & Usable
- Why You Should Choose HTML5 article Over section — Smashing Magazine
- XHTML2.0 - suikawiki
- XHTML Block Text Module