컴포넌트 패턴
스위치
설정을 켜짐·꺼짐으로 바로 바꾸는 스위치입니다. role="switch"와 aria-checked로 만들며, 체크박스·토글 버튼과의 차이를 정리합니다.
언제 쓰나요
스위치는 “알림 받기”, “다크 모드”, “Wi-Fi”처럼 어떤 기능을 켜거나 끄는 설정에 씁니다. 체크박스와 비슷하지만 의미가 다릅니다. 체크박스는 “이 항목을 선택했다”를, 스위치는 “이 기능이 지금 켜져 있다”를 나타냅니다. 그래서 스위치는 보통 누르는 즉시 설정이 적용됩니다.
네이티브 대안
HTML에는 스위치 전용 요소가 없습니다. 대신 이미 키보드·포커스를 갖춘 요소에 switch 역할을 얹습니다.
<button type="button" role="switch" aria-checked="false">: 버튼 기반. Space와 Enter 모두 동작합니다.<input type="checkbox" role="switch">: 체크박스 기반. 상태는checked로 관리되고 브라우저가aria-checked로 전달하므로aria-checked를 따로 쓰지 않습니다. 폼 제출 값도 그대로 유지됩니다.
스위치 역할을 지원하지 않는 오래된 보조기술은 체크박스나 토글 버튼으로 안내할 수 있습니다. 어느 쪽이든 켜짐·꺼짐 상태는 전달되므로 기능에는 문제가 없습니다.
스위치, 체크박스, 토글 버튼 비교
| 구분 | 스위치 | 체크박스 | 토글 버튼 |
|---|---|---|---|
| 마크업 | role="switch" + aria-checked | <input type="checkbox"> 또는 role="checkbox" + aria-checked | <button> + aria-pressed |
| 상태 값 | true / false | true / false / mixed | true / false / mixed |
| 의미 | 기능·설정이 켜짐 / 꺼짐 | 항목을 선택함 / 선택 안 함 | 버튼이 눌린 상태 / 안 눌린 상태 |
| 적용 시점 | 보통 누르는 즉시 적용 | 보통 폼 제출 때 함께 전송 | 누르는 즉시 적용 |
| 대표 예 | 알림 받기, 비행기 모드 | 약관 동의, 관심 분야 여러 개 선택 | 굵게, 음소거, 재생 목록 반복 |
| 안내 (예) | 알림 받기, 스위치, 켬 | 약관 동의, 체크 상자, 체크 안 됨 | 굵게, 토글 버튼, 눌림 |
세 가지 모두 키보드·보조기술 측면에서는 비슷하게 동작하므로, 화면에 보이는 모양과 의미가 일치하는 것을 고릅니다. 좌우로 미는 모양의 토글이라면 스위치, 네모 체크 표시라면 체크박스, 눌리는 버튼 모양이라면 토글 버튼이 자연스럽습니다.
구조
- 스위치 요소에
switch역할을 줍니다. - 상태는
aria-checked로 알리며 값은true(켜짐) 또는false(꺼짐)만 씁니다. 스위치에는mixed상태가 없습니다. - 이름은 기능을 설명하는 고정된 텍스트입니다(예: “알림 받기”). 버튼 안의 글자,
aria-labelledby,<label>중 하나로 줍니다. - 화면에 “켬/끔” 같은 상태 글자를 함께 보여 줄 수 있지만, 그 글자가 이름에 섞이거나 상태와 함께 이름이 바뀌면 안 됩니다. 상태는
aria-checked가 전달하므로, 보이는 상태 글자는aria-hidden="true"로 숨기거나 이름 계산에서 빠지는 위치에 둡니다. - 여러 스위치를 묶을 때는
<fieldset>이나group으로 묶고 묶음 이름을 줍니다.
<span id="noti-label">알림 받기</span>
<button type="button" role="switch" aria-checked="true"
aria-labelledby="noti-label">
<span class="track" aria-hidden="true"></span>
<span class="state" aria-hidden="true">켬</span>
</button>키보드 동작
| 키 | 동작 |
|---|---|
| Space | 스위치를 켜거나 끕니다. |
| Enter | (선택) 스위치를 켜거나 끕니다. <button> 기반 스위치는 자동으로 지원합니다. |
| Tab / Shift + Tab | 다음·이전 스위치로 이동합니다. 스위치는 각각 Tab 순서에 들어갑니다. |
필요한 ARIA
| 역할·속성 | 붙이는 곳 | 설명 |
|---|---|---|
switch | 스위치 요소 | 켜짐·꺼짐 스위치임을 알립니다. |
aria-checked | role="switch" 요소 | true / false. 필수입니다. 단, <input type="checkbox" role="switch">는 checked로 상태가 전달되므로 쓰지 않습니다. |
aria-labelledby 또는 aria-label | 스위치 | 보이는 레이블이 스위치 바깥에 있을 때 연결합니다. |
aria-describedby | 스위치 | 보충 설명(예: “밤 10시~아침 8시에는 울리지 않습니다”)을 연결합니다. 선택 사항입니다. |
aria-disabled | 스위치 | 바꿀 수 없는 상태임을 알립니다. 네이티브 요소는 disabled를 씁니다. |
aria-pressed와 함께 쓰지 않습니다
switch의 상태는 aria-checked입니다. aria-pressed는 토글 버튼(button)의 상태이므로 스위치에 붙이지 않습니다.
직접 해 보기
왼쪽은 상태에 따라 버튼 글자만 “켜짐/꺼짐”으로 바뀌는 잘못된 구현입니다. 이름 자체가 바뀌어 무엇을 켜는 버튼인지 알 수 없습니다. 가운데와 오른쪽은 올바른 구현입니다. Tab으로 이동해 Space 또는 Enter로 바꿔 보세요.
잘못된 구현
버튼 이름이 “꺼짐”, “켜짐”으로 바뀌고 무엇을 켜는지 연결되어 있지 않습니다. 상태 속성도 없습니다.
올바른 구현: 버튼 기반
올바른 구현: 체크박스 기반
checked 상태가 그대로 스위치 상태로 전달됩니다.
실시간 코드
전체 코드
<span>알림 받기</span>
<button type="button" onclick="this.textContent =
this.textContent === '켜짐' ? '꺼짐' : '켜짐'">꺼짐</button><span id="noti-label">알림 받기</span>
<button type="button" role="switch" id="noti"
aria-checked="false" aria-labelledby="noti-label">
<span class="track" aria-hidden="true"></span>
</button>var sw = document.getElementById('noti');
// <button>은 Space와 Enter 모두 click 이벤트를 발생시킵니다.
sw.addEventListener('click', function () {
var on = sw.getAttribute('aria-checked') === 'true';
sw.setAttribute('aria-checked', String(!on));
// 여기서 실제 설정을 바로 적용합니다. 예: saveSetting('notification', !on);
});<input type="checkbox" role="switch" id="dark">
<label for="dark">다크 모드</label>[role="switch"][aria-checked="true"] .track { background: #1a5fb4; }
/* 상태를 속성 선택자로 그리면 화면과 ARIA 상태가 어긋날 수 없습니다. */
[role="switch"][aria-checked="true"] .track::after { transform: translateX(1.25rem); }div로 만들어야 한다면
<div role="switch">를 쓰면 tabindex="0"을 주고 Space(가능하면 Enter도) 키 처리를 직접 넣어야 합니다. <button>을 쓰면 이 작업이 모두 필요 없습니다.
화면낭독기에서는
| 상황 | 들리는 안내 (예) |
|---|---|
| 꺼진 스위치에 포커스 | 알림 받기, 스위치, 끔 |
| Space로 켬 | 켬 |
| 스위치 역할을 모르는 보조기술 | 알림 받기, 체크 상자, 체크됨(체크박스로 대체 안내) |
| 잘못된 구현에 포커스 후 누름 | 꺼짐, 버튼→ 켜짐, 버튼(무엇을 켜는지 알 수 없음) |
점검 체크리스트
<button>또는<input type="checkbox">에role="switch"를 얹어 만들었습니다.aria-checked값이true/false중 하나이며 실제 설정 상태와 일치합니다(mixed를 쓰지 않음).- 체크박스 기반 스위치에는
aria-checked를 덧붙이지 않았습니다. - 접근 가능한 이름이 기능을 설명하고, 상태가 바뀌어도 이름은 바뀌지 않습니다.
- 보이는 “켬/끔” 글자가 이름에 섞이지 않습니다.
- Space로 바뀌며, 버튼 기반이면 Enter도 동작합니다.
- 켜짐·꺼짐이 색만이 아니라 손잡이 위치·글자로도 구분되고, 테두리가 배경과 3:1 이상 대비됩니다.
- 포커스 표시가 분명하게 보입니다.
관련 WCAG 성공 기준
- 1.3.1 정보와 관계 (A)
- 1.4.1 색의 사용 (A): 켜짐·꺼짐을 색만으로 구분하지 않습니다.
- 1.4.11 비텍스트 대비 (AA)
- 2.1.1 키보드 (A)
- 2.4.7 포커스 표시 (AA)
- 2.5.3 레이블 포함 이름 (A): 보이는 레이블이 접근 가능한 이름에 들어갑니다.
- 4.1.2 이름, 역할, 값 (A)