웹 접근성 - WAI-ARIA

오민영·2023년 2월 10일
0

HTML

목록 보기
6/9

(Web Accessibility Initiative - Accessible Rich Internet Applications)

W3C의 WAI(Web Accessibility Initiative) 조직이 제정한 AIRA(Accessible Rich Internet Applications) 웹 접근성에 대한 표준 기술 규격으로, HTML의 접근성 문제를 보완하는 W3C 명세이다.

웹 페이지, 특히 동적 컨텐츠(Ajax, HTML, JS) 및 관련 기술로 개발된 사용자 인터페이스 구성 요소로 접근성을 증가시키는 방법에 대해 규정해서 W3C가 출판한 기술 사양이다.

ARIA

  • ARIA는 W3C의 WAI 조직이 제작하고, HTML을 보충해 일반적으로 보조기술(LIke 스크린리더)이 알 수 없는 상호작용 및 어플리케이션 위젯에 필요한 정보를 제공한다.

  • HTML 요소에 role | aria-* 속성을 추가하여 콘텐츠의 역할(Roles), 상태(States), 속성(Properties) 정보를 보조기기에 제공한다.

  • ARIA는 Firefox, Safari, Opera, Chrome 및 Internet Explorer가 포함 된 모든 주요 브라우저의 최신 버전에서 지원되고, 오픈 소스 NVDA (NonVisual Desktop Access) 및 Orca 스크린 리더와 같은 많은 보조 기술도 ARIA를 지원한다.

    • 즉, jQuery UI, YUI, Google Closure, Dojo Dijit과 같은 JavaScript 위젯 라이브러리에 ARIA 마크 업도 포함된다.
  • ARIA 적용은 협의 후에 지정된 담당자가 적용을 하면 된다.

<!-- Now *these* are Tabs! -->
<!-- We've added role attributes to describe the tab list and each tab. -->
<ol role="tablist">
  <li id="ch1Tab" role="tab">
    <a href="#ch1Panel">Chapter 1</a>
  </li>
  <li id="ch2Tab" role="tab">
    <a href="#ch2Panel">Chapter 2</a>
  </li>
  <li id="quizTab" role="tab">
    <a href="#quizPanel">Quiz</a>
  </li>
</ol>

<div>
  <!-- Notice the role and aria-labelledby attributes we've added to describe these panels. -->
  <div id="ch1Panel" role=”tabpanel” aria-labelledby="ch1Tab">Chapter 1 content goes here</div>
  <div id="ch2Panel" role=”tabpanel” aria-labelledby="ch2Tab">Chapter 2 content goes here</div>
  <div id="quizPanel" role=”tabpanel” aria-labelledby="quizTab">Quiz content goes here</div>
</div>

AIRA 기능

역할(Roles) | 상태(States) | 속성(Properties)
등 대표적으로 3가지 기능을 제공한다.

WAI-ARIA의 장점

  1. 요소(Element) 및 컴포넌트에 누락된 의미를 명확하게 제공할 수 있다.

  2. 스크린리더 등 보조기기 사용성을 향상 시킨다.

  3. 논리적인 구조 설계와 구조의 시각화가 가능하다.

  4. 구조에 의미를 부여함으로써 페이지 영역의 빠른 탐색이 가능하다.

  5. 동적 컨텐츠의 식별이 가능하다.

  6. 상태 변화 발생시 사용자 에이전트는 사용하는 API에 적절한 이벤트 알림을 제공한다.

Role(역할)

  • slider, menu bar, dialog와 같이 HTML4에서 사용하지 못하는 특정 컴포넌트(엘리먼트)에 역할을 정의하는 것으로, 어떤 요소가 메뉴 영역인지, 콘텐츠 영역인지, 푸터인지 명시적으로 안내하기 위한 목적으로 사용된다.

  • role 어트리뷰트를 사용해서 작성하고 약 80개의 역할을 사용할 수 있다.

  • 랜드마크 역할을 지원하는 스크린리더는 시각장애인이 랜드마크로 지정한 주요 영역을 단축키 혹은 제스터로 왔다갔다 할 수 있게 해준다.

  • 추가 역할(Abstract Roles), 위젯 역할(Widget Roles), 문서구조 (Document Structure), 랜드마크 역할 (Landmark Roles)의 네 가지 종류로 구분할 수 있다.

  • MDN Document

    <!-- 역할(roles) -->
    <element role="button">
    <element role="tab">
    <element role="tabpanel">
    <element role="tooltip">
    <element role="status">
    <element role="alert">
    <element role="alertdialog">
    <element role="dialog">
    <element role="navigation">
    <element role="checkbox">
    <element role="menu">
    <element role="complementary">
    <element role="none">
    ...
     // 스크린 리더는 링크로 읽는다 (Bad)
    <a href=""> 음악재생</a>
    
     // 스크린 리더는 button으로 읽는다. (Good)
    <a href="" role="button"> 음악재생</a>

Property(속성) + State(상태)

해당 컴포넌트의 특징이나 상황을 정의하며 속성명으로 접두사 aria-를 사용하고, 약 40개의 속성을 사용할 수 있다.

aria-hidden

  • 스크린 리더와 같은 보조 기술을 사용하는 사용자를 대상으로 콘텐츠의 탐색을 제한한다.

  • 개발자가 구현한 요소와 그 하위 요소들을 보조기술에서 인식할 수 없도록 한다.

  • 요소에 장식용으로 보여지는 콘텐츠가 포함되어 있거나, 아이콘을 포함하는 링크 등에 사용한다.

- false(Default): 화면상에 노출이 된다.
- true: 스크린 리더로 해당 콘텐츠를 가상 커서로 탐색할 수 없다. 또한 하위 자식 요소 모두 건너뛴다.
  • 아래 CSS를 적용한 경우 중복으로 사용하지 않는다.
display: none;
visibility: hidden;

aria-expanded

  • 버튼을 클릭하여 특정 영역을 확장하거나 축소하는 UI 에 aria-expanded 를 사용한다.

    • 예) 아코디언, 메뉴, 콤보박스, 트리와 같이 하위 그룹(또는 독립적인) 내용을 토글(열고 닫기)하는 경우
- undefined(기본): 속성 또는 값을 선언하지 않은 초깃값. 제어 대상이 없거나 모두 확장 상태. 확장/축소 불가능
- true: 요소 또는 제어 대상 확장 상태.
- false: 요소 또는 제어 대상 축소 상태.
  • 스크린 리더 사용자에게 버튼을 클릭하여 내용이 아래로 확장되거나 축소 될 수 있음을 알릴 수 있고, 현재 UI가 축소 되었는지 / 확장 되었는지에 대한 상태 정보까지 제공한다.

  • 제어 기능을 하는 요소에 aria-expanded 를 사용할 경우, aria-controls를 함께 사용해서, 해당 요소가 제어하는 요소가 어떤 요소인지 명시하는 역할을 추가해준다.

    • aria-controls 의 값에는 제어 당하는 요소의 ID값을 넣어준다.

// 아코디언 UI
<dt>
    <button type="button" aria-controls="answer-99" aria-expanded="false">보너스 코인은 언제 소진되나요?</button>
</dt>
<dd id="answer-99" hidden>
    <p>만료기한이 짧은 보너스 코인이 일반 코인보다 먼저 소진됩니다.</p>
</dd>

// popup UI
<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>
    <li>
        <a href="#drama">드라마</a>
    </li>
</ul>

area-haspopup

  • 다른 요소 위에 겹쳐 나오는 하위 메뉴를 제어하는 버튼에 aria-haspopup을 사용한다.

    • 예) position absolute가 적용된 메뉴 리스트
- false: 요소에 팝업이 없음을 나타낸다.
- true: 요소에 팝업이 있는 메뉴버튼 임을 나타낸다.
  • 스크린 리더 사용자에게 해당 요소가 하위 메뉴를 포함하고 있다(팝업될 수 있다)는 정보를 제공한다.

  • 해당 요소에 aria-controls를 함께 사용해서, 해당 요소가 제어하는 요소가 어떤 요소인지 명시하는 역할을 추가해준다.

    • aria-controls 의 값에는 하위 메뉴의 ID를 적어준다.
<button type="button" aria-haspopup="true" aria-controls="menu" aria-expanded="false"> 
	메뉴
</button>
<nav id="menu">
	<ul aria-label="gnb메뉴">
		<li></li>
	</ul>
</nav>

aria-modal

  • 요소가 모달인지 여부를 보조기기에 전달한다.

  • 모달은 본문 위에 대화 상자를 띄워 본문을 차단한 상태로, 상호작용을 하는 요소를 의미한다.

- false: 속성 또는 값을 선언하지 않은 경우 초깃값. 모달 컨텐츠가 아님
- true: 모달 콘텐츠라고 명시
  • role = "alertdialog" 또는 role="dialog" 요소를 모달 형태로 표시할 수 있는데, 이런 경우 aria-modal="true" 속성과 함께 선언한다.

  • 모달 콘텐츠를 화면에 표시할 때 사용할 수 없는 요소에 aria-hidden="true" 속성을 선언해서 보조기기가 무시하도록 설정해야 합니다.

    • aria-hidden="true" 속성이 접근성 API를 차단하지만 포인팅 기능(마우스 커서, 키보드 초점)을 차단하는 것은 아니므로 개발자는 aria-hidden="true" 요소가 포인팅을 받지 않도록 처리해야 합니다.
<section role="dialog" aria-modal="true" aria-labelledby="TITLE">
    <h2 id="TITLE">로그인</h2>
    <form>
        <label for="ID">아이디</label>
        <input id="ID">
        <label for="PW">비밀번호</label>
        <input id="PW" type="password">
        <button>로그인</button>
    </form>
</section>

aria-controls

  • 현재 요소가 제어하는 대상을 명시하는 속성이다.
- aria-controls의 값은 하나 또는 그 이상의 제어할 대상의 ID 값이다.
  • 주로 role="tab", aria-haspopup, aria-expanded 속성과 함께 button 요소가 무엇을 제어하는지 명시한다.
<!-- O: role="tab" 요소에 aria-controls 속성 사용 -->
<div role="tablist">
    <button type="button" id="mon-anchor" aria-controls="mon" role="tab" aria-selected="true"></button>
    <button type="button" id="tue-anchor" aria-controls="tue" role="tab" aria-selected="false"></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" hidden>
    화요일 콘텐츠...
</div>

<!-- O: aria-haspopup 속성과 함께 aria-controls 속성 사용 -->
<button type="button" aria-haspopup="dialog" aria-controls="login-dialog">로그인</button>
<section id="login-dialog" role="dialog" aria-labelledby="login-heading" aria-modal="true" hidden>
    <h2 id="login-heading">로그인</h2>
    ...
</section>

aria-selected

  • 단일 또는 다중 선택이 가능한 요소에 한하여 선택 상태를 명시하는 용도로 사용한다.
- undefined(기본): 속성 또는 값을 선언하지 않은 경우 초기값. 선택할 수 없음
- true: 선택 가능한 요소를 선택했음
- false: 선택 가능한 요소를 선택하지 않았음
  • 흔히 role="tab"요소에 사용되고, 키보드 초점을 받을 수 있는 요소에 적용해야 한다.

  • 선택 요소에만 aria-selected="true"를 적용하면 속성을 적용하지 않은 요소는 undefined 상태가 되므로, 명시적으로 aria-selected="false" 속성과 값을 적용해야 한다.

<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-live

  • 실시간으로 내용을 갱신하는 영역에 적용한다.
- off(기본) : 업데이트 된 내용을 사용자에게 알리지 않음.
- polite : 사용자의 입력이 끝났을 때 알림.
- assertive : 업데이트 된 내용이 있을 때 즉시 알림.
  • 일반적으로 role="alert" 인 경우 사용하면 적절하다. 그 밖에 Ajax 기법을 이용하여 실시간으로 내용을 갱신하는 모든 영역(채팅, 오류, 로그, 상태 표시)에 사용할 수 있다!

  • assertive 값은 사용자의 현재 작업을 방해할 수 있기 때문에, 중요도가 높은 내용을 선별하여 신중하게 적용한다.

<div role="alert" aria-live="assertive">
    <p>로그인 후 이용할 수 있습니다.</p>
</div>

aria-current

  • 현재 맥락과 일치하는 항목을 의미한다.

  • 아래 값으로 정해져 있고, 이 중 하나만 사용할 수 있다.

- page: 현재 '페이지'와 일치하는 시각적으로 강조한 링크.
- step: 현재 '단계'와 일치하는 시각적으로 강조한 링크.
- location: 플로우 차트에서 현재 '위치'와 일치하는 시각적으로 강조한 이미지.
- date: 달력에서 현재 '날짜'와 일치하는 날짜.
- time: 시간표에서 현재 '시간'과 일치하는 시간.
- true / false: 구체적으로 어떤 맥락과 일치하는지 정보를 전달하지 않기 대문에 토큰이 적절하지 않은 맥락에 한해서만 사용한다.
// 현재 페이지 강조 링크
<nav>
    <h2>글로벌 네비게이션</h2>
    <ul>
        <li><a href="/home" aria-current="page"></a></li>
        <li><a href="/ongoing">연재</a></li>
        <li><a href="/ranking">랭킹</a></li>
    </ul>
</nav>

// 현재 단계 강조 링크
<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>

// 현재 위치 강조 이미지
<img src="is-payment-success.png" alt="결제 성공?" aria-current="location">
<img src="payment-info.png" alt="결제 내역 안내">
<img src="payment-fail.png" alt="결제 실패 안내">

aria-invalid

  • 주로 input 요소에 선언하여 사용자가 입력한 값이 요구하는 형식과 일치하는지 여부를 나타낸다.

  • aria-errormessage 속성과 함께 사용하여 오류 설명을 제공할 수 있다.

  • aria-errormessage 속성은 aria-invalid 속성이 없거나, 값이 false 라면 동작하지 않는다.

  • 입력 값이 비어 있거나 유효하지 않은 초기 값을 제공한 때에는 aria-invalid="true" 를 선언하지 않아야 한다.

- false(default): 오류 없음. aria-invalid 속성을 선언하지 않거나, 값이 없으면 false로 간주한다.
- true: 오류 있음
- grammer: 문법 오류
- spelling: 철자 오류
// 입력 값(value)이 유효한 경우에는 aria-invalid 속성을 생략
<label for="email">이메일</label>
<input id="email" type="email" required value="abc@xyz.xxx" aria-errormessage="email-error-msg">
<p id="email-error-msg" aria-role="alert" aria-live="assertive" hidden>이메일 형식이 유효하지 않습니다. 앳(@)과 마침표(.)를 포함해 주세요.</p>

// 입력 값(value)이 오류이면 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" aria-live="assertive">이메일 형식이 유효하지 않습니다. 앳(@)과 마침표(.)를 포함해 주세요.</p>

aria-label, aria-labeledby, aria-describedby

  • aria-label, aria-labelledby, aria-describedby 속성은 모두 현재 요소에 설명을 제공하는 속성이다.

  • aria-labelledby

    • ID(s) 값을 이용하여 '간결한' 내용을 참조(연결)하는 방식으로 설명한다.
    • 보통 h1, h2, h3, ... button 요소에 참조하면 적절하다.
    • aria-label와 함께 선언하는 경우, aria-labelledby 속성이 우선순위가 높기 때문에 보조기기는 aria-labelledby 속성을 설명한다.
    • 때문에 aria-labelledby를 사용하는게 좋다!
  • aria-label

    • 값에 '간결한' 설명(string)을 직접 제공한다.
    • aria-label 속성은 현재 요소를 설명할 다른 참조(연결) 요소가 없는 경우에만 사용한다.
  • aria-describedby

    • ID(s) 값을 이용하여 '상세한' 내용을 참조(연결)하는 방식으로 설명한다.
    • 링크(a), 폼 컨트롤(input, textarea, select,button), 알럿(role="alert"), 알럿 대화상자(role="alertdialog") 요소에 사용하면 적절하다.
// aria-labelledby
<section aria-labelledby="LZ-PATH" hidden>
    <h2 id="LZ-PATH">레진패스란?</h2>
    <p>이 작품의 유료 에피소드 열람 시 자동으로 구매합니다.</p>
</section>

// aria-label
<form>
    <input type="search" aria-label="웹툰 검색">
    <button>검색</button>
</form>

// aria-describedby
<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>

Reference

profile
이것저것 정리하는 공간

0개의 댓글