'웹 접근성'이란?
저시력자, 청각이상자, 고령자 등을 가리지 않고 모든 사람이 웹 사이트에서 제공하는 정보에 동등하게 접근하고 이해할 수 있도록 보장하는 것을 말한다.
크롬 개발자 도구에는 현재 페이지에 대한 웹 접근성 점수를 측정할 수 있는 Lighthouse라는 도구가 내장되어 있다.
진행 중인 리액트 프로젝트를 마무리하기 전, Lighthouse를 통해 부족한 접근성 요소를 개선해보자.
측정 결과, 메인 페이지의 초기 점수는 76점이었으며, 다른 페이지도 이와 비슷한 점수가 나왔다.
Lighthouse에서는 단순 점수 산정을 넘어 정확히 어떤 부분이 웹 접근성을 충족하지 못했는지 알려주므로 빠르고 편하게 개선시킬 수 있다.
텍스트가 포함되지 않은 아이콘형 버튼은 스크린 리더가 해당 버튼이 무슨 용도인지 알려줄 수 없게 된다. 이때는 aria-label
속성을 지정하여 눈에 보이지 않는 정보를 브라우저에게 전달해주도록 할 수 있다.
img
태그의 alt
속성과 유사하다. 모든 버튼에 대해 각 용도 및 목적을 명시해주었다.
리액트 기본 옵션으로 뷰 포트 content
속성의 값은 "width=device-width, user-scalable=no, initial-scale=1"
으로 지정되어 있는데, 이때 마지막 두 가지가 접근성 측면에서 문제가 되는 모양이다.
user-scalable
: 사용자가 페이지를 확대 또는 축소하는 것의 허용 여부initial-scale
: 페이지의 초기 줌 레벨을 지정W3C github의 권장에 따라 위 두 가지 규칙을 빼주었다. 기능성 측면에서도 크게 지장이 없었다.
명암이 뚜렷하지 않은, 즉 대비가 적은 텍스트의 경우 사용자의 시력 또는 환경에 따라 읽기 힘든 경우가 있을 수 있다.
명암비는 작은 글씨의 경우 4.5, 큰 글씨의 경우 3 이상이어야 한다. (WCAG 통과 기준 1.4.3)
내 프로젝트의 경우 주로 '비활성화 상태'나 '보조 설명' 등의 의미를 나타낼 때 메인 색상보다 옅은 색을 썼었는데, 이것이 밝은 배경색과 합쳐지면서 문제가 되었다.
원래 의도를 벗어나지 않는 선에서 최대한 큰 대비를 갖도록 조정해주었다.
다만, 명암비를 4.5 이상으로 맞추게 되면 배경색이 글자색에 비해 극단적으로 밝아지거나 어두워지게 된다. 내 경우는 강조색(청록) 배경 위의 하얀색 글자가 문제가 되었다. 명암비를 맞추려고 색을 이리저리 바꿔봐도 다 보기 안 좋아서 고민이 많았다. 디자인 관점에서는 꽤나 큰 제약이 생긴다고 봐야겠다.
input
태그에 label
붙이기일부 input
태그의 이름을 나타낼 때 label
대신 다른 일반적인 태그(div
등)를 이용하여 설명하는 부분이 있어서 이를 고쳐주었다.
이때 리액트 컴포넌트에서 반환하는 것은 일반 html이 아닌 jsx 객체이므로, label
이 가리킬 요소를 지정할 때 for
속성 대신 htmlFor
속성을 쓰는 것을 유의하자.
ul
태그 안에는 li
요소만 넣기컴포넌트가 리스트 형태임을 가리키기 위해 ul
태그를 사용했으면 그 자식들은 반드시 li
태그로 이루어져야 한다. 다른 태그가 삽입되면 스크린 리더가 리스트를 제대로 읽지 못하는 듯하다.
ul
태그 컴포넌트의 모든 자식들의 태그를 li
로 고쳐주었다.
위 과정을 거친 후 다시 접근성 점수를 측정한 결과, 대부분의 페이지 점수가 100점에 가깝게 올랐다.
물론 좋은 일이지만 Lighthouse가 모든 웹 접근성 항목을 검사해 주는 것은 아니기에, 다른 항목들도 직접 확인하고 개선하는 시간이 필요하다.
tabIndex
를 통한 키보드 접근성 확보키보드 접근성이란 마우스와 같은 장치를 이용하기 어려운 사용자가 키보드 조작만으로도 웹 페이지의 모든 정보 및 기능을 이용할 수 있도록 하는 것을 말한다.
이때 사용자는 주로 'Tab'키와 'Shift + Tab'키를 통해 페이지에서 상호작용 가능한 요소를 탐색하게 되므로, 모든 상호작용 가능한 요소가 올바른 순서로 Tab키에 반응하도록 만들어야 한다.
내 프로젝트의 경우, 이러한 개선 과정을 거쳤다.
<div>
, <li>
등의 요소에 반응하지 않음<a>
, <button>
, <input>
등)가 아니기 때문이다.<button>
태그를 한 번 더 중첩하여 Tab키에 반응하도록 하였다.tabIndex={0}
을 부여한다.display: none
속성을 명시한다. 그러면 Tab키가 해당 요소를 건너뛰게 된다.display: none
을 쓸 수 없다면 tabIndex={visible ? 0 : -1}
속성을 부여하여 요소가 보이지 않을 때 Tab키가 반응하지 않도록 강제한다.useRef
hook을 이용하여 ref
값을 할당한다.visible
)가 바뀔 때마다 visible
이 true
인지 확인하고 (useEffect
hook 이용) 만일 그렇다면 ref.current.focus()
함수를 실행하여 해당 요소가 강제로 focus 되도록 한다.아래는 위 사항을 적용한 Modal 컴포넌트 코드이다.
const Modal = ({ isVisible, children }: ModalProps) => {
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!ref.current) return;
if (isVisible) ref.current.focus();
}, [isVisible]);
return (
<ModalWrapper visible={isVisible}>
<ModalBlock visible={isVisible} ref={ref} tabIndex={0}>
{children}
</ModalBlock>
</ModalWrapper>
);
};