인터렉티브 UI(버튼,링크,인풋 등)에 적절한 tabIndex를 설정하는 것은 UX와 웹 접근성에 유리합니다.
저의 문제 상황은 다음과 같습니다.
위와 같이 퀴즈 페이지가 있습니다.
마우스를 사용하지 못하여, tab을 통해 인터렉티브 UI에 focusing을 잡으려 합니다.
위와 같이 인터렉티브한 UI가 페이지내에 존재하는데요.
위에서 처음 tab을 누르면 어떤 UI에 포커싱이 잡힐까요?
로고에 제일 먼저 포커싱이 잡힙니다.
로고는 링크이며 누르면 메인 화면으로 이동합니다.
하지만 사용자가 위 화면에서 탭을 누를 때, 기대하는 동작은 무엇일까요?
아마도 아래와 같이 답안 체크박스에 포커싱이 되기를 기대할 것입니다.
하지만 현재 상태에서는 답안 체크박스까지 이동하기 위해서는 두 번의 탭을 거쳐야하죠.
이는 불편한 경험을 줄 수 있습니다.
사용자의 기대 시나리오는 답안 체크에 포커싱을 한 뒤, 채점 버튼에 포커싱이 가기를 기대할 것입니다.
그렇다면 어떻게 위 시나리오대로 설정할 수 있을까요?
tabIndex
를 사용하면 됩니다.
tabIndex는 주로 Tab 키를 사용하는 연속적인 키보드 탐색에서 어느 순서에 위치할지 지정합니다.
tabIndex값 설정을 통해 tab을 누를 때, 인터렉션 요소 탐색 우선 순위를 설정할 수 있습니다.
양의 정숫값은 요소를 연속 키보드 탐색으로 접근할 수 있으며, 그 순서는 해당 값으로 지정하겠다는 것을 뜻합니다.
즉, tabindex="4"인 요소는 tabindex="5"와 tabindex="0"인 요소 이전에, 그러나 tabindex="3"인 요소 이후에 접근할 수 있습니다.
다수의 요소가 하나의 값을 공유할 경우 그 안에서 문서 소스 코드의 순서를 따릅니다. 최댓값은 32767입니다.
// 체크박스
<input
tabIndex={0}
className={"accent-orange-600 w-5 h-5"}
type={"checkbox"}
/>
// 일반 인풋
<input
tabIndex={1}
className={"accent-orange-600 w-5 h-5"}
type={"text"}
/>
// 숫자 인풋
<input
tabIndex={2}
className={"accent-orange-600 w-5 h-5"}
type={"text"}
/>
위처럼 체크박스와 인풋이 있다고 했을 때, tab으로 포커싱을 잡을시, 체크박스가 앞쪽에 위치했음에도 불구하고 일반 텍스트 인풋이 먼저 잡히고 그 다음으로 숫자 인풋이 잡히고 그 다음으로 체크박스가 잡히게 되죠.
따라서 시나리오대로 tab 우선순위를 설정하기 위해 tabIndex를 적절히 설정해보겠습니다.
우선 위 헤더의 우선순위는 제일 마지막이 되어야할 것입니다.
따라서 그대로 두겠습니다.
tabIndex의 기본값은 0입니다. 인터렉티브 UI(a,button,input 등)에는 기본적으로 0으로 설정되어있습니다.
그 다음으로 체크박스가 제일 우선순위 1위가 되어야합니다.
따라서 해당 UI에 tabIndex를 1
로 설정해보겠습니다.
import MultipleChoiceAnswers from "@/app/(page)/quiz/(page)/[detailUrl]/_components/client/quizAnswerForm/multipleChoiceAnswers";
import { MultipleChoiceContent } from "@/app/services/quiz/types";
import React from "react";
// 퀴즈 답안(객관식, 주관식,OX) 컴포넌트
function QuizAnswers({
quizType,
quizMultipleChoiceContents,
setUserAnswer,
}: {
quizType: string;
quizMultipleChoiceContents: MultipleChoiceContent[];
setUserAnswer: React.Dispatch<
React.SetStateAction<number[]>
>;
}) {
return (
<>
{quizType === "MULTIPLE_CHOICE" && (
<MultipleChoiceAnswers
tabIndex={1}
quizMultipleChoiceContents={
quizMultipleChoiceContents
}
setUserAnswer={setUserAnswer}
/>
)}
</>
);
}
export default QuizAnswers;
MultipleChoiceAnswers
컴포넌트 하위 컴포넌트들에 props로 tabIndex를 순차적으로 적용하여 결국에는 내부적으로는 input checkbox에 전달하고 있습니다.
그 다음으로 채점버튼이 우선순위가 되어야할 것입니다.
때문에 채점 버튼에 tabIndex를 2로 설정하겠습니다.
import PrimaryButton from "@/app/_components/button/primaryButton";
import PlaceOnCenter from "@/app/_layout/placeOnCenter";
import React from "react";
// 채점 버튼
function BeforeCheckButton({
userAnswer,
}: {
userAnswer: number[];
}) {
return (
<PlaceOnCenter>
<PrimaryButton
tabIndex={2}
disabled={userAnswer.length === 0}
type={"submit"}
color={"primary"}
>
채점
</PrimaryButton>
</PlaceOnCenter>
);
}
export default BeforeCheckButton;
위와 같이 설정하면 우리가 원하는 시나리오 대로 tab을 누를 시, 체크박스가 먼저 포커싱이 잡히게 되고 그 다음으로 채점 버튼일 잡히게 됩니다.
위와 같이,컨텐츠의 인터렉션 기대 시나리오대로 적절한 tabIndex 우선순위를 설정하는 것이 중요할 것 같습니다.