
native HTML 요소가 포커스, 키보드 지원, 기본 제공 의미 체계를 제공하기 때문에, 지금까지는 네이티브 HTML 요소 사용을 권장해왔다.
하지만 단순한 레이아웃과 네이티브 HTML로는 작업을 수행하지 못할 때가 있다.
예를 들어서, 현재는 매우 일반적인 UI 구조인 팝업 메뉴에 대해 표준화된 HTML 요소가 없다.
또한, '사용자가 가급적 빨리 이 점에 대해서 알아야 함'과 같은 의미 체계상 특성을 제공하는 HTML 요소 역시 없다.
그럼 HTML이 단독으로 표현할 수 없는 의미 체계를 표현은 어떻게 해야할까?
Web Accessibility Initiative(www.w3.org/TR/wai-aria/)의 Accessible Rich Internet Applications (이른 바 ARIA)
는 native HTML로 관리할 수 없는 접근성 문제가 있는 영역을 연결하기에 적합하다.
ARIA는 요소가 접근성 트리로 변환되는 방식을 수정하는 속성을 지정할 수 있도록 허용하는 방식으로 작동한다.
<li tabindex="0" class="checkbox" checked>
Receive promotional offers
</li>
위 코드를 살펴보자.
이렇게 하면 시력이 정상인 사용자에게는 알맞지만, 스크린 리더는 이 요소가 확인란이라는 점을 알려주지 못하므로 시력이 나쁜 사용자가 이 요소를 완전히 놓칠 수 있다.
하지만 ARIA 속성을 사용하면 요소에 누락된 정보를 부여할 수 있으므로, 스크린 리더가 올바로 해석할 수 있다.
여기서는 요소를 확인란으로 명시적으로 식별하고 확인란이 기본적으로 선택되어 있음을 나타내기 위해서 role, aria-checked 속성을 추가하면된다.
그러면 접근성 트리에 목록 항목이 추가되고 스크린 리더가 확인란이라고 올바르게 알려준다.
<li tabindex="0" class="checkbox" role="checkbox" checked aria-checked="true">
Receive promotional offers
</li>
properties(속성) &states(상태)
요소가 기본적으로 가진 특징or상황, "aria-"라는 접두어를 갖는다. (상태: 요소의 상황을 나타내므로 application 실행 중 자주 바뀐다.) (속성:상대적으로 바뀌는 정도가 드물다.)
1) 필수항목 속성
- aria-required를 true로 지정하여 스크린리더사용자에게 해당 요소가 필수적으로 입력되어야 함을 전달.
<input type="checkout" aria-required="true">
2) 속성
<div role="tabpanel" aria-expanded="true">
- 확장되어 있는 상태의 탭 패널
- aria-expanded로 현재 탭 패널이 펼쳐짐(활성화)상태 라는 것을전달. (false=접힌상태)
<input type="text" aria-invalid="true">
- input에 입력된 값이 유효한지 판단하기 위한 것 true는 오류가 발생한 상태라는 것을 전달.
<button aria-pressed="true">
- 선택된 상태의 토글버튼: pressed를 이용하여 해당 요소를 토글버튼으로 정의하여 준다. true는 누른상태, false는 누르지않은 상태, mixed는 부분적으로 눌린상태이다.true는 현재 버튼이 눌림상태라는 것을 전달한다.
ARIA 사용규칙
1) HTML5 섹션요소와 중복 사용하지 않는다.
<nav role="navigation"> <nav>(x) 동일한 역할을 하는 네이티브요소와의 중복마크업.
2) native요소의 의미, 기능 변경을 하지 않는다.
<h1 role="button">버튼</h1> -> h1의 본래의 기능을 버리고 다른 역할(버튼)을 부여한 잘못된 케이스.
대안)
<h1><button>버튼</button></h1>
<h1><span role="button">버튼</span></h1>
3) 키보드 사용성 보장
:상호작용이 가능한 대화형 UI(사용자가 클릭 가능한 정보, 탭, drag&drop, slide, scroll등이 필요한 기능)를 span이나 div로 마크업 후, role="button"속성 부여시, 사용자가 키보드로 접근이 가능하도록 해야 한다.
<span role="button">버튼</span>
위의 예시는 키보드로 포커싱이 불가하다 . 따라서 아래와 같이 설정해 주어야 한다.
<span role="button" tabindex="0">버튼</span>
tabindex속성을 0 으로 설정하여 콘텐츠의 선형화 순서대로 키보드 포커싱이 진입한다. "0"보다 값이 작으면 키보드 포커스를 받지 못하도록 설정된다.
키보드탭 접근 가능여부
태그접근가능여부
<div> | x |
<span> | x |
<p> | x |
<button> | o |
<a> | o |
<input> | o |
버튼으로 사용하기위해서 role="button"속성 부여시 tabindex도 반드시 설정해서 접근이 가능하도록 한다.
4) aria-hidden="true"
aria-hidden 사용시
: 스크린리더(보조기기)가 접근하는 것은 원치 않지만, 시각적으로 디자인을 주기 위해서 보여지게 하고 싶은경우에 사용한다.
반대로 눈에는 보이지 않게 하는데 보조기기 사용자가 정보에 접근 할 수 있도록 하기 위해서는 a11y-hidden을 지정해준다.
5) 레이블(label) 제공
- 모든 대화형 UI의 경우, 반드시 label을 제공해야 한다. aria-label 또는 aria-labelledby로 label제공이 가능하다.
<div>
<div id="user-name">이름</div>
<input type="text" id="name" aria-labelledby="user-name">
</div>
6) 유효성 검사
의미를 가진 semantic요소와의 충돌을 방지한다.
ex) 문법오류, h1에 적절하지 않은 역할을 부여한 경우.
<h1 role="button">
text
</h1>
ARIA
- Accessible Rich Internet Application
- 접근 가능한 고기능 인터넷 애플리케이션
- HTML의 접근성 문제를 보완하는 W3C 명세
- HTML에 '역할(roles), 상태(states), 속성(properties)' 정보를 부여하여 보조기기의 웹 문서 접근을 지원
- 장애인 사용자들이 웹 문서를 좀더 생동감있게 이해하고 사용하는데 도움을 준다.
접근성 좋은 IT 서비스는 현실 세계를 보다 더 자유롭게 누릴 수 있도록 한다.
Return to HTML
- 웹 접근성 문제의 80%는 HTML
- 대부분의 WAI-ARIA 명세는 HTML 요소와 속성을 흉내내는 것
- ARIA를 사용하기에 앞서 HTML을 의미있게 사용했는지 검토할것
역할(Roles)
<element role="tablist"> ✨
<element role="tab"> ✨
<element role="tabpanel"> ✨
<element role="tooltip"> ✨
<element role="status"> ✨
<element role="alert"> ✨
<element role="alertdialog"> == <dialog>
<element role="dialog"> == <dialog>
<element role="navigation"> == <nav>
<element role="complementary"> == <aside>
<element role="none"> == <div>
role="tablist|tab|tabpanel"
- 탭 스타일을 의미하는 것이 아님
- 현재 페이지 내용에 색인을 제공하는 구조(tablist, tab, tabpanel)를 의미
- 사이트 탐색 도구에 해당하는 요소는 nav > h2 + ul 또는 aside > h2 + ul 구조로 마크업
- tablist: 하나 하나의 tab들을 Grouping 역할
- tab: 말그대로 tab 역할
- tabpanel: tab 아래에 표시하는 콘텐츠
예시
<div class="weekly">
<div role="tablist">
<button id="mon-anchor" aria-controls="mon" role="tab" aria-selected="true">월</button>
<button id="tue-anchor" aria-controls="tue" role="tab" >화</button>
</div>
<div id="mon" tabindex="0" role="tabpanel" aria-labelledby="mon-anchor">
월요일 컨텐츠
</div>
<div id="tue" tabindex="0" role="tabpanel" aria-labelledby="tue-anchor">
화요일 컨텐츠
</div>
</div>
role="tooltip"
- 보통 포커스를 받으면 설명을 표시하는 문맥 팝업
- 툴팁은 포커스 받지 않아야 한다. ESC 또는 마우스를 빼면 사라져야 한다.
- 참조하는 컨트롤(a, button)에는 aria-describedby="ID reference list" 속성으로 연결
"ID reference list"
여러개의 아이디 값 목록을 공백으로 분리해서 연결
예시
<!-- O: 인풋 툴팁 -->
<label for="tel">전화번호</label>
<input id="tel" type="tel" aria-describedby="TIP-TEL">
<p id="TIP-TEL" role="tooltip" hidden>하이픈(-) 없이 숫자만 입력.</p>
<!-- O: 버튼 퉅립 -->
<button aria-describedby="TIP-DEL">게시물 삭제</button>
<p id="TIP-DEL" role="tooltip" hidden>게시물 삭제 후 복원할 수 없음</p>
role="status"
- 실시간 결과 정보. role="alert" 만큼 중요하지 않음
- 이 요소를 갱신할 때 포커스를 받지 않아야 한다.
- 이 역할을 선언하면 자동으로 aria-live="polite", aria-atomic="true" 속성이 할당됨
aria-live="polite"
보조기기는 현재 전달하는 음성이나 내용을 중단하지않고 기다렸다가 그 음성 낭독이 끝나면 현재 설명하려고하는 내용을 음성으로 전달
aria-atomic="true"
내용이 갱신되었을 때 부분적으로 갱신되었다하더라도 atomic="true" 영역 전체를 다시 읽어줌
예시
- toast popup 메시지에 사용하자
- 의미론에 따라 role="status" 대신 <output>요소 사용 가능
<!-- O: 결과 메시지 -->
<p role="status">회원가입 양식 전송완료.</p>
<p role="status">10개의 검색 결과.</p>
<p role="status">장바구니에 5개의 항목</p>
<output>회원가입 양식 전송완료.</output>
<output>10개의 검색 결과.</output>
<output>장바구니에 5개의 항목.</output>
role="alert"
- 시간에 민감하고 중요한(오류, 제안) 실시간 콘텐츠
- 이 요소를 갱신할 때 포커스를 받지 않아야 한다.
- aira-live="assertive" 속성과 aria-atomic="true" 속성 자동 할당
aria-live="asertive""
굉장히 긴급한 메시지기에 현재 진행중이던 과업, 음성 낭독을 중단하고 그 즉시 오류 내용을 사용자에게 알림
예시
- 무언가 실패했을때 혹은 시기적으로 민감한 상태였을때 alert 사용
<!-- O: 오류 메시지 -->
<p role="alert">우편번호 입력 오류.</p>
<!-- O: 제안 메시지 -->
<p role="alert">로그인 후, 이용 가능.</p>
상태(States)
<element aria-current="">
<!-- value: page|step|location|date|time|true|false(default) -->
<element aria-selected="">
<!-- value: true|false|undefined(default) -->
<element aria-haspopup="">
<!-- value: true|menu|dialog|listbox|tree|grid|false(default)-->
<element aria-expanded="">
<!-- value: true|false|undefined(default) -->
<element aria-hidden="">
<!-- value: true|false|undefined(default) -->
<element aria-invalid="">
<!-- value: true|false(default)|grammar|spelling -->
aria-current
value
page | step | location | date | time | true | false(default)
현재 맥락과 일치하는 항목을 의미 (보통 a, button에 사용)
- page: 현재 '페이지'와 일치하는 시각적으로 강조한 링크
- step: 현재 '단계'와 일치하는 시각적으로 강조한 링크
- location: 플로우 차트에서 현재 '위치'와 일치하는 시각적으로 강조한 이미지
- date: 달력에서 현재 '날짜'와 일치하는 날짜
- time: 시간표에서 현재 '시간'과 일치하는 시간
- true: 항목이 세트 내 현재 맥락과 일치함
- false(default): 항목이 세트내 현재 맥락과 일치하지 않음. 속성 또는 값을 선언하지 않은 경우 초기값
예시
<!-- O: 현재 페이지 링크 -->
<nav>
<h2>글로벌 네비게이션</h2>
<ul>
<li><a href="/home" aria-current="page">홈</a></li>
<li><a href="/">연재</a></li>
<li><a href="/">랭킹</a></li>
</ul>
</nav>
<!-- O: 현재 단계 링크 -->
<nav>
<h2>회원 가입</h2>
<ol>
<li><a href="/accept-terms" aria-current="step">약관 동의</a></li>
<li><a href="/id-password">아이디/비밀번호 생성</a></li>
<li><a href="/email-authentication">이메일 인증</a></li>
</ol>
</nav>
aria-selected
value
true | false | undefined(default)
단일 또는 다중 선택이 가능한 요소(role="gridcell|option|row|tab")에 선택 상태를 명시하는 의미
role="tab" 요소에 가장 흔히 사용
키보드 포커스를 받을 수 있는 요소(a, button)에 적용
- undefined(default): 속성 또는 값을 선언하지 않은 경우 초기값. 선택할 수 없음
- true: 선택 가능한 요소를 선택했음
- false: 선택 가능한 요소를 선택하지 않았음
예시
<!-- O: role="tab" 요소에 선택 상태를 명시 -->
<div role="tablist">
<a id="mon-anchor" href="#mon" role="tab" aria-selected="true">월</a>
<a id="tue-anchor" href="#tue" role="tab" aria-selected="false">화</a>
</div>
aria-haspopup
value
true | menu | dialog | listbox | tree | grid | false(default)
보통 a, button 요소에 사용
요소에 연결되어 있는 팝업(메뉴, 대화상자 등) 정보를 제공
팝업 유형은 menu, list, tree, grid, dialog 으로 제한
일반적으로 menu와 dialog 유형이 빈번
- true: menu와 동일한 의미
- menu: menu(role) 팝업이 연결됨, menu(role)는 링크 목록
- dialog: dialog(role) 팝업이 연결됨. dialog(role)는 상호작용 요소(버튼 또는 컨트롤)가 포함된 현재 문서의 하위창
- listbox: listbox(role) 팝업이 연결됨. listbox(role)는 선택 가능한 option(role)을 포함한 콤보박스
- tree: tree(role) 팝업이 연결됨. tree(role)는 하위 list(role)를 포함하며 접고 펼칠 수 있음
- grid: grid(role) 팝업이 연결됨. grid(role)는 행과 열로 구성된 선택 가능한 위젯. 상호작용 가능한 셀이기 때문에 table(role)과는 역할이 다름에 유의
- false(default): 연결된 팝업이 없음을 의미
예시
<!-- O: aria-haspopup="menu|true" -->
<button type="button" id="menu-button" aria-haspopup="menu" aria-control="menu-list" aria-expanded="false">메뉴</button>
<ul id="menu-list" role="menu" aria-labelledby="menu-button" hidden>
<li><a href="/compoleted">완결</a></li>
</ul>
<!-- O: aria-haspopup="dialog" -->
<a href="#login-dialog" aria-haspopup="dialog">로그인</a>
<section id="login-dialog" role="dialog" aria-labelledby="login-heading" aria-modal="true" hidden>
<h2 id="login-heading">로그인</h2>
...
</section>
ria-expanded
value
true | false | undefined(default)
제어 대상의 확장 또는 축소 상태
보통 a, button 요소에 사용
aria-controls 속성을 이용하여 제어 대상을 명시
툴팁(role="tooltip"), 알럿(role="alert"), 알럿 대화상자(role="alertdialog"), 대화상자(role="dialog")와 같이 동적으로 표시 상태를 결정(토글)하는 요소에 사용
- true: 요소 또는 제어 대상 확장 상태
- false: 요소 또는 제어 대상 축소 상태
- undefined(default): 속성 또는 값을 선언하지 않은 경우 초기값. 제어 대상이 없거나 모두 확장 상태. 확장/축소 제어 불가능
예시
<!-- O: 아코디언 -->
<dt>
<button type="button" aria-controls="anwer-99" aria-expanded="false">보너스 코인은 언제 소진되나</button>
</dt>
<dd id="answer-99" hidden>
<p>만료기한이 짧은 보너스 코인이 일반 코인보다 먼저 소진된다.</p>
</dd>
<!-- O: 팝업 -->
<a id="popular-btn" href="#popular" aria-haspopup="menu" aria-expanded="false">인기</a>
<ul id="popular" role="menu" aria-labelledby="popular-btn" hidden>
<li><a href="#romance">로맨스</a></li>
</ul>
aria-hidden
value
true | false | undefined(default)
접근성 API(보조기기 접근 가능성) 차단 상태를 결정
예를 들면 모달 대화상자를 화면에 표시할 때 모달 대화상자 뒤 본문 콘텐츠에 aria-hidden="true" 속성을 선언하면 보조기기가 무시.
- true: 접근성 API 차단.(화면 표시 여부와 무관한 API 차단)
- false: 접근성 API 사용.(화면에 표시한 경우 API 사용)
- undefined(default): 속성 또는 값을 선언하지 않을 경우 초기값. 화면 표시여부에 따라 접근성 API 차단 상태를 결정. 화면에 표시하면 false, 화면에 숨기면 true
예시
<!-- O: 모달 윈도우를 표시할 때 다른 요소를 차단 -->
<body>
<div class="container" aria-hidden="true">
// 보조기기가 무시하는 영역
</div>
<div role="alertdialog" aria-modal="true" aria-labelledby="TITLE" aria-describedby="DESCRIPTION">
<h2 id="TITLE">레진패스 안내</h2>
<p id="DESCRIPTION">이 작품의 유료 에피소드 열람 시 자동으로 구매합니다. 레진패스를 적용하시겠습니까?</p>
<button type="button">레진패스 적용</button>
<button type="button">취소</button>
</div>
</body>
aria-invalid
value
true | false(default) | grammar | spelling
주로 <input> 요소에 선언
사용자가 입력한 값이 요구하는 형식과 일치하는지 여부를 나타낸다.
aria-errormessage 속성과 함꼐 사용하여 오류 설명을 제공
- false(default): 오류 없음. aria-invalid 속성을 선언하지 않거나 갑싱 없으면 false로 간주
- true: 오류 있음
- grammar: 문법 오류
- spelling: 철자 오류
예시
<!-- O: 입력 값이 유효하면 aria-invalid 속성을 생략 -->
<!-- O: 입력 값이 오류이면 aria-invalid="true" 속성을 선언 -->
<label for="email">이메일</label>
<input id="email" type="email" required value="..." aria-invalid="true" aria-errormessage="email-error-msg">
<p id="email-error-msg" aria-role="alert">이메일 형식이 유효하지 않음. 앳(@)과 마침표(.)를 포함해 주세요
속성(properties)
<element aria-controls="ID refernce list"> ✨
<element aria-live="polite|assertive|off(default)"> ✨
<element aria-labelledby="ID refernce list"> ✨
<element aria-label="string">
<element aria-describedby="ID refernce list">
<element aria-errormessage="ID refernce list"> ✨
<element aria-modal="true|false(default)"> ✨
aria-controls
- 현재 요소가 제어하는 대상을 명시
- 주로 role="tab", aria-haspopup, aria-expanded 속성과 함꼐 a, button 요소가 무엇을 제어하는지 명시
- 값은 하나 이상의 ID 참조. 값이 여럿인 경우 공백으로 분리
예시
<!-- O: role="tab" 요소에 aria-controls 속성 사용 -->
<button type="button" id="mon-anchor" aria-controls="mon" role="tab" aria-selected="true">월</button>
<!-- O: aria-haspopup 속성과 함께 aria-controls 속성 사용 -->
<button type="button" aria-haspopup="dailog" aria-controls="login-dialog">로그인</button>
<!-- O: aria-expanded 속성과 함께 aria-controls 속성 사용 -->
<button type="button" aria-controls="answer-99" aria-expanded="false">보너스 코인은 언제 소진되나요?</butto
aria-live
value
polite | assertive | off(default)
실시간으로 내용을 갱신하는 영역
갱신 영역에 polite, assertive값을 사용하면 갱신 순간 보조 기기가 사용자에게 내용을 전달
- polite: 중요도가 낮은 내용에 사용, 현재 진행중인 음성 또는 타이핑을 방해하지 않고 뒤늦게 전달
- assertive: 중요도가 높은 내용에 사용하여 현재 진행중인 보조기기 작업을 중단하고 갱신 내용을 즉시 사용자에게 전달
aria-labelledby
- 간결한 설명 참조
- ID(s) 값을 이용하여 내용을 참조(연결)
- 보통 hx, a, button 요소를 참조하면 적절
- aria-label 속성과 함계 선언하는 경우, aria-labelledby 속성이 우선순위가 높음
- W3C는 aria-label 보다는 aria-labelledby 쓰는것을 권장
- aria-label은 화면에 설명 텍스트를 노출하는 방식이 아니기 때문
- <label> 를 이용해 <input>에 설명을 제공하는것과 같은 역할이라 보면됨
예시
- 이 내용에 포커스를 받았을 때 텍스트가 전달됨
<!-- O: 헤딩 설명 참조 모범사례 👏 -->
<section aria-labelledby="LZ-PATH" hidden>
<h2 id="LZ-PATH">레진패스란?</h2>
<p>이 작품의 유료 에피소드 열람시 자동으로 구매함</p>
</section>
<!-- O: 링크 설명 참조 -->
<a id="LZ-PATH" href="#LZ-PATH-TEXT">레진패스란?</a>
<div id="LZ-PATH-TEXT" aria-labelledby="LZ-PATH" hidden>
<p>이 작품의 유료 에피소드 열람시 자동으로 구매함</p>
</div>
aria-describedby
- 자세한 설명 참조
- ID(s)값을 이용하여 '상세한' 내용을 참조(연결)
- 주로 툴팁, 링크(a), 폼 컨트롤, 알럿, 알럿 대화상자 요소에 사용하면 적절
예시
<!-- O: 버튼 요소에 상세한 설명 제공 -->
<button aria-describedby="TIP-DEL">게시물 삭제</button>
<p id="TIP-DEL" role="tooltip" hidden>게시물 삭제 후 복원할 수 없음</p>
<!-- O: 알럿 대화상자 요소에 상세한 설명 제공 -->
<div role="alertdialog" aria-modal="true" aria-labelledby="TITLE" aria-describedby="DESCRIPTION">
<h2 id="TITLE">레진패스 안내</h2>
<p id="DESCRIPTION">이 작품의 유료 에피소드 열람시 자동으로 구매합니다 레진패스를 적용하시겠습니까?</p>
<button type="button">레진패스 적용</button>
<button type="button">취소</button>
</div>
aria-errormessage
- 오류 메시지 참조, 주로 <input> 요소에 선언
- aria-invalid="true" 속성을 활성하면 보조기기는 aria-errormessage 속성이 참조하는 내용을 오류 메시지로 전달
- 오류 메시지는 '오류 원인과 해결 방법'을 포함
aria-modal
value
true | false(default) |
요소가 모달인지 여부를 보조기기에 전달
보통 role="alertdialog" 또는 role="dialog"와 함께 사용
- false(default): 속성 또는 값을 선언하지 않은 경우 초기값. 모달 콘텐츠 아님
- true: 모달 콘텐츠
사용할 수 없는 요소에 aria-hidden="true" 속성을 선언해서 보조기기가 무시하도록 설정. 포인팅 기능도 차단하도록 처리
'HTML' 카테고리의 다른 글
HTML - 변경문자 (0) | 2022.12.16 |
---|---|
HTML - 주석 종류 (0) | 2022.09.29 |
HTML - <a> (0) | 2022.09.08 |
HTML - label for (0) | 2022.09.05 |
HTML - 목록태그 리스트 <ul>, <ol>, <li>, <dl>, <dt>, <dd> (0) | 2022.09.02 |