프로젝트 리팩토링 첫 번째로 웹 접근성을 개선하기로 했다.
접근성 트리와 명암, 색상은 개발하면서 고려했기에 수정할 부분이 별로 없지만, 정작 스크린 리더로 결과를 확인하지 않았다.
그래서 이번에 스크린 리더와 키보드만으로 서비스를 사용할 수 있을 정도로 접근성을 개선하고자 한다.
웹 접근성
이란 누구나 쉽게 사이트에 접근하여 사용할 수 있는 정도를 의미한다.
웹 접근성은 장애인과 비장애인 구분 없이, 모든 사용자가 동등하게 정보를 제공받을 수 있는 권리를 보장하기 위해 만들어진 개념이다.
웹 접근성을 개선하면 특히나 장애인과 고령자 등 온라인 이용에 겪는 사람들에게 큰 도움이 된다.
사용자가 콘텐츠에 접근할 때 문제가 될 수 있는 모든 장애 요인을 제거하는 것이다.
마우스, 키보드나 터치스크린을 사용할 수 없는 사람들도 사이트를 탐색할 수 있는지에 대한 것이다.
사용자가 웹 사이트 내에서 원하는 작업을 완료할 수 있도록 충분한 시간을 주는 것 역시 운용 가능성에 해당한다.
사용자가 인터페이스를 이해하고 활용할 수 있도록 일관성 있게 웹 사이트가 만들어져 있는지에 대한 것이다.
이를 위해 이해하기 쉬운 언어와 기능을 사용하는 것이 좋다.
웹사이트가 얼마나 다양한 플랫폼 및 장치에서 작동하는지, 환경적 제약에 영향을 받지 않는지에 대한 것이다.
구글은 검색 페이지에서 웹 페이지의 순위를 결정할 때 사용자 경험을 고려한다.
웹 접근성은 사용자 경험과 밀접하게 연관되어 있기 때문에, 웹 접근성을 향상시키면 더 높은 검색 순위에 등록될 수 있다.
높은 검색 순위는 서비스 접근성에 영향이 가기 때문에, 높은 순위를 점하는 것이 좋다.
웹 접근성이 요구하는 콘텐츠 가독성 충족 방법과 SEO 콘텐츠 구조 최적화 방법이 동일하다.
시맨틱 태그를 사용하여 페이지를 구조화하는 것이다.
이를 통해 크롤러가 사이트를 더욱 쉽게 크롤링할 수 있게 되어 SEO 개선에 도움이 된다.
또한 사람들도 더 쉽게 콘텐츠를 이해하고 사이트를 탐색할 수 있다.
대체 속성을 사용해 이미지를 설명하면, 장애가 있는 사용자가 사이트를 더 쉽게 사용할 수 있다.
뿐만 아니라, 검색 엔진이 멀티미디어 콘텐츠를 더 잘 이해하고 인덱스할 수 있다.
시맨틱 태그를 사용하여 콘텐츠를 구조화해야 한다.
사이트의 구조를 설계하는 데 도움이 되는 태그로, <header>
, <nav>
, <article>
, <section>
, <footer>
, <main>
등의 태그가 있다.
시맨틱 태그는 보조 기술에 대한 유용한 정보를 제공한다.
강조하는 내용을 작성할 때 <strong>
태그를 사용하면, 스크린 리더가 더 강하게 말하여 사용자에게 해당 부분이 중요한 부분임을 알려준다.
토스트 메시지는 aria 옵션을 사용하여 토스트 메시지가 표시되자마자 스크린리더기에서 확인할 수 있도록 해야 한다.
이때 role="alert"
나 aria-live
와 같은 옵션을 사용한다.
aria-live
는 개발자가 페이지 일부를 실시간으로 표시할 수 있도록 돕는다.
페이지의 해당 부분을 탐색하는 것이 아니라, 페이지 위치와 상관없이 즉시 사용자에게 전달되어야 한다는 의미이다.
aria-live=”polite”
현재 진행 중인 작업을 완료했을 때 사용자에게 변경 사항을 알리기 위한 기능
aria-live=”assertive”
스크린 리더기에서 수행 중인 작업을 중단하고 사용자에게 이 변경 사항을 알릴 때 사용한다. 서버 오류와 같은 상태 메시지 또는 입력 필드에 대한 업데이트와 같이 중요하고 긴급한 변경사항에만 사용한다.
aria-live=”off”
aria-live
의 동작을 일시적으로 중단하는 옵션이다.
aria-live
와 함께 동작하는 ARIA 옵션인 aria-hidden
, aria-atomic
, aria-busy
등을 사용하여 라이브 영역이 변경될 때 사용자에게 전달해야 하는 내용을 디테일하게 가져갈 수 있다.
ALL UPPER CASE의 경우 난독증이 있는 사람뿐만 아니라 장애가 없는 사람도 읽기 어려울 수 있다.
또한 스크린 리더기에서는 대문자를 단어가 아닌 약어라고 생각하여 오동작을 일으킬 수 있고, 단어를 읽는 것이 아닌 각 알파벳을 읽기 때문에 스크린 리더기의 읽는 속도가 현저히 느려진다.
약어를 사용해야 하는 경우엔 <abbr />
을 사용하여 단어를 설명하도록 한다.
<abbr />
의 경우 포커스를 줄 수 없고, 사용을 권장하지 않는다.
aria-label
사용을 권장하는 입장도 존재한다고 한다.
픽셀(px)을 사용하는 것은 시각 장애가 있는 사용자에게 접근성 문제를 일으킬 수 있기 때문에 폰트 크기를 조정해야 하는 경우 특히 좋지 않다.
페이지 폰트를 설정하지 않으면 기본적으로 CSS 16px로 렌더링된다.
이는 개발자와 사용자 모두 변경할 수 있다.
개발자
는 루트 태그(html)의 폰트 크기를 CSS로 변경하여 모든 태그의 기본 폰트 크기를 변경할 수 있다.사용자
는 브라우저 설정으로 원하는 폰트 크기를 지정할 수 있다.폰트 크기에 대한 사용자 기본 설정은 항상 CSS보다 우선순위가 높아야 한다.
사용자가 페이지를 보는 방식에 개발자가 가정해서는 안된다.
픽셀(px)을 사용하게 되면 기본 폰트 크기에 관계 없이 항상 해당 크기로 렌더링 된다.
WCAG 2.0의 1.4.4 Resize Text 가이드라인을 준수하기 위해 사용자가 페이지의 폰트 크기를 확대할 수 있도록 해야 한다.
사용자의 폰트 크기 기본 설정의 우선순위를 높이려면 픽셀과 같은 절대 단위는 이상적이지 않다.
상대 단위인 em
, rem
, %
를 사용하면 상대적 폰트 크기를 제공할 수 있다.
em
은 부모를 기준으로 상대 폰트 크기를 제공하기 때문에 페이지의 상대 폰트 크기를 제공하고자 한다면 rem
을 사용하는 것이 좋다.
부모를 기준으로 em
을 사용하게 되면 엘리먼트가 중첩될 수록 예측하기 어려워진다. 또한, 독립적이고 재사용 가능한 엘리먼트를 만드는 것이 어려워진다.
rem
은 다큐먼트의 루트 폰트 사이즈를 참조하여 상대적인 크기가 결정된다.
사용자의 브라우저 설정에서 기본 폰트 크기를 변경하면 모든 rem
기반 엘리먼트의 폰트 크기가 설정에 따라 조정되고 새 값으로 렌더링 된다.
즉, 사용자의 기본 설정을 존중하도록 하는 글꼴 크기인 rem
으로 설정하는 것이 좋다.
tabindex가 있는 요소는 포커스 될 수 있고, Tab 키를 눌렀을 때 포커스의 이동 순서를 지정할 수 있다.
하지만 tabindex를 사용할 때는 주의해야 한다.
남용할 시 접근성 보조기기를 사용하는 사용자의 페이지 탐색과 조작에 방해가 된다.
0보다 큰 tabindex 값을 사용하면 접근성 보조기기의 페이지 탐색과 조작에 방해가 된다.
0보다 큰 값을 쓰지 말고, 마크업 순서가 논리적으로 잘 구성되어 자연스럽게 tab 이동이 되도록 HTML을 작성해야 한다.
기본적으로 tabindex는 0의 값을 가지며, 요소에 값을 -1로 변경한다면 tab 키를 눌렀을 때, 해당 요소로 이동하지 않는다.
반대로, tabindex를 가지지 않는 요소에 tabindex 값을 추가하면 해당 요소는 tab 키로 접근이 가능해진다.
tab 키가 사용가능해지면 keyboard 이벤트를 사용할 수 있게 된다.
비 대화형 콘텐츠에 tabindex를 작성하는 것을 피하라고 권장한다.
예를 들어, button을 나타내기 위해 div
태그를 사용하고 tabindex를 사용하는 행위가 있다.
비 대화(div, span 등)형 콘텐츠를 사용해 만든 대화형 콘텐츠(button, input 등)들은 접근성 트리에서 나타나지 않기 때문에 보조기기가 제대로 작동하지 않을 수 있다.
대화형 콘텐츠는 대부분 자신의 역할과 상태를 내장하고 있기 때문에 최대한 대화형 콘텐츠를 사용하는 것이 좋다.
마크업이 논리적으로 되어있다면 굳이 사용할 필요가 없다.
시각적인 디자인이나 폼, 링크 요소가 논리적이기 않게 마크업된 경우엔 tabindex를 사용해 순서를 부여하면 자연스럽게 페이지를 탐색할 수 있게 된다.
혹은 keyboard 이벤트가 존재하지 않는 요소에 keyboard 이벤트를 사용하게 하기 위한 경우 적용하면 된다.
시맨틱 태그는 암묵적으로 갖고 있는 role
이 있기 때문에, 굳이 role
속성을 중복해서 사용하지 않는다.
// good
<input type="checkbox">
<button>버튼</button>
// bad
<input type="checkbox" role="checkbox">
<button role="button">버튼</button>
말 그대로, aria로 사용된 모든 요소는 키보드로 제어할 수 있어야 한다.
tab
으로 해당 요소에 focus한 뒤, enter
나 space
키를 눌렀을 때 active 상태가 되어야 한다.
즉, 클릭한 것과 같은 효과가 있어야 한다.
자주 사용되는 것들에 대해 알아보자.
성공(또는 결과) 상태 메시지를 사용자에게 전달하는 콘텐츠이다.
사용자의 현재 작업을 방해하지 않고(초점을 옮기지 않고) 보조기기 사용자에게 조언할만한 메시지를 전달하는 것을 의도하고 있다.
role="alert"
만큼 중요하지 않다.
role="status"
요소는 aria-live="polite"
속성과 aria-atomic="true"
속성을 암시적으로 할당하기 때문에 추가로 선언할 필요가 없다.
성공 메시지 영역을 변경하면 스크린 리더는 현재 진행 중인 음성 안내를 마친 후 성공 메시지 전체 내용을 사용자에게 전달한다.
<!-- 성공 메시지 -->
<p role="status">회원가입 양식 전송완료.</p>
<!-- 결과 메시지 -->
<p role="status">10개의 검색 결과.</p>
<p role="status">장바구니에 5개의 항목.</p>
시간에 민감하고 중요한(오류, 제안) 메시지를 사용자에게 전달하는 콘텐츠이다.
사용자의 현재 작업을 방해하지 않고(초점을 옮기지 않고) 보조기기 사용자에게 즉각 메시지를 전달하는 것을 의도하고 있다.
role="alert"
요소는 aria-live="assertive"
속성과 aria-atomic="true"
속성을 암시적으로 할당하기 때문에 추가로 선언할 필요가 없다.
오류 메시지 영역을 변경하면 스크린 리더는 현재 진행 중인 음성 안내를 즉시 멈추고 오류 메시지 전체 내용을 사용자에게 전달한다.
<!-- 오류 메시지 -->
<p role="alert">우편번호 입력 오류.</p>
<!-- 제안 메시지 -->
<p role="alert">로그인 후 이용 가능.</p>
만약, 사용자 입력폼(input
, textaria
)의 실시간 오류를 표시하는 경우라면 오류 메시지 대신 aria-invalid="true|false"
속성과 aria-errormessage="ID reference"
속성을 사용한다.
사용자 동의 또는 확인이 필요한 인터렉션 요소(button
)을 포함한 상태로 다른 작업을 차단하는 경우 사용한다.
사용자 입력 없이 ‘확인’, ‘취소’ 버튼을 제공하는 경우 적절하다.
aria-labelledby="ID reference list"
그리고 aria-describedby="ID reference list"
속성으로 alert dialog의 제목과 설명을 연결한다.
다른 작업을 차단해야 하기 때문에, 모달 윈도우 스타일로 처리한 다음 aria-modal="true"
속성을 추가한다.
또한 키보드 초점을 대화상자 내부 첫 번째 컨트롤 요소(’확인’ 버튼 등)로 옮겨야 하며, 초점이 dialog 안에서 벗어나지 않아야 한다.
<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>
사용자 응답이 필요 없는 내용이라면 role="alert"
속성이 적절하다.
그 외 정보를 입력(input, textarea, select, button)하는 dialog라면 role="dialog"
가 적절하다.
위의 role="alertdialog"
와 사용법은 비슷하다.
사용자 인터렉션이 필요한 작업을 제공하는 경우 적절하다.
aria-labelledby="ID reference list"
또는 aria-label="string"
속성으로 설명을 제공한다.
또한, 키보드 초점을 dialog 내부 첫 번째 컨트롤 요소로 옮겨야 하며 초점이 dialog 안에서 벗어나지 않아야 한다.
모달 스타일로 처리하는 경우 aria-modal="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>
보충 설명에 대한 요소 속성과 태그이다.
주로 내용을 보완하는 블록이며, 제거해도 주요 내용에 변함이 없어야 한다.
적절한 HTML 요소는 <aside>
엘리먼트이며, role="complementary"
속성을 사용하기 전에 해당 엘리먼트를 먼저 고려하는 것이 좋다.
보충 역할을 하는 요소가 문서 안에서 유일한 경우 레이블(aria-labelledby
, aria-label
)은 선택이다.
하지만 둘 이상인 경우 교유한 레이블을 제공해야 한다.
<!-- 보충에 aside 요소를 권장 -->
<aside>
<h2>배너/광고</h2>
...
</aside>
<!-- 보충 역할을 하는 요소가 유일한 경우 레이블 생략 가능 -->
<div role="complementary">
<h2>배너/광고</h2>
...
</div>
<!-- 보충 역할이 둘 이상인 경우 레이블 제공(aside) -->
<aside aria-labelledby="event">
<h2 id="event">이벤트</h2>
...
</aside>
<aside aria-labelledby="advertisement">
<h2 id="advertisement">배너/광고</h2>
...
</aside>
<!-- 보충 역할이 둘 이상인 경우 레이블 제공(role="complementary") -->
<div role="complementary" aria-labelledby="event">
<h2 id="event">이벤트</h2>
...
</div>
<div role="complementary" aria-labelledby="advertisement">
<h2 id="advertisement">배너/광고</h2>
...
</div>
흔히 모달창, 체크박스, 탭 메뉴 등을 사용하는 콘텐츠에 대한 웹 접근성을 높이기 위해 사용될 수 있다.
aria-controls
를 지정하는 요소(태그)는 다른 요소를 제어(control) 한다는 것을 보조 기술에 알려주는 역할을 한다.
주로 버튼이 다른 DOM(메뉴, 탭 패널, 드롭다운 등)을 토글하거나 표시/숨김 등의 기능을 할 때 사용된다.
<button aria-controls="menu1" aria-expanded="false" id="toggleMenuBtn">
메뉴 열기
</button>
<ul id="menu1" role="menu" hidden>
<li role="menuitem">항목 1</li>
<li role="menuitem">항목 2</li>
</ul>
aria-controls="menu1"
: 이 버튼이 id="menu1"
인 요소를 제어함을 의미aria-expanded
: 요소의 확장/축소 여부 명시(토글 버튼 등)'use client';
import { useState } from 'react';
export default function AccessibleDropdown() {
const [open, setOpen] = useState(false);
return (
<div>
<button
aria-controls="dropdown-menu"
aria-expanded={open}
aria-haspopup="true"
onClick={() => setOpen(!open)}
>
메뉴 열기
</button>
<ul
id="dropdown-menu"
role="menu"
hidden={!open}
>
<li role="menuitem">항목 1</li>
<li role="menuitem">항목 2</li>
</ul>
</div>
);
}
display: none
, visibility: hodden
을 활용하여 콘텐츠를 숨김처리하면 웹 페이지에서 보이지 않고 스크린 리더기와 같은 보조 기술로도 탐색이 불가능하다.
이때 스크린리더에서는 탐색을 제한하고 화면에 보여져야 할 경우, 즉 의미가 전혀 없는 시각적 디자인 요소의 경우 aria-hidden="true"
를 설정하여 스크린리더가 읽지 않도록 할 수 있다.
보조 기술이 읽을 수 있도록 요소에 보이지 않는 이름(라벨)을 제공할 때 사용한다.
HTML 요소에 라벨 텍스트를 직접 문자열로 제공하는 속성이며, 요소에 시각적인 텍스트가 없거나 기존 텍스트가 충분하지 않을 때 사용한다.
스크린리더는 aria-label
의 값을 해당 요소의 이름으로 인식한다.
중복되면 안되는 의미 없는 라벨(버튼, 아이콘 등)은 피하고, 정확한 기능 설명을 사용해야 한다.
<button aria-label="검색">
<svg aria-hidden="true">🔍</svg>
</button>
다른 요소의 텍스트 콘텐츠를 라벨로 사용하고 싶을 때 사용한다.
aria-label
이 직접 문자열로 지정된 요소의 라벨을 붙여주는 것이라면, aria-labelledby
는 다른 요소의 id
를 참조하여 그 요소(묶음)의 텍스트를 라벨처럼 사용한다.
스크린 리더는 aria-labelledby
에 지정된 요소의 텍스트를 읽어준다.
<h2 id="login-title">로그인</h2>
<section aria-labelledby="login-title">
...
</section>
여러 요소를 참조할 수도 있다.
<h2 id="mainTitle">회원가입</h2>
<span id="subTitle">이메일로 가입하기</span>
<form aria-labelledby="mainTitle subTitle">
<!-- 폼 내용 -->
</form>
이때 스크린 리더는 ‘회원가입 이메일로 가입하기 폼’ 이라고 읽는다.
현재 맥락과 일치하는 항목을 의미한다.
값은 정해진 값 리스트 중 하나만 사용할 수 있다.
page
: 현재 페이지와 일치하는 시각적으로 강조한 링크step
: 현재 단계와 일치하는 시각적으로 강조한 링크location
: 플로우 차트에서 현재 위치와 일치하는 시각적으로 강조한 이미지date
: 달력에서 현재 날짜와 일치하는 날짜time
: 시간표에서 현재 시간과 일치하는 시간이 밖에 true
, false
값도 있다. 이는 구체적으로 어떤 맥락과 일치하는지 정보를 전달하지 않기에 위의 토큰 값들이 모두 적절하지 않은 맥락에 한하여 사용한다.
<!-- aria-current="page" 현재 페이지 강조 링크 -->
<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>
<!-- aria-current="step" 현재 단계 강조 링크 -->
<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-current="location" 현재 위치 강조 이미지 -->
<img src="is-payment-success.png" alt="결제 성공?" aria-current="location">
<img src="payment-info.png" alt="결제 내역 안내">
<img src="payment-fail.png" alt="결제 실패 안내">
<!-- aria-current="date" 현재 날짜 강조 -->
<td aria-curent="date">12/24(토)</td>
<td>
<button type="button" aria-curent="date">12/24(토)</button>
</td>
<!-- aria-current="time" 현재 시간 강조 -->
<th scope="row" aria-current="time">18:00 ~ 20:00</th>
<td>무한 도전</td>
토글 가능한 요소(드롭다운, 아코디언, 내비게이션 패널 등)에서 현재 열림/닫힘 상태를 보조 기술에 알리는 속성이다.
요소가 펼쳐져 있는지(true), 접혀 있는지(false)를 나타낸다.
스크린리더가 사용자에게 UI의 상태 변화를 명확하게 인식할 수 있게 한다.
일반적으로 버튼이나 링크처럼 클릭하여 토글하는 컨트롤 요소에 붙인다.
<button aria-expanded="false" aria-controls="dropdown-menu">
메뉴
</button>
<ul id="dropdown-menu" hidden>
<li>항목 1</li>
<li>항목 2</li>
</ul>
사용자가 버튼을 클릭해서 드롭다운이 열리면
button.setAttribute('aria-expanded', 'true');
menu.hidden = false;
위처럼 aria-expanded
속성의 값을 업데이트해주어야 한다.
드롭다운 메뉴, 아코디언 섹션, 햄버거 메뉴 버튼과 UI 컴포넌트에 사용하며, 탭(Tab)에선 보통 aria-selected
를 사용한다.
aria-controls
와 arai-label
, aria-hidden
과 함께 사용한다.
aria-hidden
은 토글 대상이 숨겨졌을 때의 접근성을 제어한다.
주로 input
요소에 선언하여 사용자가 입력한 값이 요구하는 형식과 일치하는지 여부를 나타낸다.
aria-errormessage
속성과 함께 사용하여 오류 설명을 제공할 수 있다.
false(default)
: 오류 없음. aria-invalid
속성을 선언하지 않거나 값이 없으면 false
로 간주true
: 오류 있음grammer
: 문법 오류spelling
: 철자 오류<!-- 입력 값이 유효하면 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" role="alert" aria-live="assertive" hidden>이메일 형식이 유효하지 않습니다. 앳(@)과 마침표(.)를 포함해 주세요.</p>
<!-- 입력 값이 오류이면 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" role="alert" aria-live="assertive">이메일 형식이 유효하지 않습니다. 앳(@)과 마침표(.)를 포함해 주세요.</p>
여기서 aria-errormessage
속성은 aria-invalid
속성이 없거나 값이 false
라면 동작하지 않는다.
입력 값이 비어 있거나 유효하지 않은 초기 값을 제공한 때에는 aria-invalid="true"
를 선언하지 않아야 한다.
실시간으로 내용을 갱신하는 영역을 의미한다.
값으로 polite
, assertive
, off(default)
를 설정할 수 있으며, 갱신하는 내용의 중요도에 따라 선택한다.
polite
, assertive
값을 사용하면, 보조기기는 사용자에게 내용을 전달한다.
polite
값은 중요도가 낮은 내용에 사용하여 현재 진행중인 음성 또는 타이핑을 방해하지 않고 뒤늦게 전달한다.
assertive
값은 중요도가 높은 내용(오류, 제안)에 사용하여 현재 진행중인 보조기기 작업을 중단하고 갱신 내용을 즉시 사용자에게 전달한다.
<!-- alert -->
<div role="alert" aria-live="assertive">
<p>로그인 후 이용할 수 있습니다.</p>
</div>
assertive
값은 사용자의 현재 작업을 방해할 수 있기 때문에 중요도가 높은 내용을 선별하여 신중하게 적용해야 한다.
aria-labelledby
, aria-label
, aria-describedby
속성은 모두 현재 요소에 설명을 제공하는 속성이다.
id
값을 이용하여 상세한 내용을 참조(연결)하는 방식으로 설명한다.
보조 기술이 해당 설명을 함께 읽도록 하며, 시각적으로 설명이 있어도 스크린 리더 사용자에게 연결이 명시적으로 필요할 때 사용한다.
<!-- 버튼 요소에 상세한 설명 제공 -->
<button aria-describedby="TIP-DEL">게시물 삭제</button>
<p id="TIP-DEL" role="tooltip" hidden>게시물 삭제 후 복원할 수 없음.</p>
<!-- 알럿 대화상자 요소에 상세한 설명 제공 -->
<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>
입력 필드에 설명 텍스트를 연결할 때 사용하면 포커스가 갔을 때 해당 내용을 읽어준다.
<input
type="text"
id="username"
aria-describedby="username-desc"
placeholder="아이디를 입력하세요"
/>
<p id="username-desc">영문 또는 숫자 6자 이상 입력해 주세요.</p>
속성 | 용도 | 화면 표시 여부 |
---|---|---|
aria-label | 이름 직접 제공(텍스트로) | 안 보임 |
aria-labelledby | 이름을 다른 요소로부터 참조 | 보일 수 있음 |
aria-describedby | ‘보조 설명’을 다른 요소로 참조 | 보일 수 있음 |
주로 input
요소에 선언하여 오류 메시지를 제공하는 요소를 값으로 참조한다.
aria-invalid="true"
속성을 활성화하면 보조기기는 aria-errormessage
속성을 참조하는 요소를 오류 메시지로 전달한다.
<!-- aria-invalid 값이 true이면 보조기기는 aria-errormessage 값을 참조 -->
<label for="email">이메일</label>
<input id="email" type="email" required value="..." aria-invalid="true" aria-errormessage="email-error-msg">
<p id="email-error-msg" role="alert" aria-live="assertive">이메일 형식이 유효하지 않습니다. 앳(@)과 마침표(.)를 포함해 주세요.</p>
aria-errormessage
가 참조하는 요소를 동적으로 화면에 표시한다면 aria-live
속성을 이용해서 실시간으로 보조기기에 오류 메시지를 전달할 수 있다.
오류 메시지는 '오류 원인과 해결 방법'을 포함해야 한다.
요소가 모달인지 여부를 전달한다.
모달은 본문 위에 대화상자를 띄워 본문을 차단한 상태로 상호작용하는 요소를 의미한다.
일반적으로 role="alertdialog"
또는 role="dialog"
요소를 모달 형태로 표시할 수 있는데, 이런 경우 aria-modal="true"
속성을 함께 선언한다.
<!-- 알럿 대화상자에 aria-modal="true" 선언 -->
<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-modal="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-hidden="true"
속성을 추가하여 접근성 API를 차단하지만, 포인팅 기능(마우스 커서, 키보드 초점)을 차단하는 것은 아니므로 개발자는 aria-hidden="true"
요소가 포인팅을 받지 않도록 처리해야 한다.
이 요소를 조작하면 팝업(또는 유사 컴포넌트)이 나타난다는 의미를 전달한다.
스크린 리더에게 사용자 인터렉션 결과로 메뉴, 리스트박스, 창 등이 열릴 수 있음을 알리며 버튼이나 토글 요소 등에 자주 사용된다.
false
: 팝업 없음 (기본값)true
: 어떤 종류의 팝업이 나타남 (종류 명시 안 함)menu
: 메뉴가 나타남listbox
: 리스트박스가 나타남tree
: 트리가 나타남grid
: 그리드 형태의 UI가 나타남dialog
: 대화상자(dialog, modal 등)가 나타남여기서 aria-haspopup="dialog"
는 모달 창이 열릴 수 있음을 의미한다.
aria-controls
, aria-expanded
와 함께 사용된다.
<button aria-haspopup="true" aria-expanded="false" aria-controls="menu-id">
옵션
</button>
<ul id="menu-id" hidden>
<li>항목 1</li>
<li>항목 2</li>
</ul>
이 외 드롭다운 메뉴 버튼, 모달을 여는 버튼, 팝업 트리거 버튼, 사용자 프로필 드롭 다운 등에 사용하면 좋다.
즉, 사용자가 클릭했을 때 뭔가 “떠오르는 UI”가 있다면 aria-haspopup
을 고려하면 된다.
그래서 공부도 할겸 개발하며 참고하려고 정리해보았다.
웹 접근성은 사용자 경험 뿐만 아니라 SEO 개선에도 도움이 된다.
그러니, 한 번 쯤은 제대로 적용해보는 경험을 해보는 것도 좋을 것이라 생각한다.
https://www.zigae.com/a11y/
https://seo.tbwakorea.com/blog/web-accessibility/
https://12ahn22.tistory.com/entry/web웹-접근성과-HTML의-tabindex-속성
https://github.com/lezhin/accessibility/blob/master/aria/README.md