見出し画像

アクセシビリティ対応のスライド&プルダウンメニュー(中級以上対象)を作ってみた。


前書き

去年くらいからweb制作の現場でも色々言われているアクセシビリティ義務化についてですが、フォーム周りなど除いて、一番影響してくるのがハンバーガーボタンとスライドメニュー(ドロワーメニューとかハンバーガーメニューと言う方もいますね)あたりの対応では無いでしょうか。

「アクセシビリティ ハンバーガーメニュー」みたいなキーワードで検索すると色々ヒットしますが、大体対応しているのがaria属性の調整がメインのようです。

念のため説明すると、ハンバーガーボタンと実際のナビゲーションを関連付ける必要があるので、以下のような設定が必要になります。

・ハンバーガーボタン(button)
aria-controls属性(どこを操作するのかを指定。大体の場合navとかですね)
aria-expanded属性(ハンバーガーボタンで操作する対象が開いているかどうか)
aria-label属性(「メニューを開くor閉じる」などボタンのラベリング)

・ナビゲーション本体(navなど)
aria-hidden属性(この部分が隠れているのか、いないのか)

このうちaria-controls属性は一度書いておけば変更されることは無いかと思いますが、aria-expanded属性とaria-hidden属性は状況に応じてtrueもしくはfalseを変更しなければなりません。
aria-label属性は文章の書き方によるかと思います。「メニューを開閉する」のようにすれば変更する必要はありませんが、状況が分からないので親切ではないですね。

このあたりをjavascriptないしはjQueryで操作しますが、これだけではアクセシビリティの対応が不十分です。
なぜならキーボードでの操作への対応ができていないからですね。
具体的にはキーボードでの操作に対応するために、タブキーで移動する時にナビゲーションを開閉できるようにする必要があります。

ハンバーガーボタンにフォーカスしている状態でタブキーを押すと次の項目にフォーカスが移り、エンターキー等を押すとナビゲーションが開き、フォーカスがメニューリストに移動していくという操作です。
これはbuttonタグなどでハンバーガーボタンを作り、visibilityの指定などを正しくすれば実現できると思います。

問題はメニューからどう抜けるかです。閉じるボタンをつけるという手もあるでしょうし、メニュー外にフォーカスが移動する際にスクリプトでナビゲーションを閉じるというのも良いかと思います。

さらにアコーディオン(プルダウン)メニューなんかが入ってくると、その部分のaria属性の制御も必要になってきます。
残念ながら、この辺りに対応している丁度良いものを見つけられなかったので、ChatGPTの力をちょいちょい借りながら作ってみました。

で、せっかくならと色々な機能も追加してみました。

機能

今のところ用意した機能はこんな感じです。

  • ページ読み込み時にローダーを表示(指定しないことも可能)

  • 初回のみローダーではなくロゴを表示(指定しないことも可能)

  • ローダー非表示のあとにアニメーションを設定可能。

  • ブレイクポイントでのスライドメニュー使用の有無。

  • スムーススクロール使用の有無。下層ページ等への遷移時にも適用。

  • 見出し部分でエンターキーもしくはスペースキーを押すことでアコーディオン(プルダウンもしくはドリルダウンとも)を開閉。

  • 複数のアコーディオンを開けるようにするか設定可能。

ここまで用意すれば大概の状況に対応できるかと思います。
最近のモダンブラウザにも大体対応…してるはず。
駄目だったらごめんなさい。
もしバグがあった場合ご連絡いただけたら、できるだけ対応したいと思ってますが、本業がデザインなもので手間取るかもしれません。

以下の動画を見てもらえたらなんとなく動きがわかるかと思います。

また機能はすべてクラスの付与で行っています。
実際の動きはCSSで作成してください。
詳しくはデモファイルを参照していただければと思います。

スライドメニューの実装方法

<!-- HTML -->

<button class="js-trigger" aria-controls="nav"><span></span></button>
<nav id="nav" class="js-nav">
  <ul>
    <li><a href=""><span>menu</span></a></li>
	<li><a href=""><span>menu</span></a></li>
	<li>...以下続きます。</li>
  </ul>
</nav>
<div class="js-cover_layer"></div>
...中略
<script src="js/accessible_slidemenu.min.js"></script>
<script>
AccessibleSlideMenu.init();
</script>
</body>
  1. ハンバーガーボタンにjs-triggerクラスを付与します。

  2. ナビゲーション本体にjs-navクラスを付与します。

  3. コンテンツを覆い隠すための要素にjs-cover_layerクラスを付与します。

  4. スクリプト本体をbodyの終了直前に記述してください。

  5. AccessibleSlideMenu.init();をその後に記述します。

スライドメニューは、ハンバーガーボタンとナビゲーション本体、コンテンツを覆うカバーの3要素をbodyへのクラスの付与でコントロールします。
具体的にはjs-triggerまたはjs-cover_layerをクリックすることでbodyにクラスis-openが付与されます。
ナビゲーションオープン時に全画面を覆ってしまう場合などは、3は無くても問題ありません。

またページ読み込み時にaria-expanded属性とaria-label属性、aria-hidden属性が自動的に付与され、状態に応じて値が変更されます。
なお対象となるクラス名およびaria-label属性はオプション設定で変更することができます。

ハンバーガーボタンはbuttonタグ以外でも動きますが、アクセシビリティの観点からbuttonタグを推奨しています。
またaria-controls="nav"を指定することで、動作させる対象がid="nav"であることを明示しています。

アコーディオン(プルダウンメニュー)の実装方法

<!-- HTML -->

<button class="js-trigger" aria-controls="nav"><span></span></button>
<nav id="nav" class="js-nav">
  <ul>
    <li><a href="index.html"><span>ホーム</span></a></li>
	<li>
	  <button class="js-nav_hd" id="accordion_head_01" aria-controls="accordion_01">accordion title</button>
      <div class="js-nav_lower_wrap" id="accordion_01" aria-labelledby="accordion_head_01">
        <ul class="nav_lower">
          <li><a href=""><span>menu list</span></a></li>
          <li><a href=""><span>menu list</span></a></li>
          <li><a href=""><span>menu list</span></a></li>
          <li><a href=""><span>menu list</span></a></li>
        </ul>
      </div>
	</li>
	<li>...以下続きます。</li>
  </ul>
</nav>
<div class="js-cover_layer"></div>
...中略
<script src="js/accessible_slidemenu.min.js"></script>
<script>
AccessibleSlideMenu.init();
</script>
</body>
  1. アコーディオンのヘッダーにjs-nav_hdクラスを付与します。

  2. 直後のコンテンツ部分にjs-nav_lower_wrapクラスを付与します。

  3. aria属性とidを適切に付与してください。

スライドメニューは、アコーディオンのヘッダーおよびコンテンツへのクラスの付与でコントロールします。
具体的にはjs-nav_hdをクリックすることで自身とjs-nav_lower_wrapにクラスis-expandedが付与されます。

ヘッダーにはaria-controls属性を指定し、対象のコンテンツのidと紐づけてください。
コンテンツにはaria-labelledby属性を指定し、このコンテンツの見出しのidと紐づけてください。

またページ読み込み時にaria-expanded属性とaria-hidden属性が自動的に付与され、状態に応じて値が変更されます。
なお対象となるクラス名はオプション設定で変更することができます。

ヘッダーはbuttonタグ以外でも動きますが、アクセシビリティの観点からbuttonタグを推奨しています。

ローダーの実装方法

<!-- HTML -->

<head>
中略
<noscript>
  <style>
    .js-loader {
      display: none;
    }
  </style>
</noscript>
</head>

<body>
<div class="js-loader">
  <div class="js-loader_splash"><img src="img/logo.svg" alt="nature/furniture"></div>
  <div class="js-loader_img"><img class="c-contain" src="img/loader.svg" alt="ローダー"></div>
</div>
<script>
  (function() {
    const session = sessionStorage.getItem('first_session');
    if (session === null) {
      sessionStorage.setItem('first_session', 'true');
      document.querySelector('.js-loader_splash').style.display = 'block';
    } else {
      sessionStorage.setItem('first_session', 'false');
      document.querySelector('.js-loader_img').style.display = 'block';
    }
  })();
</script>
<header class="l-header">
  1. headにnoscriptタグでjavascriptを使用していない場合にはローダーが非表示になる記述をします(無くても動きますが念のため)

  2. body開始直後にローダーのhtmlとスクリプトを記述します。

なお初回表示とローダー表示を切り替える必要がなければスクリプトは記述する必要はありません。ローダーのみで問題なければ以下で大丈夫です。
またローダーのクラス名やセッションストレージ名は設定で変更することも可能です。

<!-- HTML -->

<head>
中略
<noscript>
  <style>
    .js-loader {
      display: none;
    }
  </style>
</noscript>
</head>

<body>
<div class="js-loader">
  <div class="js-loader_img"><img class="c-contain" src="img/loader.svg" alt="ローダー"></div>
</div>
<header class="l-header">

ローダーはセッションストレージを使用することで、初回表示とそれ以降の表示を切り替えています。
スタイルシートは以下を参考にしてください。

//scss

.js-loader {
	background: #000; // ローダーの背景色
	width: 100%;
	height: 100dvh;
	position: fixed;
	inset: 0;
	display: flex;
	justify-content: center;
	align-items: center;
	padding: 1rem;
	z-index: 100000;
	&_splash { // 初回表示用画像の指定。ローダーのみの表示であれば以下不要
		width: 250px; // 初回表示画像のサイズを記述
		height: 46px; // 初回表示画像のサイズを記述
		display: none;
	}
	&_img { // ローダーの指定
		display: none; // ローダーのみの表示であれば不要
	}
}

その他の設定

//javascript
AccessibleSlideMenu.init({
	triggerSelector: '.js-trigger', // ハンバーガーボタンのセレクタ
	navSelector: '.js-nav', // スライドメニュー本体のセレクタ
	coverSelector: '.js-cover_layer', // カバーレイヤーのセレクタ
	menuCloseText: 'メニューを閉じる', // aria-labelのメニューを閉じる時のテキスト
    menuOpenText: 'メニューを開く', // aria-labelのメニューを開く時のテキスト
	useBreakPoint: true, // PCサイズでの切り替え機能を使用するか
	breakPoint: 768, // ブレイクポイントの指定(PCサイズでスライドメニュー使用停止)
	duration: 300, // ローダーがフェードアウトするまでの時間
	delay: 1000, // ローダー表示の遅延時間(初回のみ)
	sessionStorageKey: 'first_session', // セッションストレージのキー名
	loaderBgSelector: '.js-loader', // ローダーの背景セレクタ
	effectSelector: '.js-effect', // 読み込み時のアニメーションを適用する要素のセレクタ
	navHeadSelector: '.js-nav_hd', // アコーディオンヘッダーのセレクタ
	navLowerWrapSelector: '.js-nav_lower_wrap', // アコーディオンコンテンツのセレクタ
	allowMultipleExpanded: false, // アコーディオンを複数開くことを許可するか
	scrollBehavior: 'smooth',//スムースクロールを使用するか。'auto'は不使用
	adjustSelector: '.js-adjust', // 固定ヘッダーなどスムーススクロールの調整用
	adjustHeight: 0, // さらにスムーススクロールを調整する場合
});

AccessibleSlideMenu.init();に上記のようなオプションを設定することが可能です。
それぞれの説明は以下を御覧ください。
使用する際は設定したいもののみ記述してください。

triggerSelector(初期値:'.js-trigger')

ハンバーガーボタンのセレクタ名を変更したい時にこちらを上書きしてください。開閉状況に応じてbodyにis-openというクラスが付与されます。

navSelector(初期値:'.js-nav')

スライドメニュー本体のセレクタ名を変更したい時にこちらを上書きしてください。

coverSelector(初期値:'.js-cover_layer')

カバーレイヤーのセレクタ名を変更したい時にこちらを上書きしてください。

menuCloseText(初期値:'メニューを閉じる')

ハンバーガーボタンのaria-label属性(閉じる場合)のテキストを変更したい場合はこちらを上書きしてください。

menuOpenText(初期値:'メニューを開く')

ハンバーガーボタンのaria-label属性(開く場合)のテキストを変更したい場合はこちらを上書きしてください。

useBreakPoint(初期値:true)

現在ブレイクポイント以上でスライドメニューが無効化されますが、無効化したくない場合にはこちらにfalseを指定してください。

breakpoint(初期値:768)

ここで指定した以上の画面幅(ピクセル)になるとスライドメニューが無効化されます。

duration(初期値:300)

ローダーがフェードアウトするまでの時間です。単位はミリ秒です。

delay(初期値:1000)

ローダーの代わりに初回用画像などを表示したい時に画像を表示しておく時間です。単位はミリ秒です。

sessionStorageKey(初期値:'first_session')

ローダーの代わりに初回用画像などを表示したい時のセッションストレージのキー名です。
変更する場合にはHTMLに記述するスクリプトのキー名と合わせて変更してください(const session = sessionStorage.getItem('first_session');の部分)

loaderBgSelector(初期値:'.js-loader')

ローダー本体のセレクタ名を変更したい時にこちらを上書きしてください。

effectSelector(初期値:'.js-effect')

ローダー消去後にアニメーションなどを設定したい場合に、こちらにセレクタ名を指定してください。ちなみに対象のセレクタにはローダー消去後にis-animationというクラスが付与されます。

effectSelector(初期値:'.js-effect')

ローダー消去後にアニメーションなどを設定したい場合に、こちらにセレクタ名を指定してください。ちなみに対象のセレクタにはローダー消去後にis-animationというクラスが付与されます。

navHeadSelector(初期値:'.js-nav_hd')

アコーディオンヘッダーのセレクタ名を変更したい時にこちらを上書きしてください。開閉状況に応じて自身と開閉されるコンテンツ部分にis-expandedというクラスが付与されます。

navLowerWrapSelector(初期値:'.js-nav_lower_wrap')

アコーディオンのコンテンツ部分のセレクタ名を変更したい時にこちらを上書きしてください。開閉状況に応じて自身とヘッダー部分にis-expandedというクラスが付与されます。

allowMultipleExpanded(初期値:false)

アコーディオンの複数同時オープンを許可するかしないか設定できます。

scrollBehavior(初期値:'smooth')

jabascriptによるスムーススクロールを使用するか設定できます。使用したくない場合には'auto'を指定してください。
なお指定を'auto'に変更し、CSSでscroll-behavior: smoothを指定した場合、仕様の違いによりスムーススクロールの挙動が若干変化します。

adjustSelector(初期値:'.js-adjust')

スムーススクロールしている時に、固定ヘッダー等がある場合にその高さを取得して位置を調整できます。高さが変化する要素でも大丈夫です。

adjustHeight(初期値:0)

スムーススクロールする時にずらす数値を固定にしたい場合、こちらに数値(ピクセル)を入力してください。adjustSelectorがHTML内に見つからない時にこちらの値が参照されますので、HTML内でjs-adjustを使用しないか、全く別のセレクタ名で上書きをすればこちらで指定した数値で固定されます。

adjustPadding(初期値:0)

adjustSelectorに追加でさらにスペースを空けたい場合にはこちらに数値(ピクセル)を入力してください。例えば20と入力すればjs-adjustの数値+20pxずれた位置でスムーススクロールが止まります。

ダウンロード

スクリプトのダウンロードは以下になります。
改変や商用利用は自由にしていただいて構いません。
ただこれ作るのに、めーーーっちゃ時間取られたので、ちょっと有料にさせてください(汗

ちなみにお仕事は絶賛募集中です(笑)。
webデザインでもグラフィックデザインでもディレクションでもコーディングでも講義でもなんでも言ってくださいませ。
なお写真はアクセシビリティに対応したスライドメニューを作ろうとしているデザイナーのイメージです。

ここから先は

79字 / 1ファイル

¥ 2,000

この記事が気に入ったらサポートをしてみませんか?