建立側邊導覽列元件

如何建構回應式滑出側邊導覽列的基本總覽

在這篇文章中,我們想向您介紹如何設計網路的側邊導覽列元件:回應式、有狀態、支援鍵盤導覽、與無 JavaScript 搭配使用,以及跨瀏覽器運作。試試示範

如果你偏好使用影片,也可以觀看這篇 YouTube 文章:

總覽

打造回應式導覽系統並不容易。有些使用者會使用鍵盤,有些則搭載功能強大的電腦,有些則是透過小型行動裝置造訪。所有造訪的人都應該可以開啟及關閉選單。

從電腦到行動裝置的回應式版面配置示範
iOS 和 Android 裝置上的淺色和深色主題

網路戰術

在這項元件探索中,我很享受結合幾個重要的網路平台功能:

  1. CSS :target
  2. CSS 網格
  3. CSS transforms
  4. 適用於可視區域和使用者偏好設定的 CSS 媒體查詢
  5. focus 使用者體驗強化項目 JS

我的解決方案只有一個側欄,而且只能在位於 540px 以下的「行動裝置」可視區域時切換。 540px 將是切換行動互動式版面配置和靜態電腦版面配置的中斷點。

CSS :target 虛擬類別

一個 <a> 連結會將網址雜湊設為 #sidenav-open,另一個連結則為空白 ('')。最後,元素具有 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>

只要點選這些連結,系統就會變更網頁網址的雜湊狀態,並使用虛擬類別顯示並隱藏側邊導覽列:

@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 轉換和轉換

我們的版面配置現在會以行動裝置可視區域大小堆疊。除非新增樣式,否則文章會 依預設疊加在報導上以下是我在下一節要拍攝的使用者體驗:

  • 以動畫呈現開啟和關閉動畫
  • 只有在使用者可以接受動態效果時,才產生動畫效果
  • visibility 製作動畫,讓鍵盤焦點不會進入畫面外元素

開始實作動態動畫時,我想先從無障礙設計開始著手。

無障礙動作

不是所有人都會想滑出動態效果。在我們的解決方案中,您可以調整媒體查詢內的 --duration CSS 變數,套用這項偏好設定。這個媒體查詢值代表使用者的作業系統偏好動作 (如果有的話)。

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

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
未套用時間長度的互動示範。

現在,當我們的側邊導覽列滑動開啟及關閉時,如果使用者偏好減少動作,我會立即將元素移到檢視畫面中,在不顯示動作的情況下維持狀態。

轉換、轉換、翻譯

側邊導覽列 (預設)

如要將行動裝置側邊導覽列的預設狀態設為螢幕外狀態,我可以使用 transform: translateX(-110vw) 放置元素。

請注意,我已在 -100vw 的一般螢幕外程式碼中加入另一個 10vw,確保側邊導覽列的 box-shadow 在隱藏後不會在主要可視區域中顯示。

@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 的位置滑出至 0 的「in」位置,而非 var(--duration)

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

轉場效果

您現在的目標是在螢幕閱讀器關閉時隱藏選單,這樣系統就不會將焦點放在螢幕外的選單中。做法是在 :target 變更時設定瀏覽權限轉換。

  • 進入畫面時,不要轉換顯示設定;必須立即顯示畫面,以便我看到元素滑行,並接受聚焦。
  • 啟動時,轉換瀏覽權限但延遲,因此在轉場效果結束時就會切換為 hidden

提升無障礙使用者體驗

這項解決方案需要變更網址,才能管理狀態。自然,請在此使用 <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 的 Sprinkle

按下 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>

這樣就會在關閉時移除網址記錄項目,這就如同選單從未開啟過。

專注使用者體驗

下一個程式碼片段可協助我們在使用者開啟或關閉按鈕後,將焦點移至開啟和關閉按鈕。我想讓換機毫不費力。

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

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

側邊導覽列開啟時,將焦點移至關閉按鈕。側邊導覽列關閉後 請將焦點移到開啟的按鈕只要在 JavaScript 中對元素呼叫 focus() 即可。

結語

現在你知道我怎麼做,你會怎麼做?因此,這就是有趣的元件架構!究竟誰要製作第 1 版和運算單元?🙂

我們來多方拓展實務方案,並學習運用網路打造服務的所有方式建立 Glitch透過 Twitter 推文告訴我們版本,我會將版本新增至下方的「社群重混」部分。

社群重混作品