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

レスポンシブ スライドアウト サイドナビゲーションの基本的な作成方法

この記事では、レスポンシブでステートフルな、キーボード ナビゲーションをサポートし、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 グリッド

これまでは、絶対位置または固定位置の Sidenav レイアウトとコンポーネントのみを使用していました。一方、グリッドでは 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 に設定します。CSS が要素を -110vw の外側から、URL ハッシュが変更されたときに 0 の「イン」の位置にスライドすることを確認します。var(--duration)

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

コミュニティ リミックス