WCAG
Web Content Accessibility Guidelines => 접근성을 갖춘 웹사이트를 만드는데 필요한 지침
https://www.wuhcag.com/wcag-checklist/
https://webaim.org/standards/wcag/checklist
https://www.a11yproject.com/checklist/
WAI-ARIA
Web Accessibility Initiative - Accessible Rich Internet Applications의 약자
접근성을 갖춘 JavaScript 위젯을 만드는 데 필요한 기술들이 담겨있습니다
aria-*와 같은 어트리뷰트는 일반적인 HTML과 마찬가지로 hypen-case(혹은 kebab-case, lisp-case 등)로 작성해야 합니다.
<input
type="text"
aria-label={labelText}
aria-required="true"
name="name"
/>
시맨틱 HTML
React Fragment
로 감싸자import React, { Fragment } from 'react';
function ListItem({ item }) {
return (
<>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</>
);
}
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
<ListItem item={item} key={item.id} />
))}
</dl>
);
}
항목을 매핑할 때 Fragment는 반드시 key
프로퍼티가 있어야 합니다.
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
//
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
);
}
접근성 있는 폼
https://www.tpgi.com/what-is-an-accessible-name/
https://webaim.org/techniques/forms/controls
https://www.w3.org/WAI/tutorials/forms/labels/
jsx에서는 for
는 htmlFor
로 대체 되었다.
<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>
오류 상황은 모든 사용자가 알 수 있어야 합니다. 아래 링크는 스크린 리더에 오류 문구를 노출하는 방법을 설명합니다.
https://www.w3.org/WAI/tutorials/forms/notifications/
https://webaim.org/techniques/formvalidation/
포커스 컨트롤
모든 웹 애플리케이션은 키보드만 사용하여 모든 동작을 할 수 있어야 합니다.
키보드의 탐색 속도를 높이기 위해, 이전에 탐색한 영역을 건너뛸 수 있어야 한다. 그 방법이 바로 Skiplinks이다
Skiplinks 또는 Skip Navigation Link들은 키보드 사용자가 페이지와 상호작용할 때만 표시되는 숨겨진 탐색 링크입니다
https://webaim.org/techniques/skipnav/
사용자들이 이러한 섹션으로 빠르게 이동할 수 있도록, <main>
과 <aside>
같이 대표성을 띠는 랜드마크 엘리먼트와 역할들을 사용해 페이지 영역을 나누어야 합니다
https://www.scottohara.me/blog/2018/03/03/landmarks.html
리액트에선 런타임 동안 DOM을 변경하는데, 이때 키보드 포커스를 잃거나 포커스가 변경됩니다.
이때, 코드상에서 키보드 포커스를 올바르게 변경해 주어야 합니다.
리액트에서는 ref를 사용하여 DOM 요소에 접근해줍니다.
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<CustomTextInput inputRef={this.inputElement} />
);
}
}
// 이제 필요할 때마다 포커스를 잡을 수 있습니다.
this.inputElement.current.focus();
첫 포커스를 취소 버튼에 맞출 뿐 만 아니라(키보드 사용자가 실수로 확인 동작을 일으키지 않도록 막아줌), 키보드 포커스를 모달 안으로 한정해주며, 모달이 닫힐 때 모달을 열게 했던 엘리먼트에 포커스를 잡아줍니다.
마우스와 포인터 이벤트
마우스 혹은 포인터 이벤트로 노출된 모든 기능을 키보드만으로 사용할 수 있도록 보장해야 합니다.
포인터와 마우스 이벤트에 의존하면 키보드 사용성을 해치므로 주의해야 합니다
예를 들어, 모달창을 닫을 때 모달창을 제외한 부분을 Click해서 닫는데 click 이벤트를 받는것은 키보드 사용자들에게 문제가 생깁니다.
다음 엘리먼트로 탭을 이동할 때 window 객체가 click 이벤트를 받을 수 없기 때문입니다. 이로 인해, 기능이 가려져 사용자들이 애플리케이션을 제대로 사용할 수 없게 됩니다.
onBlur
와 onFocus
같은 적절한 이벤트 핸들러를 사용하여 같은 기능을 제공할 수 있습니다.
class BlurExample extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
this.timeOutId = null;
this.onClickHandler = this.onClickHandler.bind(this);
this.onBlurHandler = this.onBlurHandler.bind(this);
this.onFocusHandler = this.onFocusHandler.bind(this);
}
onClickHandler() {
this.setState(currentState => ({
isOpen: !currentState.isOpen
}));
}
// setTimeout을 사용해 다음 순간에 팝오버를 닫습니다.
// 엘리먼트의 다른 자식에 포커스가 맞춰져있는지 확인하기 위해 필요합니다.
// 새로운 포커스 이벤트가 발생하기 전에
// 블러(blur) 이벤트가 발생해야 하기 때문입니다.
onBlurHandler() {
this.timeOutId = setTimeout(() => {
this.setState({
isOpen: false
});
});
}
// 자식이 포커스를 받으면, 팝오버를 닫지 않습니다.
onFocusHandler() {
clearTimeout(this.timeOutId);
}
render() {
// React는 블러와 포커스 이벤트를 부모에 버블링해줍니다.
return (
<div onBlur={this.onBlurHandler}
onFocus={this.onFocusHandler}>
<button onClick={this.onClickHandler} aria-haspopup="true"
aria-expanded={this.state.isOpen}>
Select an option
</button>
{this.state.isOpen && (
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
)}
</div>
);
}
}
이 코드는 포인터 장치 사용자와 키보드 사용자 모두에게 기능을 제공합니다.
동시에 스크린 리더 사용자들을 지원하기 위해 aria-* props를 추가했습니다.