サイド ナビゲーション コンポーネントの作成

レスポンシブなスライド アウト サイドナビを構築する方法の基本的な概要

この記事では、レスポンシブでステートフルなウェブ用の Sidenav コンポーネントをプロトタイプ作成する方法を紹介します。このコンポーネントは、キーボード ナビゲーションをサポートし、JavaScript の有無にかかわらず動作し、ブラウザ間で動作します。デモをお試しください。

動画でご覧になりたい場合は、こちらの YouTube 版をご覧ください。

概要

レスポンシブ ナビゲーション システムの構築は難しいものです。キーボードを使用しているユーザーもいれば、高性能なデスクトップを使用しているユーザーもいます。また、小型のモバイル デバイスからアクセスしているユーザーもいます。すべての訪問者がメニューを開閉できるようにします。

パソコンからモバイルへのレスポンシブ レイアウトのデモ
iOS と Android でライトモードとダークモードがダウンしている

ウェブ戦術

このコンポーネントの調査では、いくつかの重要なウェブ プラットフォーム機能を組み合わせることができました。

  1. CSS :target
  2. CSS grid
  3. CSS の変換
  4. ビューポートとユーザー設定の CSS メディアクエリ
  5. focus UX の機能強化のための JS

私のソリューションにはサイドバーが 1 つあり、540px 以下の「モバイル」ビューポートでのみ切り替わります。540px は、モバイルのインタラクティブ レイアウトとパソコンの静的レイアウトを切り替えるためのブレークポイントになります。

CSS :target 疑似クラス

1 つの <a> リンクは URL ハッシュを #sidenav-open に設定し、もう 1 つは空('')に設定します。最後に、ハッシュに一致する id を持つ要素があります。

<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<aside id="sidenav-open">
  …
</aside>

これらのリンクをクリックすると、ページ URL のハッシュ状態が変更され、疑似クラスを使用してサイドナビの表示と非表示を切り替えます。

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
  }

  #sidenav-open:target {
    visibility: visible;
  }
}

CSS グリッド

以前は、絶対位置または固定位置のサイドナビ レイアウトとコンポーネントのみを使用していました。ただし、Grid では grid-area 構文を使用して、複数の要素を同じ行または列に割り当てることができます。

スタック

プライマリ レイアウト要素 #sidenav-container は、1 行 2 列のグリッドで、それぞれ stack という名前が付けられています。スペースが制約されている場合、CSS は <main> 要素のすべての子を同じグリッド名に割り当て、すべての要素を同じスペースに配置してスタックを作成します。

#sidenav-container {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;
  min-height: 100vh;
}

@media (max-width: 540px) {
  #sidenav-container > * {
    grid-area: stack;
  }
}

<aside> は、サイド ナビゲーションを含むアニメーション要素です。このタグには、ナビゲーション コンテナ <nav>[nav] という名前)と、メニューを閉じるために使用されるバックドロップ <a>[escape] という名前)の 2 つの子があります。

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

2fr1fr を調整して、メニュー オーバーレイとそのネガティブ スペースの閉じるボタンに最適な比率を見つけます。

比率を変更した場合のデモ。

CSS 3D 変換とトランジション

レイアウトがモバイル ビューポート サイズで積み重ねられるようになりました。新しいスタイルを追加するまでは、デフォルトで記事がオーバーレイ表示されます。次のセクションでは、以下のような UX を目指します。

  • 開閉のアニメーション
  • ユーザーが同意した場合のみ、モーションでアニメーション表示する
  • キーボード フォーカスがオフスクリーン要素に入らないように visibility をアニメーション化する

モーション アニメーションの実装を開始するにあたり、アクセシビリティを最優先に考えています。

ユーザー補助の動き

スライドアウト モーション エクスペリエンスを望むユーザーばかりではありません。このソリューションでは、メディアクエリ内の --duration CSS 変数を調整することで、この設定が適用されます。このメディアクエリ値は、モーションに関するユーザーのオペレーティング システムの設定を表します(利用可能な場合)。

#sidenav-open {
  --duration: .6s;
}

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
期間を適用した場合と適用しない場合のインタラクションのデモ。

これで、サイドナビが開閉するときに、ユーザーがモーションの低減を希望している場合は、モーションなしで状態を維持しながら、要素をすぐにビューに移動できます。

移行、変換、翻訳

Sidenav out(デフォルト)

モバイル デバイスでサイドナビのデフォルトの状態をオフスクリーン状態にするため、transform: translateX(-110vw) を使用して要素を配置します。

サイドナビの box-shadow が非表示のときにメインのビューポートに表示されないように、通常のオフスクリーン コード -100vw10vw を追加しました。

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);
  }
}
Sidenav in

#sidenav 要素が :target と一致したら、translateX() の位置をホームベースの 0 に設定し、URL ハッシュが変更されたときに、CSS が要素を -110vw の外側の位置から var(--duration) を経由して 0 の内側の位置にスライドさせる様子を観察します。

@media (max-width: 540px) {
  #sidenav-open:target {
    visibility: visible;
    transform: translateX(0);
    transition:
      transform var(--duration) var(--easeOutExpo);
  }
}

移行の可視性

このメニューが画面外にあるときにスクリーン リーダーから隠し、システムが画面外のメニューにフォーカスを当てないようにすることが、今回の目標です。これを実現するために、:target が変更されたときに可視性のトランジションを設定します。

  • 表示する際は、可視性を切り替えないでください。すぐに表示して、要素がスライドインしてフォーカスを受け入れるのを確認できるようにしてください。
  • アウト時は、可視性を遷移させますが、遅延させることで、アウト遷移の最後に hidden に切り替わります。

ユーザー補助機能の UX の強化

このソリューションは、状態を管理するために URL を変更することに依存しています。当然ながら、ここでは <a> 要素を使用する必要があります。そうすることで、アクセシビリティ機能が無料で利用できるようになります。インタラクティブな要素に、意図を明確に伝えるラベルを付けましょう。

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
  <svg>...</svg>
</a>
ナレーションとキーボード操作の UX のデモ。

これで、マウスとキーボードの両方で、主な操作ボタンの意図が明確に示されるようになりました。

:is(:hover, :focus)

この便利な CSS 関数疑似セレクタを使用すると、ホバー スタイルをフォーカスと共有することで、ホバー スタイルをすばやく包括的に設定できます。

.hamburger:is(:hover, :focus) svg > line {
  stroke: hsl(var(--brandHSL));
}

JavaScript を追加する

escape を押して閉じます

キーボードの Escape キーでメニューを閉じられるはずですよね?これを配線しましょう。

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
ブラウザの履歴

開閉操作によってブラウザの履歴に複数のエントリが追加されるのを防ぐため、次の JavaScript をインラインで閉じるボタンに追加します。

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>

これにより、閉じたときに URL 履歴エントリが削除され、メニューが開かれなかったかのように動作します。

フォーカス UX

次のスニペットは、開閉ボタンが開閉した後にフォーカスを当てるのに役立ちます。切り替えを簡単にしたい。

sidenav.addEventListener('transitionend', e => {
  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
      ? document.querySelector('#sidenav-close').focus()
      : document.querySelector('#sidenav-button').focus();
})

サイドナビが開いたら、閉じるボタンにフォーカスします。サイドナビが閉じたら、開くボタンにフォーカスします。これを行うには、JavaScript で要素の focus() を呼び出します。

まとめ

私がどのようにして達成したかをご理解いただけたかと思います。では、あなたならどうしますか?これは、楽しいコンポーネント アーキテクチャになります。スロット付きの最初のバージョンを作成するのは誰ですか?🙂

アプローチを多様化し、ウェブで構築するすべての方法を学びましょう。Glitch を作成して、そのバージョンを ツイートしてください。下のコミュニティ リミックス セクションに追加します。

コミュニティ リミックス