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

이 튜토리얼에서는 접근성이 우수한 웹사이트 기본 탐색을 빌드하는 방법을 설명합니다. 의미론적 HTML과 접근성에 관해 알아보고 ARIA 속성을 사용하면 때때로 득보다 실이 클 수도 있는 이유를 알아봅니다.

Manuel Matuzović
Manuel Matuzović

스타일, 기능, 기본 마크업 및 시맨틱 정보 측면에서 웹사이트의 기본 탐색을 만드는 방법은 다양합니다. 구현이 너무 미니멀하면 대부분의 사용자에게는 적합하지만 사용자 환경 (UX)은 좋지 않을 수 있습니다. 과도하게 엔지니어링되면 사용자에게 혼란을 주거나 전혀 액세스하지 못하게 될 수도 있습니다.

대부분의 웹사이트에서는 너무 간단하거나 복잡하지 않은 방식으로 사이트를 만들려고 합니다.

레이어별 빌드

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

기본 구조

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

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

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

방법은 다음과 같습니다.

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

활성 페이지 강조표시

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

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

이 접근 방식의 문제는 활성화된 링크가 순전히 시각적으로만 표시되는 정보를 전달한다는 것입니다. 시각장애인 스크린 리더 사용자가 활성 페이지와 다른 페이지를 구분하지 못합니다. 다행히 ARIA (Accessible Rich Internet Applications) 표준에서는 이 정보를 의미론적으로 전달하는 방법도 제공합니다. 클래스 대신 aria-current=&quot;page&quot; 속성 및 값을 사용합니다.

<ph type="x-smartling-placeholder">
</ph> 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에서 활성 페이지 강조표시를 확인합니다.

항목 수 알림

일반 사용자는 탐색 메뉴를 보고 링크가 네 개만 포함되어 있음을 알 수 있습니다. 시각장애인 스크린 리더 사용자는 이러한 정보를 빠르게 얻을 수 없습니다. 전체 링크 목록을 살펴봐야 할 수도 있습니다. 이 예와 같이 목록이 짧으면 문제가 되지 않을 수도 있지만 링크가 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에 있는 항목 수 안내를 참고하세요.

명소 추가

적은 노력으로 스크린 리더 사용자를 위한 큰 개선 효과를 거두었지만 한 가지 방법이 더 있습니다. 탐색은 의미상 여전히 링크 목록일 뿐이므로 이 특정 목록이 웹사이트의 기본 탐색 메뉴인지 구별하기 어렵습니다. <nav> 요소에서 <ul>를 래핑하여 이 일반 목록을 탐색 목록으로 전환할 수 있습니다.

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

<ph type="x-smartling-placeholder">
</ph> 배너, 탐색, 기본, 콘텐츠 정보 등 네 가지 랜드마크로 구성된 목록입니다.
페이지의 모든 랜드마크를 나열하는 VoiceOver의 로터

이 목록에는 <header> 요소인 배너, <nav> 요소인 탐색, <main> 요소 기본, 콘텐츠 정보 등 4개의 랜드마크가 표시됩니다.<footer> 이 목록은 너무 길지 않아야 하며, 사이트 검색, 로컬 탐색, 페이지로 나누기 등 UI의 중요한 부분만 랜드마크로 표시하는 것이 좋습니다.

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

<ph type="x-smartling-placeholder">
</ph> 모두 &#39;내비게이션&#39;이라고 표시된 세 개의 랜드마크를 보여주는 이미지입니다.
라벨이 지정되지 않은 세 개의 탐색 랜드마크를 나열하는 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>

간결한 라벨로 충분하며 너무 장황하지 마세요. 'navigation'과 같은 표현식 생략 또는 '메뉴' 스크린 리더가 이미 사용자에게 이 정보를 제공하고 있기 때문입니다.

<ph type="x-smartling-placeholder">
</ph> 명소
'배너', '기본 탐색', '기본', '페이지 탐색', '페이지 탐색 선택' 랜드마크를 나열하는 VoiceOver '콘텐츠 정보'가 포함됩니다.
4단계: CodePen에 랜드마크 추가하기를 참고하세요.

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

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

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

버거 버튼 추가

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

<ph type="x-smartling-placeholder">
</ph> 버거 버튼이 표시된 페이지
결과: 탐색 시 링크 대신 좁은 표시 영역에 버거 버튼이 표시됩니다.
<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;
}

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

<ph type="x-smartling-placeholder">
</ph> 탐색 목록과 햄버거 버튼을 보여주는 페이지
햄버거 버튼과 목록은 모두 표시 영역의 상단 끝에 배치됩니다.

이 목록에는 분명히 CSS가 필요합니다. 상단 끝 모서리로 이동하여 전체 화면을 세로로 채우도록 하고 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;
}

목록은 좁은 표시 영역에서 다음과 같아야 합니다. 단순한 목록이라기보다는 사이드바에 가깝습니다.

탐색 목록이 열립니다.

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

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

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

목록에 애니메이션 적용

display: none;보다 visibility: hidden;를 사용해야 하는 이유는 가시성에 애니메이션을 적용할 수 있기 때문입니다. 두 가지 상태, 즉 hiddenvisible만 있지만 transform 또는 opacity와 같은 다른 속성과 결합하여 슬라이드 또는 페이드 인 효과를 만들 수 있습니다. 이 경우 display: none에서는 작동하지 않습니다. display 속성이 애니메이션 가능하지 않기 때문입니다.

다음 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에서 포커스가 맞춰진 링크를 둘러싼 파란색 2픽셀 윤곽선

자체 색상으로 자체 스타일을 제공하면 이를 개선할 수 있습니다. :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 브라우저 지원

브라우저 지원

  • Chrome: 86 <ph type="x-smartling-placeholder">
  • Edge: 86. <ph type="x-smartling-placeholder">
  • Firefox: 85 <ph type="x-smartling-placeholder">
  • Safari 15.4. <ph type="x-smartling-placeholder">

소스

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

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

어두운 배경에 포커스가 보라색으로 강조 표시된 사이트
8단계: CodePen에서 포커스 스타일 개선을 참고하세요.

축하합니다. 지속적으로 개선되고 의미론적으로 풍부하며 접근성과 모바일 친화적인 기본 탐색을 만들었습니다.

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

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

솔루션이 '너무 단순하거나 너무 복잡하지 않아야 한다'는 목표 하에 이 글이 어떻게 시작되었는지 기억하시겠지만, 지금 말씀드리는 것이 바로 지금입니다. 하지만 탐색을 과도하게 엔지니어링할 수도 있습니다.

탐색과 메뉴 사이에는 분명한 차이가 있습니다. 탐색은 관련 문서를 탐색하기 위한 링크 모음입니다. 메뉴는 문서에서 실행할 작업의 모음입니다. 이러한 작업이 중복될 때도 있습니다. 모달 창 열기와 같이 작업을 실행하는 버튼도 포함된 탐색이 있거나 한 작업이 다른 페이지(예: 도움말 페이지)로 이동하는 메뉴가 있을 수 있습니다. 이 경우 ARIA 역할을 매시업하지 말고 구성요소의 주요 목적을 파악하고 마크업과 역할을 적절히 선택하는 것이 중요합니다.

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

탐색 역할

문서 또는 관련 문서를 탐색하는 탐색 요소의 컬렉션 (일반적으로 링크)입니다.

Navigation (역할) WAI-ARIA 1.1

메뉴 역할

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

메뉴 (역할) WAI-ARIA 1.1

메뉴 바 역할

일반적으로 계속 표시되며 가로로 표시되는 메뉴 프레젠테이션입니다. 메뉴바 역할은 Windows, Mac, Gnome 데스크톱 애플리케이션과 유사한 메뉴바를 만드는 데 사용됩니다. 메뉴 바는 자주 사용되는 명령의 일관된 집합을 만드는 데 사용됩니다. 작성자는 메뉴바 상호작용이 데스크톱 그래픽 사용자 인터페이스의 일반적인 메뉴 바 상호작용과 유사한지 확인해야 합니다.

메뉴 바 (역할) WAI-ARIA 1.1

Menuitem 역할

메뉴 또는 메뉴 바에 의해 포함된 선택 항목 집합에 있는 옵션입니다.

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 작성 관행 가이드 (APG)에 따라 다음이 포함됩니다.

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

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

이는 ShiftShift + Tab을 사용할 수 있을 것으로 예상하는 키보드 사용자에게도 동일하게 적용됩니다.

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

추가 리소스

히어로 이미지 제공: 믹 하우프트