JS不要でアクセシビリティ配慮のHTML/CSSスライドメニュー
ブラウザをキーボードで操作する場合にメニューの開閉ボタンはフォーカスできるよう<button>要素を使います。かつては<button>要素を操作するためにJavaScriptを使っていましたが、現在ではHTMLとCSSのみで操作ができるように対応が進んでいます。
そこで実際にJavaScriptを使わずにアクセシビリティに配慮したスライド形式のメニューを作成しようと思います。
Invoker Commands API
「Invoker Commands API」が主なブラウザに実装され 、ボタンに動作を割り当てる方法を提供しています。スペースバーやリターンキーなどのキー操作やマウスクリックでボタンが有効になると、特定の要素を制御できます。今回のメニュー表示では開くための<button>を操作すると<dialog>にopen属性が付与され、閉じるための<button>はopen属性を消します。
Closedbyの対応状況
Invoker Commands APIを操作できる属性の「command」と「commandfor」を<button>に付けると共に、<dialog>に「closedby」を付与すると<dialog>を閉じるのに<button>以外の操作を加えられます。メニュー領域以外のクリックやエスケープキーの操作で<dialog>のopen属性を取り除きます。
ただし「closedby」は2026年6月現在では主なブラウザの内、Safariでは動作しません。2026年4月にSafari Technology Preview 242に含まれたので、しばらく待てば使えるようになるでしょう。「closedby」が動かないブラウザのためにJavaScriptで補えば、現状でも「Invoker Commands APIと「closedby」を使いアクセシビリティを考慮したメニューをサイトに実装できます。
基本のHTML構造
HTMLは基本的に以下のように記述します。
<button type="button" commandfor="d1" command="show-modal" aria-label="メニューを開く">
<svg aria-hidden="true"></svg>
</button>
<dialog id="d1" closedby="any" aria-labelledby="d3">
<h2 id="d3">メニュー</h2>
<nav>
<ul>
<li><a autofocus href="#">リンク1</a></li>
<li><a href="#">リンク2</a></li>
<li><a href="#">リンク3</a></li>
</ul>
</nav>
<button type="button" commandfor="d1" command="close" aria-label="メニューを閉じる">
<svg aria-hidden="true"></svg>
</button>
</dialog>
<button>には必ず「type="button"」を付けます。「commandfor」で操作する<dialog>のidを指定し「command」で操作の内容を表します。<dialog>に「closedby」で「any」「closerequest」「none」のいずれかの値を指定することで閉じ方を選べます。
visibility: hiddenで要素を隠す場合
画面外のメニューがボタンの操作で画面内にスライドして表示させる場合のCSSについて考えます。キーボードでブラウザを操作する場合やスクリーンリーダーで読み上げる場合に画面外にあるメニューを無視するために「visibility: hidden」か「display: none」を使います。
「visibility: hidden」は要素は存在するけど中身は見えない状態で「display: none」は要素自体が存在しないという違いがあります。スライドをアニメーションで表現するには「visibility: hidden」が簡単です。要素が存在しているので「hidden」と「visible」を切り替えるだけです。以下のCSSで動作します。
dialog {
display: block;
visibility: hidden;
transition: .5s ease-out;
right: -300px
}
dialog[open] {
visibility: visible;
right: 0
}
dialogにdisplay:blockをあえて書いているのはFirefoxで閉じる際のアニメーションを表示させるためです。display:blockがない場合はボタンの操作と同時にメニューが消えます。Chromeは書かなくても問題ないのでFirefoxのバグかもしれません。
「visibility: hidden」ではメニューの表示時に「autofocus」属性を付与されたリンクがフォーカスされません。仕様ではメニューが表示されると「autofocus」が存在するリンクがフォーカスされ、「autofocus」が無い場合は<dialog>にフォーカスするとなっていますが、「autofocus」が存在しても<dialog>にフォーカスします。
試しにtransitionの時間を0にすると「autofocus」があるリンクにフォーカスし、仕様のように動作しました。レンダリングの関係で時間が掛かるとうまくいかないようです。解決方法はJavaScriptでフォーカスをコントロールするしかなさそうです。
display: noneを使う場合
一方「display: none」を使えばアニメーションの時間があっても「autofocus」のあるリンクにフォーカスします。ただ、「display: none」から「display:block」へのアニメーションのCSSは記述量が増えます。以前は不可能だったのですが「@starting-style」でdisplay:noneの状態でのプロパティを定義し、「allow-discrete」でアニメーションの時間の間に値が変更しないようにできるようになりました。
今回の場合だとボタン操作から0.5秒間経ってから「display:none」になるように指定しているのが以下のCSSです。
dialog {
display: none;
right: -300px;
transition: right 0.5s ease-out, display 0.5s allow-discrete, overlay 0.5s allow-discrete
}
dialog[open] {
display: block;
right: 0
}
dialog::backdrop {
opacity: 0;
transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete
}
dialog[open]::backdrop {
opacity: 1
}
@starting-style {
dialog[open] {
right: -300px
}
dialog[open]::backdrop {
opacity: 0
}
}
transitionのoverlay 0.5s allow-discreteはdialog::backdropが消えるときのアニメーションに必要です。dialog,dialog::backdropの両方にoverlay .5s allow-discreteを記述すると初めてdialog::backdropが0.5秒かけて消えるようになります。
Can I Useで「allow-discrete」を調べるとFirefoxも対応していると表示されますが、実際は動作していません。バグとして認識されているようなので修正を待っていますがなかなか修正されず、メニューが消える際のアニメーションが動作しません。
最後までお読みいただき、誠にありがとうございます。

自作テーマのランキング





総合ランキング




当サイトの管理者 : 炭火で美味しいものを作ることを中心に、日々の趣味についてを文章にすることで、WordPressを使ってのWebページ作成を忘れないようにするブログです。熱帯魚の世話や野菜の栽培、Linuxについて、音楽を聴くための機材やアイデアなど、興味のあることを書いています。兵庫県在住。