하위 하이퍼링크를 접어 두는 메뉴는 열기와 이동을 구분해야 한다. 열고 닫는 동작은 버튼이, 다른 위치로 이동하는 동작은 하이퍼링크가 맡는다. 마우스 없이 읽는 순서에서도 같은 길을 찾을 수 있게 한다.
- 버튼의
aria-expanded로 펼쳐진 상태를 알리고aria-controls로 대상 목록을 연결한다. - Enter와 Space로 버튼을 누르고, Tab으로 펼쳐진 하이퍼링크를 이동한다. 예제는 Escape로 닫고 버튼으로 돌아간다.
- 일반적인 웹사이트 탐색에 앱 메뉴용
role="menu"를 붙이지 않는다. 그 역할에는 다른 키보드 동작이 기대된다. - 스크립트가 실행되지 않으면 목록을 펼친 채 남긴다. 작동하지 않는 열기 버튼은 보이지 않는다.
작은 예제
아래 코드를 UTF-8 형식의 example-08.html로 저장해 웹 브라우저에서 연다. 가상의 작업 두 종류로 구성한 예제다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>마우스와 키보드로 여는 메뉴</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 1rem; font-family: sans-serif;
line-height: 1.6; color: #222; background: #fff; }
main { max-inline-size: 60rem; margin-inline: auto; }
h1 { font-size: 1.7rem; line-height: 1.3; }
a { color: #174fa0; }
:focus-visible { outline: 3px solid #174fa0; outline-offset: 3px; }
button { font: inherit; padding: .5rem .75rem; }
nav a { display: inline-block; padding-block: .4rem; }
[hidden] { display: none !important; }
</style>
</head>
<body>
<main>
<h1>마우스와 키보드로 여는 메뉴</h1>
<nav aria-label="작업 둘러보기">
<button type="button" id="toggle" aria-expanded="false"
aria-controls="works" hidden>작업 목록</button>
<ul id="works">
<li><a href="#paper">종이 작업</a></li>
<li><a href="#web">웹 작업</a></li>
</ul>
</nav>
<section id="paper"><h2>종이 작업</h2><p>가상의 작품 목록이다.</p></section>
<section id="web"><h2>웹 작업</h2><p>가상의 작품 목록이다.</p></section>
</main>
<script>
const nav = document.querySelector("nav");
const button = document.querySelector("#toggle");
const list = document.querySelector("#works");
function setOpen(open) {
button.setAttribute("aria-expanded", String(open));
list.hidden = !open;
}
button.addEventListener("click", () => {
setOpen(button.getAttribute("aria-expanded") !== "true");
});
nav.addEventListener("keydown", event => {
if (event.key === "Escape" && !list.hidden) {
setOpen(false);
button.focus();
event.preventDefault();
}
});
nav.addEventListener("focusout", event => {
if (event.relatedTarget && !nav.contains(event.relatedTarget)) setOpen(false);
});
document.addEventListener("click", event => {
if (!nav.contains(event.target)) setOpen(false);
});
setOpen(false);
button.hidden = false;
</script>
</body>
</html>
마우스로 올려놓기만 해서는 열리지 않는다. 클릭이나 터치로 버튼을 누르거나 키보드로 조작한다. 웹 브라우저 설정에 따라 하이퍼링크로 Tab 이동을 켜야 할 수 있다. 시맨틱 HTML을 바탕으로 만들되 실제 적용 전에는 화면 낭독기에서도 확인한다. 메뉴가 필요 없을 만큼 항목이 적다면 접지 않아도 되는 메뉴가 더 편할 수 있다.