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

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

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

動画で確認したい場合は、YouTube 版の投稿をご覧ください。

概要

レスポンシブなナビゲーション システムを構築するのは難しいものです。キーボードを使用しているユーザーもいれば、高性能なパソコンを使用しているユーザーもいれば、小さなモバイル デバイスからアクセスしているユーザーもいます。訪問するすべてのユーザーがメニューを開閉できる必要があります。

パソコンからモバイルへのレスポンシブ レイアウトのデモ
iOS と Android でライトモードとダークモードが使用できなくなった

ウェブ戦術

このコンポーネント探索では、次のような重要なウェブ プラットフォーム機能を組み合わせることができました。

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

私のソリューションでは、サイドバーが 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-area 構文を使用して、同じ行または列に複数の要素を割り当てることができます。

スタック

プライマリ レイアウト要素 #sidenav-container は、1 行と 2 列を作成します。各行と列の 1 つは 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> は、サイドナビゲーションを含んでいるアニメーション要素です。2 つの子があります。[nav] という名前のナビゲーション コンテナ <nav> と、メニューの閉じに使用される [escape] という名前の背景 <a> です。

#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;
  }
}
時間の適用ありと適用なしのインタラクションのデモ。

サイドナビがスライド開閉するときに、ユーザーがモーションを減らしたい場合は、要素を即座にビューに移動し、モーションなしで状態を維持します。

遷移、変換、翻訳

サイドナビを閉じる(デフォルト)

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

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

@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 要素が :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 を作成して、自分のバージョンをツイートしてください。下記のコミュニティのリミックス セクションに追加します。

コミュニティ リミックス