レスポンシブなスライドアウト サイド ナビゲーションを作成する方法の基本的な概要
この投稿では、レスポンシブでステートフルで、キーボード ナビゲーションをサポートし、JavaScript の有無にかかわらず動作し、複数のブラウザで機能するウェブ用の Sidenav コンポーネントをどのように作成したかをご紹介します。デモをお試しください。
動画をご覧になる場合は、この投稿の YouTube バージョンをご覧ください。
概要
レスポンシブ ナビゲーション システムの構築は簡単ではありません。キーボードを使用するユーザーもいれば、高性能のパソコンを使用するユーザーもいれば、小型のモバイル デバイスからアクセスするユーザーもいます。アクセスするすべてのユーザーがメニューを開閉できるようにする必要があります。
ウェブ戦術
このコンポーネント データ探索では、ウェブ プラットフォームの重要な機能をいくつか組み合わせてみました。
- CSS
:target
- CSS グリッド
- CSS transforms
- ビューポートとユーザー設定の CSS メディアクエリ
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>
は、サイド ナビゲーションを含むアニメーション要素です。これには、[nav]
という名前のナビゲーション コンテナ <nav>
と、メニューを閉じるために使用される [escape]
という名前の背景 <a>
の 2 つの子があります。
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
2fr
と 1fr
を調整して、メニュー オーバーレイとネガティブ スペースを閉じるボタンの比率を好みます。
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
の外位置から 0
の「in」位置までスライドするのを確認します。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>
これで、メインの操作ボタンに、マウスとキーボードの両方に対する意図が明確に示されるようになりました。
: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 を作成して、そのバージョンをツイートしてください。下のコミュニティ リミックス セクションに追加します。
コミュニティのリミックス
- @_developit: カスタム要素: デモとコード
- HTML/CSS/JS を使用した場合の @mayeedwin1: デモとコード
- @a_nurella と Glitch リミックス: デモとコード
- HTML/CSS/JS を使用した場合の @EvroMalarkey: デモとコード