[Svelte 시리즈 15편] 컴포넌트 접근성 다듬기

English version

접근성은 기능을 더하는 일이 아니라 더 많은 사람이 쓰게 만드는 일입니다.

이번 글에서 새로 나오는 용어

  1. 포커스: 키보드 탐색 중 현재 조작 가능한 요소를 가리키는 상태로, tabindex로 순서를 조절합니다.
  2. aria-label: 화면에는 보이지 않지만 스크린 리더에게 버튼 목적을 설명하는 속성입니다.
  3. 모달(dialog): 페이지 위에 겹쳐 뜨는 대화 상자로, 열렸을 때 포커스가 내부에만 머물러야 합니다.
  4. 대비 미디어 쿼리: @media (prefers-contrast: more)처럼 사용자 설정에 따라 더 강한 윤곽을 적용하는 CSS 규칙입니다.

개념

포커스(focus)는 키보드로 이동할 때 어느 요소가 선택되는지를 나타내는 상태입니다. 버튼이 아닌 요소를 클릭용으로 사용할 때는 role="button"tabindex="0"를 지정해 포커스를 허용해야 합니다. 스크린 리더 레이블은 화면에 글자가 없더라도 버튼의 의도를 설명하는 텍스트입니다. aria-label 또는 aria-labelledby로 제공할 수 있습니다. 모달(modal, 겹쳐 뜨는 대화 상자)은 페이지 위에 떠 있는 UI입니다. 열리는 동안 포커스가 내부에만 머물러야 하며 닫을 때 이전 위치로 돌아가야 합니다.

코드

카드 토글, 모달, 대비 설정을 차례로 살펴봅니다.

<div
  role="button"
  tabindex="0"
  on:click={() => toggle(task)}
  on:keydown={(event) => {
    if (event.key === 'Enter' || event.key === ' ') toggle(task);
  }}
>
  <h3>{task.title}</h3>
</div>

<button aria-label={`${task.title} 완료 상태 전환`}>

</button>

<dialog bind:open={open} aria-labelledby={labelId} aria-describedby={descId}>
  <h2 id={labelId}>과제 상세</h2>
  <p id={descId}>상태, 담당자, 첨부 파일을 확인하세요.</p>
  <button on:click={() => (open = false)}>닫기</button>
</dialog>
:global(.card) {
  background: var(--surface);
}

@media (prefers-contrast: more) {
  :global(.card) {
    border: 2px solid var(--outline-strong);
  }
}

아이콘 버튼에는 aria-label을 붙이고, dialog 요소에는 제목과 설명을 연결해 스크린 리더가 내용을 읽어 주도록 합니다. 대비 미디어 쿼리를 활용하면 고대비 모드를 선호하는 사용자에게 더 강한 윤곽을 제공합니다.

왜 중요한가

학교 프로젝트는 다양한 상황의 학생이 사용합니다. 키보드만 사용하는 친구도 있어야 하고, 화면 확대나 고대비 모드를 켠 사용자도 서비스에 접근할 수 있어야 합니다. 기본적인 ARIA 속성과 포커스 순서를 점검하는 것만으로도 문의와 버그를 크게 줄일 수 있습니다.

실습

  • 따라 하기: 카드 토글 컴포넌트에 role, tabindex, 키보드 핸들러를 추가해 Tab 키로 조작한다.
  • 확장하기: dialog 기반 모달을 만들어 bind:open 상태와 닫기 버튼을 테스트한다.
  • 디버깅: 스크린 리더가 조용하면 aria-live 영역과 aria-label 연결이 올바른지 확인한다.
  • 완료 기준: Tab 키로 주요 기능에 접근할 수 있고 고대비 모드에서도 카드 윤곽이 유지된다.

마무리

접근성 체크는 꾸준히 반복해야 합니다. 다음 편에서는 이러한 컴포넌트를 테스트 자동화 도구로 검증해 품질을 높여 봅니다.

💬 댓글

이 글에 대한 의견을 남겨주세요