접근성

js·2022년 6월 19일
0

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

  • 접근성을 깨지 않기 위해서 div 태그로 감싸지 말고
    아래와 같이 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에서는 forhtmlFor로 대체 되었다.

<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();

BEST-PRACTICE : react-aria-model

첫 포커스를 취소 버튼에 맞출 뿐 만 아니라(키보드 사용자가 실수로 확인 동작을 일으키지 않도록 막아줌), 키보드 포커스를 모달 안으로 한정해주며, 모달이 닫힐 때 모달을 열게 했던 엘리먼트에 포커스를 잡아줍니다.

마우스와 포인터 이벤트

마우스 혹은 포인터 이벤트로 노출된 모든 기능을 키보드만으로 사용할 수 있도록 보장해야 합니다.
포인터와 마우스 이벤트에 의존하면 키보드 사용성을 해치므로 주의해야 합니다

예를 들어, 모달창을 닫을 때 모달창을 제외한 부분을 Click해서 닫는데 click 이벤트를 받는것은 키보드 사용자들에게 문제가 생깁니다.

다음 엘리먼트로 탭을 이동할 때 window 객체가 click 이벤트를 받을 수 없기 때문입니다. 이로 인해, 기능이 가려져 사용자들이 애플리케이션을 제대로 사용할 수 없게 됩니다.

onBluronFocus 같은 적절한 이벤트 핸들러를 사용하여 같은 기능을 제공할 수 있습니다.

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를 추가했습니다.

0개의 댓글