dialog要素を使ってアクセシブルなモーダルを作ってみよう
この記事について
2022 年 3 月、Safari15.4で HTML の dialog 要素が標準でサポートされました。
これにより、全ての主要ブラウザ(Chrome, Edge, Safari, Firefox)で dialog 要素が利用可能になり、今まで@react-ariaなどのライブラリに依存していたモーダル実装を見直したいと思っている方も多いのではないでしょうか。
本記事では、HTML の dialog 要素の使い方からアクセシブルなモーダルの要件、それらを全て満たすアクセシブルなモーダルの実装例についてご紹介します 🫡
そもそもアクセシブルなモーダルの要件って何?
そもそもアクセシブルなモーダルの要件とはどのようなものを指すのでしょうか。
Accessible Rich Internet Applications (WAI-ARIA) 1.1やARIA Authoring Practices Guide (APG)などに記載されていた内容によると、次のものがあげられます。
アクセシブルなモーダルの要件
1. フォーカス管理
- 開始時のフォーカス:開いたとき、フォーカスはモーダル内のフォーカス可能な要素に移動
- 終了時のフォーカス復元:閉じたときに、フォーカスはモーダルを呼び出した要素に移動
-
フォーカストラップ:
Tab
,Shift+Tab
によってモーダルの外側にフォーカスを移動させない
2. モーダルと背景の相互作用
-
背景の非活性化:モーダルの外側にある要素は、
inert
属性により非活性化 - 背景との対話を制御:モーダルの外側にある要素と対話しようとした場合、モーダルを閉じる
- 背景のスクロール不可:アクティブ時、モーダルの外側にある要素はスクロールさせない
3. モーダルの操作
-
閉じる操作:
Esc
キーでモーダルを閉じることができる
4. アクセシビリティ属性
-
役割と状態の指定:
role
属性は dialog に指定され、aria-modal
属性が true に設定 -
ラベルの提供:
aria-label
,aria-labelledby
属性によりラベルが設定されている -
説明の提供:
aria-describedby
属性によってモーダルの説明を含む要素が設定されている[1]
HTML の dialog 要素の使い方
dialog 要素を表示するには、以下の3つの方法があります。
- dialog 要素に
open
属性を付与する(非推奨) -
.show()
メソッドを使う -
.showModal()
メソッドを使う
用語としてのダイアログとモーダルの違いについては、以下の記事がとてもわかりやすかったので気になる方はぜひ読んでみてください。
本記事内では、dialog 要素を使ったモーダルの実装に焦点を当てているため、意図的にダイアログという単語は使わないようにしています。
dialog 要素の表示方法による違い
では、表示方法によってどのような違いがあるのか、簡単な実装例を見てみましょう。
この例では、.show()
メソッドと.showModal()
を使って表示された 2 種類の dialog 要素があります。
.show()
メソッドを使って表示された dialog 要素は、「ユーザーに特定のアクションを求めることはなく、あくまで情報を提示するもの」、すなわちダイアログとして扱われます。
そのため、このダイアログを開いている状態であっても、ユーザーは背景にある「Open Modal」ボタンをクリックすることができます。
一方で、.showModal()
メソッドを使って表示された dialog 要素は、「ユーザーに強制的にアクションを促す、モードのある状態」、すなわちモーダルとして扱われます。
そのため、このモーダルを開いているうちは、背景にある要素へ何かしらのアクションを起こすことはできません。
.showModal()
メソッドを使って表示された dialog 要素内のコンテンツは、最上位レイヤー(top layer)[2]に表示されます。これにより、私たち開発者にとって嬉しいことが 2 つあります。
- 他の要素との重なりを気にする必要がない
-
::backdrop
疑似要素を使用して、背景のスタイルを指定可能
最上位レイヤーの要素は、セット内に表示される順番に積み重ねられます。そのため、従来のようにz-index
や DOM ツリーを駆使して一番上に表示されるように、、なんてことはする必要がありません。
dialog 要素を使うことで省略できるアクセシブルなモーダルの要件
上記の他にも、dialog 要素を使うことでブラウザが標準でサポートしてくれる機能があります。
冒頭で挙げた、アクセシブルなモーダルの要件に照らし合わせると以下のようになります。
1. フォーカス管理
- 開始時のフォーカス:開いたとき、フォーカスはモーダル内のフォーカス可能な要素に移動
- 終了時のフォーカス復元:閉じたときに、フォーカスはモーダルを呼び出した要素に移動
-
フォーカストラップ:
Tab
,Shift+Tab
によってモーダルの外側にフォーカスを移動させない
2. モーダルと背景の相互作用
-
背景の非活性化:モーダルの外側にある要素は、
inert
属性により非活性化 - 背景との対話を制御:モーダルの外側にある要素と対話しようとした場合、モーダルを閉じる
- 背景のスクロール不可:アクティブ時、モーダルの外側にある要素はスクロールさせない
3. モーダルの操作
-
閉じる操作:
Esc
キーでモーダルを閉じることができる
4. アクセシビリティ属性
-
役割と状態の指定:
role
属性は dialog に指定され、aria-modal
属性が true に設定 -
ラベルの提供:
aria-label
,aria-labelledby
属性によりラベルが設定されている -
説明の提供:
aria-describedby
属性によってモーダルの説明を含む要素が設定されている
dialog 要素を使ってモーダルの実装を行うだけでこれほどの要件をカバーできると考えると、かなり便利ではないでしょうか。
MDN にも以下の記載がされており、dialog 要素を使うことが推奨されています。
モーダルダイアログの作成には、HTML ネイティブの <dialog> 要素を使用しましょう。これは、ユーザビリティとアクセシビリティの機能を提供するためで、他の要素を同様の目的で使用する場合は、それを再現しなければなりません。
React で実際に触ってみよう
前章より、dialog 要素を使ってアクセシブルなモーダルを実装する際は、以下の項目については追加で対応する必要があります。
- 終了時のフォーカス復元
- フォーカストラップ
- 背景との対話を制御
- 背景のスクロール不可
- ラベルの提供
- 説明の提供
これらの項目に対しては、開発チームによって様々な対応方法があると思います。
そのため、本記事では今すぐ使える汎用性が高いモーダルコンポーネントを作るのではなく、アクセシビリティの要件をすべて満たしたシンプルなモーダルの実装例をご紹介します。
私は、dialog 要素を使って以下のようにモーダルを実装してみました。
この実装例は、本記事でご紹介したアクセシビリティの要件をすべて満たしたものになります。
それぞれのコードに対する説明は省略しますが、全体的なコードは Github にて公開しているので、気になる方はご参照ください。
あとがき
dialog 要素について調査した結果をまとめてみましたが、いかがでしたでしょうか。
HTML ネイティブの dialog 要素を使うことで、これまでライブラリに依存していたモーダルの実装を改善することができるかもしれません。
この記事がモーダル実装を見直したいと思っている方にとって、少しでもお役に立てば幸いです。
それではまた 👋
-
aria-describedby
属性は、省略されることが望ましい場合もあるため必須ではない
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal ↩︎ -
CSS の z-index: 10000;はいらなくなる、要素を最上位に表示する「最上位レイヤー(top layer)」の基礎知識と使い方
https://coliss.com/articles/build-websites/operation/css/what-is-the-top-layer.html ↩︎
Discussion