ちゃんとしたモーダルウィンドウを作る
新しい情報は jxck さんの記事がおすすめ。
参考サイト
UIにおける見えるけど利用できない非活性な領域の実装とinert属性について
モーダルウィンドウの外側の実装
モーダルウィンドウを作る場合にモーダル外の実装に気を配らなければならない。見た目だけ作ればいいというわけではない。
code: html
<!-- こっちの話(モーダル外) -->
<main>
<a href="/foo">Foo</a>
<a href="/bar">Bar</a>
</main>
<div id="modal">
<div id="modal__overlay"></div>
<div id="modal__dialog"></div>
</div>
クリックの無効化
モーダル外の要素がクリックされないようにする。
モーダルの外の要素が隠れるようにオーバーレイで覆い被せるか、モーダル外のルート要素のスタイルに pointer-events: none; をつければ良い。
範囲選択の無効化
モーダル外の要素が Control+A や検索などで範囲選択されないようにする。
モーダル外のルート要素のスタイルに user-select: none; をつければ良い。
スクリーンリーダーの読み上げ無効化
モーダル外の要素のスクリーンリーダーによる読み上げを抑制する。
モーダル外のルート要素に属性として aria-hidden="true" をつければ良い。
フォーカスの無効化
モーダル外が Tab キーによってフォーカスされないようにする。フォーカスされる要素自体に属性として tabindex="-1" をつければ良い。
ちなみにデフォルトでフォーカスされる要素は定義されている。
a elements that have an href attribute
link elements that have an href attribute
button elements
input elements whose type attribute are not in the Hidden state
select elements
textarea elements
summary elements that are the first summary element child of a details element
Elements with a draggable attribute set, if that would enable the user agent to allow the user to begin drag operations for those elements without the use of a pointing device
Editing hosts
Browsing context containers
これらの要素に一つずつ属性を追加しないといけない。
まとめ
code: html
<main aria-hidden="true" style="pointer-events: none; user-select: none;">
<a href="/foo" tabindex="-1">Foo</a>
<a href="/bar" tabindex="-1">Bar</a>
</main>
<div id="modal">
<div id="modal__overlay"></div>
<div id="modal__dialog"></div>
</div>
focus-trap
子孫要素もまとめて tabindex="-1" を付ける専用のライブラリが存在する。React 用のラッパーも提供されている。
これを使うことによって現実的に"ちゃんとしたモーダルウィンドウ"(の外側)が作れるようになる。
code: jsx (html)
<FocusTrap>
<main aria-hidden="true" style="pointer-events: none; user-select: none;">
<a href="/foo">Foo</a>
<a href="/bar">Bar</a>
</main>
</FocusTrap>
<div id="modal">
<div id="modal__overlay"></div>
<div id="modal__dialog"></div>
</div>
ちょっと実装がめんどい。
inert 属性
管理が面倒くさいということから inert 属性が追加された。
これを使うと今までの列挙してきた指定がルート要素にこの属性をつけるだけで出来るようになる。もちろんフォーカスの無効化もルートに書くことで子孫要素にも反映される。
code: html
<main inert>
<a href="/foo">Foo</a>
<a href="/bar">Bar</a>
</main>
<div id="modal">
<div id="modal__overlay"></div>
<div id="modal__dialog"></div>
</div>
便利。