ARIA 접근성 가이드

컴포넌트 패턴

체크박스

켜짐·꺼짐 두 가지 상태를 고르는 이중 상태 체크박스와, 하위 항목 일부만 선택되었음을 알리는 혼합(mixed) 상태 “전체 선택” 체크박스를 만드는 방법입니다.

언제 쓰나요

체크박스는 서로 독립적인 선택지를 하나씩 켜고 끌 때 씁니다. 여러 개를 동시에 고를 수 있고, 아무것도 고르지 않아도 됩니다. 체크박스에는 두 종류가 있습니다.

  • 이중 상태(dual-state) 체크박스: 선택됨(true)과 선택 안 됨(false) 두 상태만 가집니다. 대부분의 체크박스가 여기에 해당합니다.
  • 삼중 상태(tri-state) 체크박스: 여기에 혼합(mixed) 상태가 더해집니다. 보통 하위 체크박스 묶음을 한 번에 제어하는 “전체 선택” 체크박스에 쓰며, 하위 항목이 일부만 선택되었을 때 혼합 상태가 됩니다.

네이티브 체크박스를 먼저 쓰세요

<input type="checkbox">는 키보드 조작, 포커스, 상태 안내, 폼 제출을 모두 기본으로 제공합니다. 혼합 상태도 네이티브로 만들 수 있습니다. HTML 속성은 없지만 JavaScript로 input.indeterminate = true를 설정하면 브라우저가 보조기술에 혼합 상태로 전달합니다. 디자인 때문에 직접 만들어야 할 때만 checkbox 역할을 씁니다.

다음 경우에는 다른 패턴이 더 알맞습니다.

  • 여러 선택지 중 하나만 고른다면 라디오 그룹을 씁니다.
  • 누르는 즉시 설정이 켜지고 꺼지는 기능(예: 알림 켜기)이라면 스위치가 더 의미에 맞습니다.
  • “굵게”처럼 눌림 상태가 있는 도구 버튼이라면 토글 버튼(aria-pressed)을 씁니다.

구조

  • 체크박스 요소에 checkbox 역할을 줍니다. 네이티브 <input type="checkbox">는 이미 이 역할을 가집니다.
  • 현재 상태를 aria-checked로 알립니다. 값은 true, false, 그리고 삼중 상태라면 mixed입니다.
  • 체크박스의 이름은 보이는 레이블에서 가져옵니다. 직접 만든 체크박스는 내용 텍스트로 이름이 정해지거나, aria-labelledby로 레이블을 연결합니다. 네이티브는 <label>로 연결합니다.
  • 관련된 체크박스 묶음은 <fieldset>+<legend>, 또는 group 역할과 aria-labelledby로 묶어 묶음 이름을 줍니다.
  • “전체 선택” 체크박스는 aria-controls에 자신이 제어하는 하위 체크박스들의 id를 공백으로 나열할 수 있습니다.
역할과 속성 관계
<div role="group" aria-labelledby="toppings-title">
  <h3 id="toppings-title">토핑</h3>
  <div role="checkbox" tabindex="0" aria-checked="mixed"
       aria-controls="t1 t2 t3">전체 선택</div>
  <input type="checkbox" id="t1"> <label for="t1">치즈</label>
  <input type="checkbox" id="t2" checked> <label for="t2">버섯</label>
  <input type="checkbox" id="t3"> <label for="t3">올리브</label>
</div>

키보드 동작

키보드 동작
키동작
Space포커스된 체크박스의 상태를 바꿉니다. 혼합 상태 체크박스는 아래 순서로 상태가 돌아갑니다.
Tab / Shift + Tab다음·이전 체크박스로 이동합니다. 체크박스는 묶음 안에서도 각각 Tab 순서에 들어갑니다(라디오 그룹과 다른 점).

APG 예시의 혼합 상태 “전체 선택” 체크박스는 다음과 같이 순환합니다.

  1. 혼합(일부 선택) → 누르면 모두 선택(true)
  2. 모두 선택 → 누르면 모두 해제(false)
  3. 모두 해제 → 누르면 사용자가 마지막으로 만든 일부 선택 조합으로 복원(mixed)

마지막 단계에서 되살릴 일부 선택 조합이 없다면 모두 선택으로 바꾸는 것이 일반적입니다. 혼합 상태로 돌아가는 단계를 아예 빼고 “모두 선택 ↔ 모두 해제”만 오가도록 만들어도 됩니다. 어떤 방식이든 하위 체크박스 상태와 aria-checked 값이 항상 일치해야 합니다.

Enter 키는?

APG 체크박스 패턴에서 필수 키는 Space뿐입니다. 네이티브 체크박스도 Enter로는 상태가 바뀌지 않으며, 폼 안에서는 Enter가 폼 제출로 이어질 수 있습니다. 직접 만든 체크박스도 네이티브와 같게 Space로 동작하게 만듭니다.

필요한 ARIA

필요한 ARIA
역할·속성붙이는 곳설명
checkbox체크박스 요소체크박스임을 알립니다. 네이티브 <input type="checkbox">에는 필요 없습니다.
aria-checkedrole="checkbox" 요소true / false / mixed. 직접 만든 체크박스에서 필수입니다. 네이티브 체크박스에는 쓰지 않고 checked, indeterminate를 씁니다.
aria-labelledby 또는 aria-label체크박스, 묶음보이는 레이블이 다른 요소에 있을 때 연결합니다. 체크박스 안에 텍스트가 있으면 그 텍스트가 이름이 됩니다.
group체크박스 묶음<fieldset>을 쓸 수 없을 때 관련 체크박스를 묶고 이름을 줍니다.
aria-controls“전체 선택” 체크박스제어하는 하위 체크박스들의 id 목록. 선택 사항입니다.
aria-describedby체크박스추가 설명(예: 약관 요약)을 연결합니다. 선택 사항입니다.
aria-disabled체크박스비활성 상태를 알립니다. 네이티브는 disabled 속성을 씁니다.

직접 해 보기

왼쪽은 클릭만 처리하는 흔한 잘못된 구현이고, 오른쪽은 혼합 상태를 지원하는 올바른 구현입니다. Tab으로 이동하고 Space로 눌러 보세요. 하위 항목을 하나만 해제하면 “전체 선택”이 혼합 상태로 바뀌는 것을 실시간 코드에서 확인할 수 있습니다.

잘못된 구현

이용약관에 동의

역할·상태·포커스가 없어 키보드로 닿을 수 없고, 화면낭독기는 일반 텍스트로만 읽습니다.

올바른 구현: 피자 토핑

전체 코드

이중 상태 체크박스

비권장비권장: div에 클릭만 처리
<div class="checkbox" onclick="this.classList.toggle('on')">
  이용약관에 동의
</div>
권장권장: 네이티브 체크박스
<input type="checkbox" id="agree">
<label for="agree">이용약관에 동의</label>

직접 만들어야 한다면 역할, 상태, 포커스, 키보드를 모두 갖춥니다.

직접 만든 이중 상태 체크박스 (HTML)
<div role="checkbox" tabindex="0" aria-checked="false" id="agree">
  이용약관에 동의
</div>
직접 만든 이중 상태 체크박스 (JavaScript)
var box = document.getElementById('agree');

function toggle() {
  var on = box.getAttribute('aria-checked') === 'true';
  box.setAttribute('aria-checked', String(!on));
}

box.addEventListener('click', toggle);
box.addEventListener('keydown', function (e) {
  if (e.key === ' ') {
    e.preventDefault();   // Space의 기본 동작(스크롤) 막기
    toggle();
  }
});

혼합 상태 “전체 선택”

HTML
<fieldset>
  <legend>피자 토핑</legend>
  <div role="checkbox" id="all" tabindex="0" aria-checked="mixed"
       aria-controls="opt1 opt2 opt3">전체 선택</div>
  <ul>
    <li><input type="checkbox" id="opt1"> <label for="opt1">치즈 추가</label></li>
    <li><input type="checkbox" id="opt2" checked> <label for="opt2">버섯</label></li>
    <li><input type="checkbox" id="opt3"> <label for="opt3">올리브</label></li>
  </ul>
</fieldset>
JavaScript
var all = document.getElementById('all');
var items = ['opt1', 'opt2', 'opt3'].map(function (id) {
  return document.getElementById(id);
});
var saved = items.map(function (el) { return el.checked; }); // 마지막 일부 선택 조합

function syncAll() {
  var n = items.filter(function (el) { return el.checked; }).length;
  all.setAttribute('aria-checked',
    n === 0 ? 'false' : n === items.length ? 'true' : 'mixed');
}

function toggleAll() {
  var state = all.getAttribute('aria-checked');
  var partial = saved.some(Boolean) && !saved.every(Boolean);
  items.forEach(function (el, i) {
    if (state === 'mixed') el.checked = true;          // 혼합 → 모두 선택
    else if (state === 'true') el.checked = false;     // 모두 선택 → 모두 해제
    else el.checked = partial ? saved[i] : true;       // 모두 해제 → 일부 선택 복원
  });
  syncAll();
}

all.addEventListener('click', toggleAll);
all.addEventListener('keydown', function (e) {
  if (e.key === ' ') { e.preventDefault(); toggleAll(); }
});
items.forEach(function (el) {
  el.addEventListener('change', function () {
    saved = items.map(function (x) { return x.checked; });
    syncAll();
  });
});
syncAll();

네이티브로 혼합 상태 만들기

“전체 선택”도 네이티브 체크박스로 만들 수 있습니다. 하위 항목이 일부만 선택되면 all.indeterminate = true, 그 밖에는 all.indeterminate = false와 함께 all.checked를 맞춥니다. 사용자가 누르면 브라우저가 indeterminate를 false로 바꾸므로, change 이벤트에서 하위 항목을 함께 갱신합니다.

화면낭독기에서는

화면낭독기 안내 예시 (표현은 화면낭독기·브라우저·버전에 따라 조금씩 다릅니다)
상황들리는 안내 (예)
묶음 안으로 처음 이동피자 토핑, 그룹
혼합 상태 “전체 선택”에 포커스전체 선택, 체크 상자, 부분 선택됨
Space를 눌러 모두 선택체크됨
하위 항목에 포커스버섯, 체크 상자, 체크됨
잘못된 구현(div)에 도달이용약관에 동의 (역할·상태 없이 텍스트로만 읽힘, Tab으로는 도달 불가)

점검 체크리스트

  • 가능하면 네이티브 <input type="checkbox">와 <label>을 썼습니다.
  • 직접 만든 체크박스에 role="checkbox", tabindex="0", aria-checked가 모두 있습니다.
  • Space로 상태가 바뀌고, 이때 페이지가 스크롤되지 않습니다.
  • 모든 체크박스에 보이는 레이블과 같은 접근 가능한 이름이 있습니다.
  • 관련 체크박스 묶음에 fieldset/legend 또는 role="group"과 이름이 있습니다.
  • “전체 선택”의 aria-checked(또는 indeterminate)가 하위 항목 상태와 항상 일치합니다.
  • 네이티브 체크박스에 aria-checked를 덧붙이지 않았습니다.
  • 선택·해제·혼합 상태가 색만이 아니라 체크 표시·대시 같은 모양으로도 구분되고, 고대비 모드에서도 보입니다.
  • 포커스 표시가 분명하게 보입니다.

관련 WCAG 성공 기준

참고 자료