Xây dựng bảng điều hướng chính cho trang web

Hướng dẫn này mô tả cách tạo thành phần điều hướng chính dễ tiếp cận của một trang web. Bạn sẽ tìm hiểu về HTML ngữ nghĩa, khả năng hỗ trợ tiếp cận và lý do việc sử dụng các thuộc tính của ARIA đôi khi có thể gây hại nhiều hơn lợi ích.

Manuel Matuzović
Manuel Matuzović

Có nhiều cách để tạo thành phần điều hướng chính của trang web, về mặt kiểu, chức năng cũng như mã đánh dấu và thông tin ngữ nghĩa cơ bản. Nếu cách triển khai quá tối giản, cách triển khai này phù hợp với hầu hết mọi người, nhưng trải nghiệm người dùng (UX) có thể không được tốt. Nếu dùng kỹ thuật quá mức, ứng dụng này có thể khiến người dùng bối rối hoặc thậm chí cản trở họ truy cập vào ứng dụng đó.

Đối với hầu hết các trang web, bạn nên tạo một thứ gì đó không quá đơn giản cũng không quá phức tạp.

Xây dựng theo từng lớp

Trong hướng dẫn này, bạn bắt đầu từ một thiết lập cơ bản và thêm đối tượng theo từng lớp cho đến khi bạn cung cấp vừa đủ thông tin, kiểu và chức năng để làm hài lòng hầu hết người dùng. Để làm được điều đó, bạn sử dụng nguyên tắc nâng cao tăng dần, trong đó nêu rằng bạn bắt đầu bằng giải pháp cơ bản và mạnh mẽ nhất, sau đó thêm dần các lớp chức năng. Nếu một lớp không hoạt động vì lý do nào đó, điều hướng sẽ vẫn hoạt động vì lớp này sẽ nhẹ nhàng quay lại lớp bên dưới.

Cấu trúc cơ bản

Đối với một thao tác điều hướng cơ bản, bạn cần có 2 yếu tố: phần tử <a> và một vài dòng CSS để cải thiện kiểu và bố cục mặc định cho các đường liên kết.

<a href="/home">Home</a>
<a href="/about-us">About us</a>
<a href="/pricing">Pricing</a>
<a href="/contact">Contact</a>
/* Define variables for your colors */
:root {
  --color-shades-dark: rgb(25, 25, 25);
}

/* Use the alternative box model
Details: <https://web.dev/learn/css/box-model/> */
*{
  box-sizing: border-box;
}

/* Basic font styling */
body {
  font-family: Segoe UI, system-ui, -apple-system, sans-serif;
  font-size: 1.6rem;
}

/* Link styling */
a {
  --text-color: var(--color-shades-dark);
  border-block-end: 3px solid var(--border-color, transparent);
  color: var(--text-color);
  display: inline-block;
  margin-block-end: 0.5rem; /* See note at the bottom of this chapter */
  margin-inline-end: 0.5rem;
  padding: 0.1rem;
  text-decoration: none;
}

/* Change the border-color on :hover and :focus */
a:where(:hover, :focus) {
  --border-color: var(--text-color);
}
Xem Bước 1: HTML và CSS cơ bản" trên CodePen.

Tính năng này phù hợp với hầu hết người dùng, bất kể họ truy cập trang web bằng cách nào. Bạn có thể thao tác bằng chuột, bàn phím, thiết bị cảm ứng hoặc trình đọc màn hình, nhưng vẫn có thể cải thiện. Bạn có thể nâng cao trải nghiệm bằng cách mở rộng mẫu cơ bản này với chức năng và thông tin bổ sung.

Sau đây là những việc bạn có thể làm:

  • Đánh dấu trang đang hoạt động.
  • Thông báo số lượng mục cho người dùng trình đọc màn hình.
  • Thêm mốc và cho phép người dùng trình đọc màn hình truy cập trực tiếp vào phần điều hướng bằng phím tắt.
  • Ẩn thành phần điều hướng trên các khung nhìn hẹp.
  • Cải thiện kiểu lấy nét.

Đánh dấu trang đang hoạt động

Để đánh dấu trang đang hoạt động, bạn có thể thêm lớp học vào đường liên kết tương ứng.

<a href="/about-us" class="active-page">About us</a>

Vấn đề của phương pháp này là nó truyền tải thông tin liên kết chỉ hoạt động thông qua hình ảnh. Người dùng trình đọc màn hình khiếm thị không thể phân biệt giữa trang đang hoạt động và các trang khác. May mắn là tiêu chuẩn Ứng dụng Internet đa dạng thức dễ tiếp cận (ARIA) cũng cung cấp một cách truyền đạt thông tin này về mặt ngữ nghĩa. Sử dụng thuộc tính và giá trị aria-current="page" thay vì một lớp.

aria-current (trạng thái) cho biết phần tử đại diện cho mục hiện tại trong một vùng chứa hoặc tập hợp các phần tử liên quan. Mã thông báo trang được dùng để cho biết một đường liên kết trong một nhóm đường liên kết phân trang, nơi đường liên kết đó được tạo kiểu trực quan để đại diện cho trang đang hiển thị. [Ứng dụng Internet đa dạng thức dễ tiếp cận (WAI-ARIA) 1.1](https://www.w3.org/TR/wai-aria/#aria-current)

Với thuộc tính bổ sung này, trình đọc màn hình giờ đây sẽ thông báo các nội dung như "trang hiện tại, đường liên kết, Giới thiệu" thay vì chỉ "đường liên kết, Giới thiệu về chúng tôi".

<a href="/about-us" aria-current="page" class="active-page">About us</a>

Một hiệu ứng phụ thuận tiện là bạn có thể sử dụng thuộc tính này để chọn đường liên kết đang hoạt động trong CSS, khiến lớp active-page trở nên lỗi thời.

<a href="/home">Home</a>
<a href="/about-us" aria-current="page">About us</a>
<a href="/pricing">Pricing</a>
<a href="/contact">Contact</a>
/* Change border-color and color for the active page */
[aria-current="page"] {
  --border-color: var(--color-highlight);
  --text-color: var(--color-highlight);
}
Xem Bước 2: Đánh dấu trang đang hoạt động trên CodePen.

Thông báo số lượng mặt hàng

Bằng cách nhìn vào bảng điều hướng, người dùng bình thường có thể biết rằng quảng cáo chỉ chứa bốn đường liên kết. Người dùng trình đọc màn hình khiếm thị không thể lấy thông tin này một cách nhanh chóng. Có thể họ sẽ phải xử lý toàn bộ danh sách đường liên kết. Điều này có thể không gây ra vấn đề gì nếu danh sách ngắn như trong ví dụ này, nhưng nếu danh sách chứa 40 đường liên kết thì thao tác này có thể rườm rà. Nếu người dùng trình đọc màn hình biết trước rằng điều hướng có chứa nhiều đường liên kết, thì họ có thể quyết định sử dụng cách điều hướng khác hiệu quả hơn, như tìm kiếm trên trang web.
Một cách hiệu quả để thông báo trước số lượng mục là gói từng đường liên kết trong một mục trong danh sách (<li>), lồng trong một danh sách không theo thứ tự (<ul>).

<ul>
  <li>
     <a href="/home">Home</a>
  </li>
  <li>
    <a href="/about-us" aria-current="page">About us</a>
  </li>
  <li>
    <a href="/pricing">Pricing</a>
  </li>
  <li>
    <a href="/contact">Contact</a>
  </li>
</ul>

Khi người dùng trình đọc màn hình tìm thấy danh sách, phần mềm của họ sẽ thông báo những nội dung như "danh sách, 4 mục".

Dưới đây là bản minh hoạ thao tác được sử dụng với trình đọc màn hình NVDA trên Windows.

Bây giờ, bạn phải điều chỉnh kiểu để giống như trước đây.

/* Remove the default list styling and create a flexible layout for the list */
ul {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Basic link styling */
a {
  --text-color: var(--color-shades-dark);

  border-block-end: 3px solid var(--border-color, transparent);
  color: var(--text-color);
  padding: 0.1rem;
  text-decoration: none;
}

Việc sử dụng danh sách có thể có nhiều lợi ích cho người dùng trình đọc màn hình:

  • Người dùng có thể biết tổng số mặt hàng trước khi tương tác với các mặt hàng đó.
  • Người dùng có thể sử dụng phím tắt để chuyển từ mục danh sách sang mục danh sách.
  • Họ có thể sử dụng phím tắt để chuyển từ danh sách này sang danh sách khác.
  • Trình đọc màn hình có thể thông báo chỉ mục của mục hiện tại (ví dụ: "mục danh sách, hai trên bốn").

Hơn nữa, nếu trang được hiển thị mà không có CSS, danh sách sẽ hiển thị các liên kết dưới dạng một nhóm các mục nhất quán thay vì chỉ một loạt các liên kết.

Một chi tiết đáng chú ý về VoiceOver trong Safari là bạn sẽ mất tất cả các ưu điểm này khi đặt list-style: none. Điều này là do thiết kế. Nhóm WebKit đã quyết định xoá ngữ nghĩa của danh sách khi danh sách không giống như danh sách. Tuỳ thuộc vào mức độ phức tạp của thao tác, điều này có thể là vấn đề hoặc không. Một mặt, thao tác điều hướng vẫn có thể sử dụng được và chỉ ảnh hưởng đến VoiceOver trong Safari. VoiceOver với Chrome hoặc Firefox vẫn thông báo số lượng mục cũng như các trình đọc màn hình khác, chẳng hạn như NVDA. Mặt khác, thông tin ngữ nghĩa có thể thực sự hữu ích trong một số trường hợp. Để đưa ra quyết định đó, bạn nên thử nghiệm cách điều hướng với người dùng trình đọc màn hình thực tế và nhận ý kiến phản hồi của họ. Nếu quyết định rằng mình cần VoiceOver trong Safari để hoạt động như tất cả các trình đọc màn hình khác, bạn có thể giải quyết vấn đề này bằng cách đặt rõ ràng vai trò trong danh sách ARIA trên <ul>. Việc này sẽ hoàn nguyên hành vi về trạng thái trước khi bạn xoá kiểu danh sách. Rõ ràng danh sách vẫn giữ nguyên.

<ul role="list">
  <li>
     <a href="/home">Home</a>
  </li>
  ...
</ul>
Xem Bước 3: Thông báo số lượng mục trên CodePen.

Thêm mốc

Chỉ với chút nỗ lực, bạn đã có những cải tiến tuyệt vời cho người dùng trình đọc màn hình, nhưng bạn còn có thể làm một việc nữa. Điều hướng về mặt ngữ nghĩa vẫn chỉ là một danh sách các đường liên kết và thật khó để nhận biết danh sách cụ thể này là thành phần điều hướng chính trên trang web của bạn. Bạn có thể chuyển danh sách thông thường này thành danh sách điều hướng bằng cách gói <ul> trong phần tử <nav>.

Việc sử dụng phần tử <nav> có một số lợi ích. Đáng chú ý, trình đọc màn hình sẽ thông báo các thông báo như "điều hướng", khi người dùng tương tác với trình đọc màn hình và thêm một điểm đánh dấu vào trang. Mốc là các vùng đặc biệt trên trang, chẳng hạn như <header>, <footer> hoặc <main> mà trình đọc màn hình có thể chuyển đến. Việc tạo các điểm mốc trên một trang có thể hữu ích, vì điều này cho phép người dùng trình đọc màn hình truy cập trực tiếp vào các khu vực quan trọng trên trang mà không cần phải tương tác với phần còn lại của trang. Ví dụ: bạn có thể chuyển từ mốc này sang mốc khác bằng cách nhấn phím D trong NVDA. Trong Giọng lồng tiếng, bạn có thể sử dụng rô-to để liệt kê tất cả các điểm mốc trên trang bằng cách nhấn VO + U.

Danh sách bốn điểm mốc: biểu ngữ, thanh điều hướng, chính, thông tin nội dung.
Rotor trong VoiceOver liệt kê tất cả các địa danh trên một trang.

Trong danh sách này, bạn sẽ thấy 4 mốc: banner là phần tử <header>, navigation<nav>, chính phần tử <main>thông tin nội dung<footer>. Danh sách này không nên quá dài, bạn thực sự chỉ muốn đánh dấu những phần quan trọng trên giao diện người dùng là điểm mốc, chẳng hạn như tìm kiếm trên trang web, điều hướng cục bộ hoặc phân trang.

Nếu bạn có thành phần điều hướng trên toàn trang web, thành phần điều hướng cục bộ cho trang và lệnh phân trang trên một trang, thì có thể bạn cũng có 3 phần tử <nav>. Điều đó là bình thường, nhưng giờ đây có 3 mốc điều hướng và về mặt ngữ nghĩa, tất cả các mốc này đều trông giống nhau. Rất khó để phân biệt các trang này với nhau trừ phi bạn hiểu rõ cấu trúc của trang.

Hình ảnh cho thấy 3 điểm mốc đều có nội dung &quot;chỉ đường&quot;.
Rô-to trong VoiceOver liệt kê 3 mốc điều hướng chưa được gắn nhãn.

Để dễ phân biệt, bạn nên gắn nhãn cho các thành phần đó bằng cách sử dụng aria-labelledby hoặc aria-label.

<nav aria-label="Main">
    <ul>
      <li>
         <a href="/home">Home</a>
      </li>
      ...
  </ul>
</nav>
...
<nav aria-label="Select page">
    <ul>
      <li>
         <a href="/page-1">1</a>
      </li>
      ...
    </ul>
</nav>

Nếu nhãn bạn chọn đã tồn tại ở đâu đó trên trang, bạn có thể sử dụng aria-labelledby và tham chiếu đến nhãn hiện có bằng thuộc tính id.

<nav aria-labelledby="pagination_heading">
  <h2 id="pagination_heading">Select a page</h2>
  <ul>
    <li>
       <a href="/page-1">1</a>
    </li>
    ...
  </ul>
</nav>

Chỉ cần một nhãn ngắn gọn là đủ, đừng quá dài dòng. Hãy bỏ qua các biểu thức như "di chuyển" hoặc "trình đơn" vì trình đọc màn hình đã cung cấp cho người dùng thông tin này.

Địa danh
VoiceOver sẽ liệt kê các mốc "biểu ngữ", "trình điều hướng chính", "chính", "điều hướng trang", "chọn điều hướng trang" và "thông tin nội dung".
Xem Bước 4: Thêm một mốc trên CodePen.

Ẩn điều hướng trên các khung nhìn hẹp

Cá nhân tôi không thích việc ẩn thanh điều hướng chính trên các khung nhìn hẹp, nhưng nếu danh sách các đường liên kết quá dài thì không có cách nào để ẩn. Trong trường hợp đó, thay vì danh sách, người dùng sẽ thấy một nút có nhãn "Trình đơn" hoặc biểu tượng bánh mì kẹp thịt hoặc một tổ hợp. Nhấp vào nút này sẽ hiển thị và ẩn danh sách. Nếu bạn biết JavaScript và CSS cơ bản, đó là một nhiệm vụ khả thi, nhưng bạn phải quan tâm đến một số vấn đề về trải nghiệm người dùng và khả năng tiếp cận.

  • Bạn phải ẩn danh sách theo cách dễ truy cập.
  • Bạn phải hỗ trợ tiếp cận bằng bàn phím để điều hướng.
  • Thanh điều hướng phải cho biết liệu có hiển thị hay không.

Thêm nút bánh mì kẹp

Vì bạn đang làm theo nguyên tắc nâng cao tăng dần, bạn muốn đảm bảo rằng thành phần điều hướng của bạn vẫn hoạt động và hợp lý ngay cả khi JavaScript tắt.
Điều đầu tiên bạn cần làm để điều hướng là một nút bánh mì kẹp thịt. Bạn tạo mã đó trong HTML trong phần tử mẫu, sao chép mã trong JavaScript và thêm mã vào điều hướng.

Một trang hiển thị nút bánh mì kẹp thịt.
Kết quả: Thay vì các đường liên kết, thanh điều hướng sẽ hiển thị một nút bánh mì kẹp trên các khung nhìn hẹp.
<nav id="mainnav">
  ...
</nav>

<template id="burger-template">
  <button type="button" aria-expanded="false" aria-label="Menu" aria-controls="mainnav">
    <svg width="24" height="24" aria-hidden="true">
      <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z">
    </svg>
  </button>
</template>
  1. Thuộc tính aria-expanded cho phần mềm trình đọc màn hình biết liệu phần tử điều khiển nút có được mở rộng hay không.
  2. aria-label đặt cho nút này một tên thành phần hỗ trợ tiếp cận, đây là một văn bản thay thế cho biểu tượng bánh mì kẹp thịt.
  3. Bạn ẩn <svg> khỏi công nghệ hỗ trợ bằng cách sử dụng aria-hidden vì công nghệ này đã có nhãn văn bản do aria-label cung cấp.
  4. aria-controls cho công nghệ hỗ trợ biết có hỗ trợ thuộc tính nào (ví dụ: JAWS) và thành phần nào cần điều khiển nút.
const nav = document.querySelector('#mainnav')
const list = nav.querySelector('ul');
const burgerClone = document.querySelector('#burger-template').content.cloneNode(true);
const button = burgerClone.querySelector('button');

// Toggle aria-expanded attribute
button.addEventListener('click', e => {
  // aria-expanded="true" signals that the menu is currently open
  const isOpen = button.getAttribute('aria-expanded') === "true"
  button.setAttribute('aria-expanded', !isOpen);
});

// Hide list on keydown Escape
nav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    button.setAttribute('aria-expanded', false);
  }
});

// Add the button to the page
nav.insertBefore(burgerClone, list);
  1. Thật thuận tiện, người dùng có thể đóng trình đơn điều hướng bất cứ khi nào họ muốn, chẳng hạn như bằng cách nhấn phím Escape.
  2. Bạn cần sử dụng insertBefore thay vì appendChild vì nút này phải là phần tử đầu tiên trong thành phần điều hướng. Nếu người dùng bàn phím hoặc trình đọc màn hình nhấn Tab sau khi nhấp vào nút, họ muốn lấy tiêu điểm vào mục đầu tiên trong danh sách. Nếu nút này xuất hiện sau danh sách, thì điều đó không xảy ra.

Tiếp theo, bạn đặt lại kiểu mặc định của nút và đảm bảo rằng nút này chỉ hiển thị trên khung nhìn hẹp.

@media (min-width: 48em) {
  nav {
    --nav-button-display: none;
  }
}

/* Reset button styling */
button {
  all: unset;
  display: var(--nav-button-display, flex);
}
Xem Bước 5: Thêm nút bánh mì kẹp thịt trên CodePen.

Đang ẩn danh sách

Trước khi ẩn danh sách, hãy định vị và tạo kiểu điều hướng và danh sách để bố cục được tối ưu hoá cho các khung nhìn hẹp nhưng vẫn trông đẹp trên màn hình lớn hơn.
Trước tiên, hãy loại bỏ <nav> khỏi luồng tự nhiên của trang và đặt thẻ này ở góc trên cùng của khung nhìn.

@media (min-width: 48em) {
  nav {
    --nav-button-display: none;
    --nav-position: static;
  }
}

nav {
  position: var(--nav-position, fixed);
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
}

Tiếp theo, hãy thay đổi bố cục trên các khung nhìn hẹp bằng cách thêm thuộc tính tuỳ chỉnh mới (—-nav-list-layout). Theo mặc định, bố cục là cột và chuyển sang hàng trên màn hình lớn hơn.

@media (min-width: 48em) {
  nav {
    --nav-button-display: none;
    --nav-position: static;
  }

  ul {
    --nav-list-layout: row;
  }
}

ul {
  display: flex;
  flex-direction: var(--nav-list-layout, column);
  flex-wrap: wrap;
  gap: 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

Thành phần điều hướng của bạn sẽ có dạng như sau trên các khung nhìn hẹp.

Trang hiển thị danh sách điều hướng và nút bánh mì kẹp thịt.
Cả nút bánh mì kẹp thịt và danh sách đều được đặt ở góc trên cùng của khung nhìn.

Danh sách này rõ ràng cần một số CSS. Chúng ta sẽ di chuyển khung này lên góc trên cùng, lấp đầy toàn bộ màn hình theo chiều dọc, áp dụng background-colorbox-shadow.

@media (min-width: 48em) {
  nav {
    --nav-button-display: none;
    --nav-position: static;
  }
  
  ul {
    --nav-list-layout: row;
    --nav-list-position: static;
    --nav-list-padding: 0;
    --nav-list-height: auto;
    --nav-list-width: 100%;
    --nav-list-shadow: none;
  }
}

ul {
  background: rgb(255, 255, 255);
  box-shadow: var(--nav-list-shadow, -5px 0 11px 0 rgb(0 0 0 / 0.2));
  display: flex;
  flex-direction: var(--nav-list-layout, column);
  flex-wrap: wrap;
  gap: 1rem;
  height: var(--nav-list-height, 100vh);
  list-style: none;
  margin: 0;
  padding: var(--nav-list-padding, 2rem);
  position: var(--nav-list-position, fixed);
  inset-block-start: 0; /* Logical property. Equivalent to top: 0; */
  inset-inline-end: 0; /* Logical property. Equivalent to right: 0; */
  width: var(--nav-list-width, min(22rem, 100vw));
}

button {
  all: unset;
  display: var(--nav-button-display, flex);
  position: relative;
  z-index: 1;
}

Danh sách sẽ có dạng như sau trên các khung nhìn hẹp, giống như thanh bên hơn là danh sách đơn giản.

Danh sách điều hướng sẽ mở ra.

Cuối cùng, hãy ẩn danh sách, chỉ hiển thị danh sách khi người dùng nhấp vào nút một lần và ẩn danh sách khi họ nhấp lại. Bạn chỉ nên ẩn danh sách chứ không ẩn toàn bộ thành phần điều hướng, vì việc ẩn thành phần điều hướng cũng có nghĩa là ẩn một điểm mốc quan trọng.

Trước đó, bạn đã thêm một sự kiện nhấp chuột vào nút để chuyển đổi giá trị của thuộc tính aria-expanded. Bạn có thể dùng thông tin đó làm điều kiện để hiện và ẩn danh sách trong CSS.

@media (min-width: 48em) {
  ul {
    --nav-list-visibility: visible;
  }
}

ul {
  visibility: var(--nav-list-visibility, visible);
}

/* Hide the list on narrow viewports, if it comes after an element with
   aria-expanded set to "false". */
[aria-expanded="false"] + ul {
  visibility: var(--nav-list-visibility, hidden);
}

Bạn cần sử dụng phần khai báo thuộc tính như visibility: hidden hoặc display: none thay vì opacity: 0 hoặc translateX(100%) để ẩn danh sách. Những thuộc tính này đảm bảo rằng các đường liên kết sẽ không thể làm tâm điểm khi điều hướng bị ẩn. Việc sử dụng opacity hoặc translate sẽ xoá nội dung trực quan nên các đường liên kết sẽ ẩn đi nhưng vẫn có thể truy cập được bằng bàn phím. Điều này sẽ gây nhầm lẫn và khó chịu. Việc sử dụng visibility hoặc display sẽ ẩn nội dung đó về mặt trực quan và khiến không thể truy cập được, do đó, sẽ ẩn nội dung đó đối với tất cả người dùng.

Xem Bước 6: Ẩn danh sách.

Tạo ảnh động cho danh sách

Nếu bạn đang tự hỏi tại sao nên sử dụng visibility: hidden; thay vì display: none;, thì đó là vì bạn có thể tạo ảnh động cho chế độ hiển thị. Thuộc tính này chỉ có hai trạng thái là hiddenvisible, nhưng bạn có thể kết hợp với một thuộc tính khác như transform hoặc opacity để tạo hiệu ứng trượt hoặc hiệu ứng mờ dần. Phương thức này không hoạt động với màn hình: không có vì thuộc tính hiển thị không thể tạo ảnh động.

CSS sau đây chuyển đổi opacity để tạo hiệu ứng mờ dần và mờ dần.

ul {
  transition: opacity 0.6s linear, visibility 0.3s linear;
  visibility: var(--nav-list-visibility, visible);
}

[aria-expanded="false"] + ul {
  opacity: 0;
  visibility: var(--nav-list-visibility, hidden);
}

Nếu muốn tạo ảnh động cho chuyển động, bạn nên cân nhắc việc gói thuộc tính transition trong truy vấn nội dung nghe nhìn ưu tiên-reduced-motion vì ảnh động có thể gây ra hiện tượng nai bụng, chóng mặt và đau đầu ở một số người dùng.

ul {
  visibility: var(--nav-list-visibility, visible);
}

@media (prefers-reduced-motion: no-preference) {
  ul {
    transition: transform 0.6s cubic-bezier(.68,-0.55,.27,1.55), visibility 0.3s linear;
  }
}

[aria-expanded="false"] + ul {
  transform: var(--nav-list-transform, translateX(100%));
  visibility: var(--nav-list-visibility, hidden);
}

Điều này đảm bảo rằng chỉ những người không thích việc giảm chuyển động mới thấy ảnh động.

Xem Bước 7: Tạo ảnh động cho danh sách trên CodePen.

Cải thiện kiểu lấy nét

Người dùng bàn phím dựa vào kiểu tiêu điểm của các thành phần để hướng và điều hướng trên trang. Các kiểu tiêu điểm mặc định sẽ tốt hơn là không có kiểu tiêu điểm nào (điều này xảy ra nếu bạn đặt outline: none), nhưng việc có các kiểu tiêu điểm tuỳ chỉnh hiển thị rõ ràng hơn sẽ cải thiện trải nghiệm người dùng.

Dưới đây là giao diện của kiểu tiêu điểm mặc định trên đường liên kết trong Chrome 103.

Đường viền 2px màu xanh dương xung quanh một đường liên kết được lấy tiêu điểm trong Chrome 103.

Bạn có thể cải thiện điều đó bằng cách cung cấp kiểu của riêng bạn bằng màu sắc riêng. Bằng cách sử dụng :focus-visible thay vì :focus, bạn để trình duyệt quyết định thời điểm thích hợp để hiển thị kiểu tiêu điểm. Mọi người, dùng chuột, bàn phím và thao tác chạm sẽ thấy các kiểu :focus, bất kể họ có cần các kiểu đó hay không. Với :focus-visible, trình duyệt sẽ sử dụng các thông tin phỏng đoán nội bộ để quyết định xem chỉ hiển thị chúng cho người dùng bàn phím hay cho mọi người.

/* Remove the default :focus outline */
*:focus {
  outline: none;
}

/* Show a custom outline on :focus-visible */
*:focus-visible {
  outline: 2px solid var(--color-shades-dark);
  outline-offset: 4px;
}

Hỗ trợ trình duyệt cho :focus-visible

Hỗ trợ trình duyệt

  • 86
  • 86
  • 85
  • 15,4

Nguồn

Đường viền 2px màu tối, có thể nhìn thấy rõ ràng với khoảng cách bên trong.

Có nhiều cách để làm nổi bật các mục khi chúng được lấy tiêu điểm. Bạn nên sử dụng thuộc tính outline vì thuộc tính này không làm hỏng bố cục, có thể xảy ra với border và hoạt động tốt với chế độ tương phản cao trên Windows. Các thuộc tính không hoạt động tốt là background-color hoặc box-shadow, vì các thuộc tính này có thể không hiển thị với chế độ cài đặt độ tương phản tuỳ chỉnh.

Một trang web có nền tối với tiêu điểm được làm nổi bật bằng màu tím.
Xem Bước 8: Cải thiện kiểu lấy nét trên CodePen.

Xin chúc mừng! Bạn đã xây dựng một trình điều hướng chính được cải tiến dần, phong phú về ngữ nghĩa, dễ truy cập và thân thiện với thiết bị di động.

Luôn có những điểm cần cải thiện, ví dụ:

Nếu bạn còn nhớ cách bài viết này bắt đầu, với mục tiêu rằng giải pháp này "không quá đơn giản hoặc quá phức tạp", đó là chúng ta hiện tại. Tuy nhiên, bạn vẫn có thể thiết kế sơ đồ điều hướng.

Có sự khác biệt rõ ràng giữa điều hướng và trình đơn. Thao tác điều hướng là tập hợp các đường liên kết để di chuyển trong các tài liệu liên quan. Trình đơn là tập hợp các thao tác để thực hiện trong một tài liệu. Đôi khi, những công việc này sẽ chồng chéo nhau. Bạn có thể có một thành phần điều hướng bao gồm một nút để thực hiện một thao tác (như mở cửa sổ phụ) hoặc có một trình đơn chứa thao tác điều hướng đến một trang khác, chẳng hạn như trang trợ giúp. Trong trường hợp này, bạn không nên kết hợp các vai trò ARIA mà hãy xác định mục đích chính của thành phần và chọn mã đánh dấu tương ứng với các vai trò tương ứng.

Phần tử <nav> có vai trò điều hướng ngầm ẩn của ARIA đủ để cho biết rằng phần tử này là một thành phần điều hướng, nhưng bạn thường thấy các trang web cũng sử dụng trình đơn, thanh trình đơn và mục trong trình đơn. Vì đôi khi chúng tôi có thể sử dụng các thuật ngữ này thay thế cho nhau, nên việc kết hợp chúng để cải thiện trải nghiệm cho người dùng trình đọc màn hình có thể hợp lý. Trước khi tìm hiểu lý do dẫn đến trường hợp này, hãy cùng xem định nghĩa chính thức về các vai trò này.

Vai trò điều hướng

Tập hợp các phần tử điều hướng (thường là các đường liên kết) để điều hướng trong tài liệu hoặc các tài liệu liên quan.

navigation (role) WAI-ARIA 1.1

Vai trò trên trình đơn

Trình đơn thường là danh sách các thao tác hoặc chức năng phổ biến mà người dùng có thể gọi. Vai trò trình đơn thích hợp khi danh sách các mục trong trình đơn được hiển thị theo cách tương tự như một trình đơn trên ứng dụng dành cho máy tính.

trình đơn (vai trò) WAI-ARIA 1.1

Vai trò trong thanh trình đơn

Bản trình bày thực đơn thường hiển thị và thường hiển thị theo chiều ngang. Vai trò thanh trình đơn được dùng để tạo thanh trình đơn tương tự như trong các ứng dụng dành cho máy tính Windows, Mac và Gnome. Thanh trình đơn được dùng để tạo một tập hợp nhất quán các lệnh thường dùng. Tác giả cần đảm bảo rằng tương tác trên thanh trình đơn tương tự như tương tác trên thanh trình đơn thông thường trong giao diện người dùng đồ hoạ trên máy tính.

thanh trình đơn (vai trò) WAI-ARIA 1.1

Vai trò mục trình đơn

Một lựa chọn trong một tập hợp các lựa chọn nằm trong trình đơn hoặc thanh trình đơn.

menuitem (vai trò) WAI-ARIA 1.1

Ở đây, thông số rất rõ ràng, chỉ sử dụng tính năng điều hướng để điều hướng tài liệu hoặc các tài liệu và trình đơn có liên quan cho một danh sách các thao tác hoặc chức năng tương tự như trình đơn trong các ứng dụng dành cho máy tính. Nếu không xây dựng Google Tài liệu tiếp theo, có thể bạn không cần vai trò nào trên trình đơn cho thanh điều hướng chính.

Khi nào thì nên dùng thực đơn?

Công dụng chính của các mục trong trình đơn không phải là điều hướng, mà là thực hiện các hành động. Giả sử bạn có một danh sách hoặc bảng dữ liệu và người dùng có thể thực hiện một số hành động nhất định đối với từng mục trong danh sách. Bạn có thể thêm một nút vào mỗi hàng và hiển thị các hành động khi người dùng nhấp vào nút đó.

<ul>
  <li>
    Product 1

    <button aria-expanded="false" aria-controls="options1">Edit</button>

    <div role="menu" id="options1">
      <button role="menuitem">
        Duplicate
      </button>
      <button role="menuitem">
        Delete
      </button>
      <button role="menuitem">
        Disable
      </button>
    </div>
  </li>
  <li>
    Product 2
    ...
  </li>
</ul>

Hệ quả của việc sử dụng vai trò trong trình đơn

Điều quan trọng là bạn phải sử dụng các vai trò này trên thực đơn một cách khôn ngoan vì rất nhiều việc có thể xảy ra.

Các trình đơn đòi hỏi một cấu trúc DOM nhất định. menuitem phải là mục con trực tiếp của menu. Mã sau đây có thể phá vỡ hành vi ngữ nghĩa:

 <!-- Wrong, don't do this -->
<ul role="menu">
  <li>
    <a href="#" role="menuitem">Item 1</a>
  </li>
</ul>

Người dùng thông thái cho rằng một số phím tắt sẽ hoạt động với trình đơn và thanh trình đơn. Dựa trên Hướng dẫn về phương pháp ghi nhận quyền tác giả (APG) của ARIA, nội dung này bao gồm:

  • Enterdấu cách để chọn các mục trong trình đơn.
  • Các phím mũi tên theo mọi hướng để di chuyển giữa các mục.
  • Phím HomeEnd (Kết thúc) để di chuyển tiêu điểm tương ứng đến mục đầu tiên hoặc mục cuối cùng.
  • a-z để di chuyển tiêu điểm đến mục tiếp theo trong trình đơn có nhãn bắt đầu bằng ký tự đã nhập.
  • Esc để đóng trình đơn.

Nếu trình đọc màn hình phát hiện thấy một trình đơn, thì phần mềm có thể tự động thay đổi chế độ duyệt web, cho phép bạn sử dụng các phím tắt đã đề cập trước đó. Người dùng trình đọc màn hình thiếu kinh nghiệm có thể không sử dụng được trình đơn do không biết các phím tắt này hoặc không biết cách sử dụng.

Điều này cũng tương tự đối với những người dùng bàn phím có thể cho rằng họ có thể sử dụng tổ hợp phím ShiftShift + Tab.

Có rất nhiều điều cần xem xét khi bạn tạo trình đơn và thanh trình đơn, đó là việc sử dụng trình đơn và thanh trình đơn đó có phù hợp ngay từ đầu hay không. Khi bạn xây dựng một trang web thông thường, bạn chỉ cần cung cấp phần tử điều hướng với danh sách và đường liên kết. Danh mục này cũng bao gồm Ứng dụng trang đơn (SPA) hoặc ứng dụng web. Ngăn xếp cơ bản không quan trọng. Trừ khi bạn đang tạo một ứng dụng nào đó rất gần với ứng dụng dành cho máy tính để bàn, hãy tránh các vai trò trên trình đơn.

Nguồn thông tin khác

Hình ảnh chính của Mick Haupt