의미론적 HTML 요소의 DOM 위치에 따라 정해지는 기본 탭 순서는 편리하지만 탭 순서를 수정해야 할 때도 있습니다. HTML에서 요소를 이동하는 것이 이상적이지만 현실적이지 않을 수 있습니다. 이 경우 tabindex
HTML 속성을 사용하여 요소의 탭 위치를 명시적으로 설정할 수 있습니다.
tabindex
는 모든 요소에 적용할 수 있지만 모든 요소에서 반드시 유용한 것은 아니며 다양한 정수 값을 취합니다. tabindex
를 사용하면 포커스 가능한 페이지 요소에 대해 명시적 순서를 지정하고, 포커스 불가능한 요소를 탭 순서에 삽입하고, 탭 순서에서 요소를 제거할 수 있습니다. 예를 들면 다음과 같습니다.
tabindex="0"
: 자연 탭 순서에 요소를 삽입합니다. 탭 키를 눌러 요소에 포커스를 맞출 수 있으며 focus()
메서드를 호출하여 요소에 포커스를 맞출 수 있습니다.
tabindex="-1"
: 일반적인 탭 순서에서 요소를 삭제하더라도 focus()
메서드를 호출하여 계속 요소에 포커스를 맞출 수 있습니다.
tabindex="5"
: tabindex가 0
보다 크면 요소가 자연 탭 순서의 앞으로 이동합니다. tabindex가 0
보다 큰 요소가 여러 개 있다면 탭 순서는 0보다 크고 가장 낮은 값부터 시작해서 계속 높은 값으로 이동합니다.
헤더, 이미지 또는 기사 제목과 같은 비입력 요소의 경우는 더욱 그렇습니다. 가능하면 DOM 시퀀스가 논리적인 탭 순서를 제공하도록 소스 코드를 적절히 배치하는 것이 좋습니다. tabindex
를 사용한다면 버튼, 탭, 드롭다운, 텍스트 필드와 같이 사용자가 입력을 제공할 것으로 예상되는 요소인 맞춤 대화형 컨트롤로 제한하세요.
상호작용이 있는 콘텐츠에만 tabindex
를 추가합니다. 주요 이미지와 같이 중요한 콘텐츠라도 스크린 리더 사용자는 포커스를 추가하지 않고도 이를 이해할 수 있습니다.
페이지 수준에서 포커스 관리
원활한 사용자 환경을 위해 tabindex
가 필요한 경우도 있습니다. 예를 들어 여러 콘텐츠 섹션을 한 페이지에 통합하는 강력한 단일 페이지를 빌드하는 경우 모든 콘텐츠가 동시에 표시되지는 않습니다. 즉, 페이지 새로고침 없이 내비게이션 링크가 표시되는 콘텐츠를 변경할 수 있습니다.
이 경우 선택한 콘텐츠 영역을 식별하고 tabindex
를 -1
로 지정한 다음 focus
메서드를 호출합니다. 이렇게 하면 콘텐츠가 자연스러운 탭 순서에 표시되지 않습니다. 포커스 관리라고 하는 이 기법은 사용자가 인지한 컨텍스트를 사이트의 시각적 콘텐츠와 일치된 상태로 유지합니다.
구성요소에서 포커스 관리
맞춤 구성요소와 같이 컨트롤 수준에서 포커스를 관리해야 하는 경우도 있습니다.
예를 들어 select
요소는 기본 포커스를 받을 수 있지만 포커스를 받으면 화살표 키를 사용하여 선택 가능한 추가 옵션을 노출할 수 있습니다.
맞춤 select
요소를 빌드하는 경우 키보드 사용자가 계속 컨트롤과 상호작용할 수 있도록 이 동작을 복제하는 것이 중요합니다.
어떤 키보드 동작을 구현할지 알기 어려울 수 있습니다. Accessible Rich Internet Applications(ARIA) Authoring Practices 가이드에는 구성요소 유형과 구성요소가 지원하는 키보드 작업의 종류가 나와 있습니다.
라디오 버튼 모음과 비슷하지만 개발자 자신의 고유한 모양과 동작을 구현하는 맞춤 요소를 만들고 고유한 외형과 동작을 적용할 수 있습니다.
<radio-group>
<radio-button>Water</radio-button>
<radio-button>Coffee</radio-button>
<radio-button>Tea</radio-button>
<radio-button>Cola</radio-button>
<radio-button>Ginger Ale</radio-button>
</radio-group>
어떤 키보드 지원이 필요한지 알아보려면 ARIA 작성 방법 가이드를 참고하세요. 섹션 2에는 디자인 패턴 목록이 있으며, 이 목록에는 라디오 그룹의 특성표를 비롯하여 새 요소와 가장 근접하게 일치하는 기존 구성요소가 포함되어 있습니다.
지원해야 할 일반적인 키보드 동작 중 하나는 위쪽/아래쪽/왼쪽/오른쪽 화살표 키입니다. 새 구성요소에 이 동작을 추가하려면 이동 tabindex라는 기법을 사용합니다.
현재 활성 상태인 하위 항목을 제외한 모든 하위 항목에 대해 tabindex
를 -1로 설정하면 이동 tabindex가 작동합니다.
<radio-group>
<radio-button tabindex="0">Water</radio-button>
<radio-button tabindex="-1">Coffee</radio-button>
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
구성요소는 키보드 이벤트 리스너를 사용하여 사용자가 어떤 키를 눌렀는지 확인합니다. 그러면 이전에 포커스를 맞춘 하위 요소의 tabindex
를 -1로 설정하고 포커스를 맞출 하위 요소의 tabindex
를 0으로 설정한 다음 여기에 포커스 메서드를 호출합니다.
<radio-group>
<!-- Assuming the user pressed the down arrow, we'll focus the next available child -->
<radio-button tabindex="-1">Water</radio-button>
<radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
사용자가 마지막 (또는 첫 번째, 포커스 이동 방향에 따라 첫 번째) 하위 요소에 도달하면 포커스는 첫 번째 (또는 마지막) 하위 요소로 되돌아갑니다.
다음 예를 시도해 보세요. DevTools에서 이 요소를 검사해 보면서 tabindex가 라디오 버튼 사이를 이동하는지 살펴보세요.
모달 및 키보드 트랩
포커스를 수동으로 관리하면 복잡한 상황이 발생할 수 있으므로 피하는 것이 좋습니다. 예를 들어 포커스 관리를 시도하고 탭 동작을 캡처하지만 사용자가 완료할 때까지는 나갈 수 없게 되어 있는 자동 완성 위젯을 생각해 보세요. 이를 키보드 트랩이라고 하며 사용자 입장에선 무척 난처할 수 있습니다.
WCAG의 섹션 2.1.2에는 키보드 포커스를 하나의 특정 페이지 요소에 고정하거나 가두어서는 안 된다고 명시되어 있습니다. 사용자가 키보드만 사용해 모든 페이지 요소를 자유롭게 탐색할 수 있어야 합니다.
모달은 이 규칙의 예외입니다. 하지만 모달을 만들 때는 tabindex
를 사용하지 않는 것이 좋습니다. inert
를 사용하면 사용자가 실수로 요소와 상호작용 (의도적인 키보드 트랩)하는 것을 방지할 수 있습니다. 기본적으로 비활성 상태인 <dialog>
요소를 사용하여 사용자를 위한 모달을 만들고 모달 외부의 클릭과 탭을 차단합니다. 이렇게 하면 사용자가 필수 선택사항에 집중할 수 있습니다.