웹사이트의 기본 탐색 메뉴 빌드하기

이 튜토리얼에서는 웹사이트의 접근성 높은 기본 탐색을 빌드하는 방법을 설명합니다. 시맨틱 HTML, 접근성, ARIA 속성 사용이 때로는 득보다 실이 많을 수 있는 이유를 알아봅니다.

Manuel Matuzović
Manuel Matuzović

스타일 지정, 기능, 기본 마크업 및 시맨틱 정보 측면에서 웹사이트의 기본 탐색을 빌드하는 방법은 다양합니다. 구현이 너무 단순하면 대부분의 사용자에게는 작동하지만 사용자 경험 (UX)이 좋지 않을 수 있습니다. 과도하게 설계되면 사용자를 혼란스럽게 하거나 사용자가 아예 액세스하지 못하게 할 수도 있습니다.

대부분의 웹사이트는 너무 단순하지도, 너무 복잡하지도 않게 구축하는 것이 좋습니다.

레이어별로 빌드

이 튜토리얼에서는 기본 설정으로 시작하여 대부분의 사용자를 만족시킬 수 있는 충분한 정보, 스타일, 기능을 제공하는 지점까지 레이어별로 기능을 추가합니다. 이를 위해 점진적 개선 원칙을 사용합니다. 이 원칙은 가장 기본적이고 강력한 솔루션으로 시작하여 점진적으로 기능 레이어를 추가하는 것입니다. 어떤 이유로든 한 레이어가 작동하지 않더라도 기본 레이어로 원활하게 대체되므로 탐색은 계속 작동합니다.

기본 구조

기본 탐색에는 <a> 요소와 링크의 기본 스타일 및 레이아웃을 개선하는 몇 줄의 CSS가 필요합니다.

  • HTML

    <a href="/home">Home</a>
    <a href="/about-us">About us</a>
    <a href="/pricing">Pricing</a>
    <a href="/contact">Contact</a>
    
  • CSS

    /* 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);
    }
    
CodePen에서 1단계: 기본 HTML 및 CSS' 보기

이 방법은 사이트에 액세스하는 방식과 관계없이 대부분의 사용자에게 적합합니다. 마우스, 키보드, 터치 기기 또는 스크린 리더로 탐색할 수 있지만 개선의 여지가 있습니다. 추가 기능과 정보로 이 기본 패턴을 확장하여 환경을 개선할 수 있습니다.

방법은 다음과 같습니다.

  • 활성 페이지를 강조 표시합니다.
  • 스크린 리더 사용자에게 항목 수를 알립니다.
  • 랜드마크를 추가하고 스크린 리더 사용자가 바로가기를 사용하여 탐색에 직접 액세스할 수 있도록 허용합니다.
  • 좁은 표시 영역에서 탐색 메뉴를 숨깁니다.
  • 포커스 스타일 지정 개선

활성 페이지 강조 표시

활성 페이지를 강조 표시하려면 해당 링크에 클래스를 추가하면 됩니다.

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

이 접근 방식의 문제는 어떤 링크가 활성 상태인지에 관한 정보를 순전히 시각적으로 전달한다는 점입니다. 시각 장애가 있는 스크린 리더 사용자는 활성 페이지와 다른 페이지의 차이를 구분할 수 없습니다. 다행히 Accessible Rich Internet Applications (ARIA) 표준은 이 정보를 의미론적으로 전달하는 방법도 제공합니다. 클래스 대신 aria-current="page" 속성과 값을 사용합니다.

aria-current (상태)는 컨테이너 또는 관련 요소 집합 내에서 현재 항목을 나타내는 요소를 나타냅니다. 페이지로 나누기 링크 집합 내에서 링크를 나타내는 데 사용되는 페이지 토큰입니다. 링크는 현재 표시된 페이지를 나타내도록 시각적으로 스타일이 지정됩니다. [Accessible Rich Internet Applications (WAI-ARIA) 1.1](https://www.w3.org/TR/wai-aria/#aria-current)

추가 속성을 사용하면 스크린 리더가 이제 '링크, 회사 소개'가 아닌 '현재 페이지, 링크, 회사 소개'와 같이 알립니다.

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

이 속성을 사용하여 CSS에서 활성 링크를 선택할 수 있으므로 active-page 클래스가 더 이상 필요하지 않습니다.

<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);
}
2단계: CodePen에서 활성 페이지 강조 표시를 확인합니다.

항목 수 알림

정안인은 탐색을 보고 링크가 4개만 포함되어 있음을 알 수 있습니다. 시각장애인 스크린 리더 사용자는 이 정보를 빠르게 얻을 수 없습니다. 전체 링크 목록을 모두 확인해야 할 수도 있습니다. 이 예시처럼 목록이 짧으면 문제가 되지 않을 수 있지만 링크가 40개 포함되어 있다면 이 작업이 번거로울 수 있습니다. 스크린 리더 사용자가 탐색에 많은 링크가 포함되어 있다는 사실을 미리 알고 있다면 사이트 검색과 같이 더 효율적인 다른 탐색 방법을 사용하기로 결정할 수 있습니다.
항목 수를 미리 전달하는 좋은 방법은 각 링크를 순서가 지정되지 않은 목록 (<ul>)에 중첩된 목록 항목 (<li>)으로 래핑하는 것입니다.

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

스크린 리더 사용자가 목록을 찾으면 소프트웨어에서 '목록, 항목 4개'와 같이 알려줍니다.

다음은 Windows에서 스크린 리더 NVDA를 사용한 탐색의 데모입니다.

이제 이전과 같이 보이도록 스타일을 조정해야 합니다.

/* 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;
}

목록을 사용하면 스크린 리더 사용자에게 다음과 같은 이점이 있습니다.

  • 항목과 상호작용하기 전에 총 항목 수를 확인할 수 있습니다.
  • 단축키를 사용하여 목록 항목 간에 이동할 수 있습니다.
  • 단축키를 사용하여 목록 간에 이동할 수 있습니다.
  • 스크린 리더가 현재 항목의 색인을 알려줄 수 있습니다 (예: '목록 항목, 4개 중 2개').

또한 페이지가 CSS 없이 표시되는 경우 목록은 링크를 단순한 링크 모음이 아닌 일관된 항목 그룹으로 표시합니다.

Safari의 VoiceOver에 관한 주목할 만한 세부정보는 list-style: none를 설정하면 이러한 모든 이점을 잃게 된다는 것입니다. 처음부터 그렇게 설계되어 있습니다. WebKit팀은 목록이 목록처럼 보이지 않는 경우 목록 시맨틱을 삭제하기로 결정했습니다. 탐색의 복잡성에 따라 문제가 될 수도 있고 그렇지 않을 수도 있습니다. 한편으로는 탐색이 여전히 사용 가능하며 Safari의 VoiceOver에만 영향을 미칩니다. Chrome 또는 Firefox에서 VoiceOver를 사용하면 NVDA와 같은 다른 스크린 리더와 마찬가지로 항목 수를 계속 알려줍니다. 반면에 시맨틱 정보는 일부 상황에서 매우 유용할 수 있습니다. 이러한 결정을 내리려면 실제 스크린 리더 사용자를 대상으로 탐색을 테스트하고 의견을 받아야 합니다. Safari의 VoiceOver가 다른 모든 스크린 리더와 동일하게 작동해야 한다고 판단되면 <ul>에 ARIA 목록 역할을 명시적으로 설정하여 이 문제를 해결할 수 있습니다. 이렇게 하면 목록 스타일 지정을 삭제하기 전 상태로 동작이 되돌아갑니다. 시각적으로는 목록이 여전히 동일하게 보입니다.

<ul role="list">
  <li>
     <a href="/home">Home</a>
  </li>
  ...
</ul>
3단계: CodePen에서 항목 수 알리기를 확인합니다.

랜드마크 추가

약간의 노력으로 스크린 리더 사용자를 위해 많은 부분을 개선했지만 한 가지 더 할 수 있는 일이 있습니다. 탐색은 의미론적으로 여전히 링크 목록일 뿐이며 이 특정 목록이 웹사이트의 기본 탐색인지 알기 어렵습니다. <ul>를 <nav> 요소로 래핑하여 이 일반 목록을 탐색 목록으로 전환할 수 있습니다.

<nav> 요소를 사용하면 다음과 같은 몇 가지 이점이 있습니다. 특히 사용자가 상호작용할 때 스크린 리더는 '탐색'과 같은 내용을 알리고 페이지에 랜드마크를 추가합니다. 랜드마크는 페이지에서 스크린 리더가 이동할 수 있는 특별한 영역(예: <header>, <footer>, <main>)입니다. 페이지에 랜드마크가 있으면 스크린 리더 사용자가 페이지의 나머지 부분과 상호작용하지 않고도 페이지의 중요한 영역에 직접 액세스할 수 있으므로 유용합니다. 예를 들어 NVDA에서 D 키를 눌러 랜드마크 간에 이동할 수 있습니다. VoiceOver에서는 VO + U를 눌러 로터를 사용하여 페이지의 모든 주요 지점을 나열할 수 있습니다.

배너, 탐색, 기본, 콘텐츠 정보 등 4가지 랜드마크 목록
페이지의 모든 랜드마크를 나열하는 VoiceOver의 로터

이 목록에는 4개의 랜드마크가 표시됩니다. banner는 <header> 요소, navigation은 <nav>, main은 <main> 요소, content information은 <footer>입니다. 이 목록은 너무 길지 않아야 합니다. 사이트 검색, 로컬 탐색, 페이지로 나누기와 같이 UI의 중요한 부분만 랜드마크로 표시해야 합니다.

사이트 전체 탐색, 페이지의 로컬 탐색, 단일 페이지의 페이지로 나누기가 있는 경우 <nav> 요소가 3개 있을 수도 있습니다. 괜찮지만 이제 세 개의 탐색 랜드마크가 있고 의미론적으로 모두 동일하게 보입니다. 페이지 구조를 잘 알지 못하면 구분하기 어렵습니다.

모두 '탐색'이라고 표시된 세 개의 랜드마크를 보여주는 이미지
라벨이 지정되지 않은 탐색 랜드마크 3개를 나열하는 VoiceOver의 로터

구분할 수 있도록 aria-labelledby 또는 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>

선택한 라벨이 페이지의 어딘가에 이미 있는 경우 aria-labelledby를 대신 사용하고 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>

간결한 라벨로 충분하며 너무 장황하게 작성하지 마세요. 스크린 리더가 이미 사용자에게 이 정보를 제공하므로 '탐색' 또는 '메뉴'와 같은 표현은 생략합니다.

명소
'배너', '기본 탐색', '기본', '페이지 탐색', '페이지 탐색 선택', '콘텐츠 정보' 랜드마크를 나열하는 VoiceOver
4단계: CodePen에서 랜드마크 추가하기를 참고하세요.

좁은 표시 영역에서 탐색 숨기기

개인적으로는 좁은 표시 영역에서 기본 탐색을 숨기는 것을 좋아하지 않지만 링크 목록이 너무 길면 어쩔 수 없습니다. 이 경우 목록 대신 '메뉴'라는 라벨이 지정된 버튼이나 버거 아이콘 또는 이 둘의 조합이 표시됩니다. 버튼을 클릭하면 목록이 표시되거나 숨겨집니다. 기본적인 JavaScript와 CSS를 알고 있다면 가능한 작업이지만 UX와 접근성 측면에서 처리해야 할 사항이 몇 가지 있습니다.

  • 액세스 가능한 방식으로 목록을 숨겨야 합니다.
  • 탐색은 키보드로 액세스할 수 있어야 합니다.
  • 탐색은 표시 여부를 전달해야 합니다.

버거 버튼 추가

점진적 개선 원칙을 따르고 있으므로 JavaScript가 사용 중지된 경우에도 탐색이 계속 작동하고 의미가 있는지 확인해야 합니다.
탐색에 가장 먼저 필요한 것은 버거 버튼입니다. HTML의 템플릿 요소에서 만들고, JavaScript에서 클론하고, 탐색에 추가합니다.

버거 버튼을 표시하는 페이지
결과: 탐색 메뉴에 링크 대신 좁은 표시 영역에 버거 버튼이 표시됩니다.
<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. aria-expanded 속성은 버튼이 제어하는 요소가 펼쳐져 있는지 여부를 스크린 리더 소프트웨어에 알려줍니다.
  2. aria-label는 버튼에 액세스 가능한 이름, 즉 버거 아이콘의 대체 텍스트를 제공합니다.
  3. aria-label에서 제공하는 텍스트 라벨이 이미 있으므로 aria-hidden를 사용하여 보조 기술에서 <svg>를 숨깁니다.
  4. aria-controls는 이 속성을 지원하는 보조 기술 (예: JAWS)에 버튼이 제어하는 요소를 알려줍니다.
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. 사용자가 원할 때 언제든지 탐색을 닫을 수 있는 기능(예: Esc 키 누르기)이 있으면 편리합니다.
  2. 버튼은 탐색의 첫 번째 요소여야 하므로 appendChild 대신 insertBefore를 사용하는 것이 중요합니다. 키보드 또는 스크린 리더 사용자가 버튼을 클릭한 후 Tab을 누르면 목록의 첫 번째 항목에 포커스가 맞춰질 것으로 예상합니다. 버튼이 목록 뒤에 있으면 그렇지 않습니다.

다음으로 버튼의 기본 스타일을 재설정하고 좁은 표시 영역에만 표시되도록 합니다.

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

/* Reset button styling */
button {
  all: unset;
  display: var(--nav-button-display, flex);
}
5단계: CodePen에서 버거 버튼 추가하기를 참고하세요.

목록 숨기기

목록을 숨기기 전에 레이아웃이 좁은 표시 영역에 최적화되지만 큰 화면에서도 보기 좋게 표시되도록 탐색 및 목록의 위치와 스타일을 지정합니다.
먼저 페이지의 자연스러운 흐름에서 <nav>를 삭제하고 표시 영역의 상단 모서리에 배치합니다.

@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;
}

그런 다음 새 맞춤 속성 (—-nav-list-layout)을 추가하여 좁은 표시 영역의 레이아웃을 변경합니다. 레이아웃은 기본적으로 열이며 더 큰 화면에서는 행으로 전환됩니다.

@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;
}

좁은 표시 영역에서는 탐색이 다음과 같이 표시됩니다.

탐색 목록과 버거 버튼을 보여주는 페이지
버거 버튼과 목록이 모두 표시 영역의 상단 끝 모서리에 배치됩니다.

목록에는 CSS가 필요합니다. 오른쪽 상단으로 이동하고, 세로로 전체 화면을 채우도록 하고, background-color 및 box-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;
}

좁은 표시 영역에서는 목록이 단순한 목록이 아닌 사이드바와 같이 표시됩니다.

탐색 목록이 열립니다.

마지막으로 목록을 숨기고 사용자가 버튼을 한 번 클릭할 때만 표시하고 다시 클릭하면 숨깁니다. 탐색을 숨기면 중요한 랜드마크도 숨겨지므로 전체 탐색이 아닌 목록만 숨기는 것이 중요합니다.

앞서 버튼에 클릭 이벤트를 추가하여 aria-expanded 속성 값을 전환했습니다. 이 정보를 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);
}

목록을 숨기려면 opacity: 0 또는 translateX(100%) 대신 visibility: hidden 또는 display: none와 같은 속성 선언을 사용하는 것이 중요합니다. 이러한 속성은 탐색이 숨겨져 있을 때 링크에 포커스를 맞출 수 없도록 합니다. opacity 또는 translate를 사용하면 콘텐츠가 시각적으로 삭제되므로 링크가 보이지 않지만 키보드를 사용하여 계속 액세스할 수 있으므로 혼란스럽고 불편할 수 있습니다. visibility 또는 display를 사용하면 시각적으로 숨겨지고 액세스할 수 없게 되므로 모든 사용자에게 숨겨집니다.

6단계: 목록 숨기기를 참고하세요.

목록에 애니메이션 적용

display: none; 대신 visibility: hidden;를 사용하는 이유는 공개 상태에 애니메이션을 적용할 수 있기 때문입니다. hidden 및 visible의 두 가지 상태만 있지만 transform 또는 opacity와 같은 다른 속성과 결합하여 슬라이드 또는 페이드인 효과를 만들 수 있습니다. display 속성은 애니메이션을 적용할 수 없으므로 display: none으로는 작동하지 않습니다.

다음 CSS는 opacity를 전환하여 페이드 인 및 페이드 아웃 효과를 만듭니다.

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);
}

대신 모션에 애니메이션을 적용하려면 transition 속성을 prefers-reduced-motion 미디어 쿼리로 래핑하는 것이 좋습니다. 애니메이션은 일부 사용자에게 메스꺼움, 현기증, 두통을 유발할 수 있기 때문입니다.

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);
}

이렇게 하면 모션 감소를 선호하지 않는 사용자에게만 애니메이션이 표시됩니다.

7단계: CodePen에서 목록 애니메이션 처리를 참고하세요.

포커스 스타일 개선

키보드 사용자는 페이지에서 방향을 잡고 탐색하기 위해 요소의 포커스 스타일에 의존합니다. 기본 포커스 스타일은 포커스 스타일이 없는 것(outline: none를 설정하는 경우)보다 낫지만, 더 명확하게 표시되는 맞춤 포커스 스타일을 사용하면 사용자 환경이 개선됩니다.

Chrome 103에서 링크의 기본 포커스 스타일은 다음과 같습니다.

Chrome 103에서 포커스가 지정된 링크 주위에 파란색 2px 윤곽선이 표시됩니다.

자체 색상으로 자체 스타일을 제공하여 이를 개선할 수 있습니다. :focus 대신 :focus-visible를 사용하면 브라우저에서 포커스 스타일을 표시하는 것이 적절한 시기를 결정할 수 있습니다. :focus 스타일은 마우스, 키보드, 터치 사용자 등 모든 사용자에게 필요 여부와 관계없이 표시됩니다. :focus-visible를 사용하면 브라우저는 내부 휴리스틱을 사용하여 키보드 사용자에게만 표시할지 아니면 모든 사용자에게 표시할지 결정합니다.

/* 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;
}

:focus-visible의 브라우저 지원

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 85.
  • Safari: 15.4.

Source

내부에 간격이 있는 명확하게 보이는 어두운 2px 윤곽선입니다.

포커스가 맞춰진 항목을 강조 표시하는 방법에는 여러 가지가 있습니다. outline 속성을 사용하면 border에서 발생할 수 있는 레이아웃 손상이 발생하지 않고 Windows의 고대비 모드에서 잘 작동하므로 이 속성을 사용하는 것이 좋습니다. background-color 또는 box-shadow 속성은 맞춤 대비 설정으로 인해 전혀 표시되지 않을 수 있으므로 제대로 작동하지 않습니다.

어두운 배경의 사이트에서 포커스가 보라색으로 강조 표시되어 있습니다.
8단계: CodePen에서 포커스 스타일 개선하기를 확인합니다.

수고하셨습니다 점진적으로 개선되고, 의미론적으로 풍부하며, 접근성이 높고, 모바일 친화적인 기본 탐색을 빌드했습니다.

항상 개선할 수 있는 부분이 있습니다. 예를 들면 다음과 같습니다.

  • 좁은 표시 영역에서 탐색 내에 포커스를 트랩하거나 페이지의 나머지 부분을 비활성 상태로 만드는 것이 좋습니다.
  • 페이지 상단에 건너뛰기 링크를 추가하여 키보드 사용자가 탐색을 건너뛰도록 할 수 있습니다.

이 글의 시작 부분에서 솔루션은 '너무 단순하지도, 너무 복잡하지도 않아야 한다'고 언급한 것을 기억한다면, 지금이 바로 그 지점입니다. 하지만 탐색을 오버 엔지니어링할 수도 있습니다.

탐색과 메뉴에는 명확한 차이가 있습니다. 탐색은 관련 문서를 탐색하기 위한 링크 모음입니다. 메뉴는 문서에서 실행할 수 있는 작업의 모음입니다. 이러한 작업은 때때로 중복되기도 합니다. 모달 창을 여는 등의 작업을 실행하는 버튼이 포함된 탐색 메뉴가 있거나, 도움말 페이지와 같은 다른 페이지로 이동하는 작업이 포함된 메뉴가 있을 수 있습니다. 이 경우 ARIA 역할을 혼합하지 않고 구성요소의 주요 목적을 파악하고 그에 따라 마크업과 역할을 선택하는 것이 중요합니다.

<nav> 요소에는 암시적 ARIA 탐색 역할이 있어 요소가 탐색임을 전달하기에 충분하지만, 사이트에서 메뉴, 메뉴 바, 메뉴 항목을 사용하는 경우도 많습니다. 이러한 용어를 혼용하는 경우가 있으므로 스크린 리더 사용자의 환경을 개선하기 위해 용어를 결합하는 것이 합리적일 수 있습니다. 일반적으로 그렇지 않은 이유를 알아보기 전에 이러한 역할의 공식 정의를 살펴보겠습니다.

탐색 역할

문서 또는 관련 문서를 탐색하기 위한 탐색 요소 (일반적으로 링크)의 모음입니다.

탐색 (역할) WAI-ARIA 1.1

메뉴 역할

메뉴는 사용자가 호출할 수 있는 일반적인 작업 또는 함수의 목록인 경우가 많습니다. 메뉴 역할은 메뉴 항목 목록이 데스크톱 애플리케이션의 메뉴와 유사한 방식으로 표시될 때 적합합니다.

메뉴 (역할) WAI-ARIA 1.1

menubar 역할

일반적으로 표시된 상태로 유지되며 가로로 표시되는 메뉴입니다. menubar 역할은 Windows, Mac, Gnome 데스크톱 애플리케이션에서 볼 수 있는 것과 유사한 메뉴 바를 만드는 데 사용됩니다. 메뉴 바는 자주 사용하는 명령어의 일관된 집합을 만드는 데 사용됩니다. 작성자는 메뉴 바 상호작용이 데스크톱 그래픽 사용자 인터페이스의 일반적인 메뉴 바 상호작용과 유사하도록 해야 합니다.

menubar (역할) WAI-ARIA 1.1

menuitem 역할

menu 또는 menubar에 포함된 선택 집합의 옵션입니다.

menuitem (역할) WAI-ARIA 1.1

사양은 매우 명확합니다. 문서 또는 관련 문서를 탐색하는 데는 탐색을 사용하고, 데스크톱 애플리케이션의 메뉴와 유사한 작업 또는 기능 목록에는 메뉴만 사용합니다. 차세대 Google Docs를 빌드하는 것이 아니라면 기본 탐색에 메뉴 역할이 필요하지 않을 수 있습니다.

메뉴는 언제 적절한가요?

메뉴 항목의 주된 용도는 탐색이 아니라 작업을 실행하는 것입니다. 데이터 목록이나 표가 있고 사용자가 목록의 각 항목에 대해 특정 작업을 실행할 수 있다고 가정해 보겠습니다. 각 행에 버튼을 추가하고 사용자가 버튼을 클릭할 때 작업을 표시할 수 있습니다.

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

메뉴 역할 사용의 영향

이러한 메뉴 역할을 현명하게 사용하는 것이 매우 중요합니다. 잘못될 수 있는 부분이 많기 때문입니다.

메뉴에는 특정 DOM 구조가 필요합니다. menuitem은(는) menu의 직접 하위 항목이어야 합니다. 다음 코드는 시맨틱 동작을 중단할 수 있습니다.

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

숙련된 사용자는 특정 키보드 단축키가 메뉴 및 메뉴 바에서 작동하기를 기대합니다. ARIA Authoring Practices Guide (APG)에 따라 다음이 포함됩니다.

  • Enter 및 스페이스바를 눌러 메뉴 항목을 선택합니다.
  • 모든 방향의 화살표 키를 사용하여 항목 간에 이동합니다.
  • Home 및 End 키를 사용하여 포커스를 각각 첫 번째 또는 마지막 항목으로 이동합니다.
  • a~z: 입력한 문자로 시작하는 라벨이 있는 다음 메뉴 항목으로 포커스를 이동합니다.
  • Esc를 눌러 메뉴를 닫습니다.

스크린 리더가 메뉴를 감지하면 소프트웨어가 자동으로 탐색 모드를 변경하여 앞서 언급한 단축키를 사용할 수 있습니다. 스크린 리더 사용에 익숙하지 않은 사용자는 이러한 단축키 또는 사용 방법을 모르기 때문에 메뉴를 사용하지 못할 수 있습니다.

Shift 및 Shift + Tab을 사용할 수 있다고 예상하는 키보드 사용자도 마찬가지입니다.

메뉴와 메뉴 바를 만들 때는 고려해야 할 사항이 많습니다. 우선 메뉴와 메뉴 바를 사용하는 것이 적절한지부터 따져봐야 합니다. 일반적인 웹사이트를 빌드할 때는 목록과 링크가 있는 nav 요소만 있으면 됩니다. 여기에는 단일 페이지 애플리케이션 (SPA) 또는 웹 앱도 포함됩니다. 기본 스택은 중요하지 않습니다. 데스크톱 애플리케이션과 매우 유사한 것을 빌드하는 경우가 아니라면 메뉴 역할을 피하세요.

추가 리소스