HTML — 태그 하나가 기능을 대신한다 · 마우스를 빼앗겼다 · 이론
Tab 만으로 끝까지 가 본다
한 줄 요약
키보드 접근성은 기능이 아니라 순서다. 무엇이 초점을 받는가, 어떤 차례로 도는가, 그리고 지금 어디 있는지 보이는가. 이 셋이면 대부분이 끝난다.
왜 이게 필요했나
마우스를 치우고 Tab 만으로 자기 화면을 5분 써 보면 거의 언제나 무언가가 나온다. 초점이 갑자기 페이지 맨 아래로 튄다. 감춰 둔 메뉴 안으로 들어가서 몇 번을 눌러도 나오지 않는다. 초점이 지금 어디 있는지 안 보인다.
이런 것들은 QA 에서 절대 안 나온다. QA 도 마우스로 하기 때문이다. 그리고 사용자가 신고하지도 않는다 — 쓸 수 없는 화면은 그냥 떠나는 것이지 신고할 대상이 아니다.
키보드만 쓰는 사람은 생각보다 많다. 손목을 다친 사람, 스크린리더 사용자, 떨림이 있어 마우스 조준이 어려운 사람, 그리고 그냥 Tab 이 빠른 사람. 이 화면이 결제 화면이면 그 사람들은 결제를 못 한다.
어떻게 동작하나 — 순차 초점 순서
[HTML 표준의 tabindex 절](https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute)이 정한 규칙은 단순하다.
1. tabindex 가 양수인 것들 — 값이 작은 것부터, 같으면 문서 순서2. 그다음 tabindex="0" 과 기본으로 초점을 받는 요소들 — 문서 순서a[href], button, input, select, textarea, summary 는 기본으로 초점을 받는다. tabindex="-1" 은 스크립트로만 초점을 줄 수 있고 Tab 으로는 가지 않는다.
여기서 사람들이 가장 자주 다치는 것이 양수 tabindex 다. tabindex="3" 은 그 요소 하나를 세 번째로 옮기는 것이 아니라, 페이지 전체를 두 무리로 쪼갠다. 양수가 붙은 것들이 먼저 전부 돌고, 그다음에 나머지가 돈다. 그래서 한 곳에 tabindex="1" 을 붙이면 그 요소가 페이지의 첫 칸이 된다.
> 순서를 바꾸고 싶으면 마크업 순서를 바꾼다. tabindex 로 옮기지 않는다.
CSS 로 시각적 순서만 바꾸는 것(order, grid-row)도 같은 문제를 만든다. 눈에 보이는 차례와 Tab 이 도는 차례가 달라지고, [WCAG 의 Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html)가 말하는 "의미가 보존되는 순서" 가 깨진다.
건너뛰기 링크
스크린리더 사용자는 랜드마크를 건너뛸 수 있지만 키보드만 쓰는 사용자는 그럴 수 없다. 메뉴가 스무 칸이면 본문에 닿기까지 매 페이지마다 스무 번을 누른다.
그래서 페이지의 첫 칸에 본문으로 가는 링크를 둔다. [WCAG 의 Bypass Blocks](https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html)가 요구하는 것이 이것이다.
여기에 함정이 둘 있다.
- 첫 칸이어야 한다. 메뉴 뒤에 있으면 건너뛸 것을 이미 다 지난 뒤다
- 목적지가 초점을 받을 수 있어야 한다.
<main id="main">만으로는 브라우저가 주소만 바꾸고 초점은 그대로 두는 경우가 있다.tabindex="-1"을 주면 스크립트 없이도 초점이 따라간다
평소에는 화면 밖에 두었다가 초점이 왔을 때만 보이게 하는 것이 관례다. .skip { position: absolute; left: -9999px } 와 .skip:focus { left: 8px } 두 줄이면 된다.
감춘 것이 정말 감춰졌는가
접은 메뉴, 닫힌 대화 상자, 화면 밖으로 밀어 둔 패널. 이것들이 탭 순서에는 남아 있는 경우가 흔하다. 초점은 가는데 화면에는 아무것도 안 보이니, 사용자는 Tab 을 누를 때마다 초점이 사라지는 것처럼 느낀다.
hidden속성이나display: none— 초점 순서에서 완전히 빠진다inert— 그 안의 모든 것이 초점·클릭·읽기에서 빠진다aria-hidden="true"— 보조기기 트리에서만 사라진다. 초점은 그대로 간다
마지막이 가장 나쁜 조합이다. 초점은 가는데 스크린리더는 아무것도 읽지 않는다. [MDN 의 aria-hidden 문서](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden)도 초점 가능한 요소에 쓰지 말라고 못 박는다. aria-hidden 은 글자로 그린 아이콘처럼 읽을 필요가 없고 초점도 안 받는 것에만 쓴다.
초점이 보여야 한다
outline: none 은 CSS 를 배우는 사람이 가장 먼저 배우는 한 줄이고, 접근성을 가장 크게 망가뜨리는 한 줄이다. 초점 링을 지우면 키보드 사용자는 자기가 어디 있는지 모른 채 Tab 을 누르게 된다.
:focus-visible 이 이 문제를 푼다. 마우스로 눌렀을 때는 안 걸리고 키보드로 왔을 때만 걸린다. 디자인을 해치지 않으면서 필요한 사람에게만 링을 보여 줄 수 있다.
현장에서 만나는 모습
이 서비스에서도 실제로 있었던 것 셋을 적어 둔다.
1. 모달을 열었는데 초점이 모달로 가지 않아서, 스크린리더는 여전히 뒤쪽 본문을 읽고 있었다
2. 접힌 필터 패널이 visibility: hidden 이 아니라 높이 0 이어서, 안의 단추들이 탭 순서에 그대로 남아 있었다
3. 디자인 시스템의 버튼 컴포넌트가 outline: none 을 전역으로 걸고 대체 스타일을 안 줘서, 사이트 전체에서 초점이 보이지 않았다
셋 다 "마우스를 치우고 Tab 만으로 5분" 이면 그 자리에서 나온다. PR 체크리스트에 그 한 줄을 넣는 것이 자동 도구 열 개보다 낫다.
다음 실습에서 할 것
콘솔 화면 하나를 쓰고, Tab 을 눌렀을 때 실제로 가는 차례를 계산기로 확인한다. 건너뛰기 링크를 첫 칸에 놓고, 양수 tabindex 를 없애고, 접은 영역을 탭 순서에서 빼고, 초점 표시를 되살린다.