웹 접근성 향상시키기 (키보드 접근성 편)

HYERI ·2023년 8월 11일
0
post-thumbnail

들어가기 전

밴딩머신 프로젝트를 다시 진행하면서 작은 프로젝트인만큼 사소한 디테일도 신경쓸 수 있는 기회가 될 것 같아 여러가지 웹 접근성을 향상시켜보려 했다. 키보드 접근성과 스크린 리더를 사용해 장애인분들도 사용할 수 있을 정도로 접근성을 향상시켜보는 시도를 해보았다.

커스텀 모달

문제점

커스텀 모달을 사용할 때 발생할 수 있는 모달 탭 키 이동과 관련된 문제점은 이러하다

  • 포커스가 모달 밖으로 나갈 수 있다: 일반적으로 모달은 해당 모달 내부에서만 작동해야 합니다. 하지만 모달 내부에서 탭 키를 사용하여 포커스를 이동시키면, 모달 밖으로 포커스가 나갈 수 있다. 이로 인해 사용자 경험이 저하될 수 있다.
  • 포커스 요소의 관리 어려움: 모달 내부에서 탭 키를 사용하여 포커스를 이동시킨 후, 모달을 닫았을 때 이전 포커스 위치로 돌아가야 한다. 이를 위해서는 포커스를 관리하고 기억하는 로직이 필요합니다.
  • 접근성 문제: 웹 접근성을 고려하지 않으면 모달 내부의 포커스 이동이 시각 장애인 등 접근성을 필요로 하는 사용자에게 문제를 야기할 수 있다.

내 밴딩머신 코드 또한 3개의 문제점을 전부 가지고 있었다. 모달이 켜저있는 상태인데도 뒤의 요소들에게 포커스가 가능했으며 모달이 켜졌다가 닫히면 포커스요소의 인덱스가 원상태로 되어 같은 요소로 포커스가 갔다.

적용 전 코드

<div class="modal-wrapper">
	<article class="article-modal">
		<h2>제목</h2>
		<p>설명</p>
		<button class="btn-cancel" type="button">Cancel</button>
		<button class="btn-yes" type="button">Yes</button>
	</article>
</div>

해결:<dialog> 사용

커스텀 모달의 문제 해결은 의외로 매우 단순했다. dialog 요소를 사용하면 거의 모든 문제가 해결이 되었다.

<dialog> 요소는 웹 브라우저 내에서 모달 다이얼로그를 생성하기 위한 HTML 요소이다.

<dialog>
	<h2>제목</h2>
	<p>설명</p>
	<button class="btn-cancel" type="button">Cancel</button>
	<button class="btn-yes" type="button">Yes</button>
</dialog>
  • 포커스 관리: <dialog> 요소는 열려있는 동안 다이얼로그 내부로 포커스를 제한하고, 닫힐 때 이전 포커스 위치로 돌려보내는 포커스 관리를 자동으로 처리한다.
  • 브라우저 호환성: 대부분의 모던 브라우저에서 <dialog> 요소를 지원하며, 라이브러리 없이도 활용할 수 있다.
  • 스타일링: <dialog> 요소는 기본적으로 브라우저의 스타일을 따르지만, CSS를 사용하여 스타일링할 수 있다.
  • backdrop 제공: 알아서 backdrop을 제공해주기에 마크업이 줄 수 있으며 css에서 dialog::backdrop 선택자를 사용해 쉽게 스타일을 변경할 수 있다.
  • 키보드 접근성: esc를 사용해 모달에서 나갈 수 있다.

<dialog> 간단한 사용법

  • 켜고 닫기: 본래 커스텀 모달을 키고 닫기 위해 class를 활용해 display: none을 줬다면 <dialog>는 기본적으로 display: none이 적용되어 있다. 키고 닫기 위해서는 dialog.showModal()dialog.close() 메서드를 사용하면 된다.
  • 스타일링: <dialog>는 기본적으로 여러가지 스타일링이 되어있는데 살펴봐야할 것은
    display: none;
    position: fixed;
    padding: 1em; 

이 정도이다. 기본적으로 fixed가 되어 있고 화면 중간에 위치하게 되어 있다. 그리고 padding이 이미 들어가있어 dialog를 계속 쓰게 된다면 reset.css에서 padding: 0값을 주는 것도 좋을 것 같다.

  • 배경 클릭 시 닫기: dialog는 기본적으로는 배경 (backdrop)을 클릭시 닫히지 않는다. 그렇기에 원할 시에 따로 설정을 해주어야 한다.
    dialog.addEventListener('click', (event) => {
      if (event.target.nodeName === 'DIALOG') {
        dialog.close();
      }
    });

dialog 자체에 이벤트 리스너를 넣어 배경을 클릭시 해당 dialog를 닫아주면 된다.


시작 화면

문제점

밴딩머신 프로젝트 같은 경우 픽셀 게임의 느낌을 주기 위해 화면을 클릭해야지 게임을 시작할 수 있게 해놨다. 커스텀 모달의 문제와 비슷하게 tab을 했을 경우 뒤의 다른 요소를 선택하지 못하게 해야하는데 가능하게 되었다. 해당 시작화면은 header 태그 안에 넣어놔서 커스텀 모달과 같이 dialog 태그를 사용해서 처리하기에는 마크업이 애매해서 JavaScript로 처리해주기로 했다.

적용 전 코드

<header role="dialog">
 	<h1><img src="./img/logo.png" alt="FRONT-END logo"></h1>
	<button class="btn-press"><img src="./img/press-start.png" alt="press start"></button>
	<div class="slot-wrapper"></div>
</header>
startEvent(bgmAudio) {
this.header.addEventListener(
    "click",
    (event) => {
      event.currentTarget.style.position = "static";
      event.currentTarget.style.cursor = "initial";
      this.btnPress.style.display = "none";
      this.slot.style.display = "none";
    },
    { once: true }
);

해결: JavaScript 코드

focusEvent() {
  this.header.addEventListener("keydown", (event) => {
    if (event.key === "Tab") {
      event.preventDefault();
      this.btnPress.focus();
    } else if (event.key === "Enter") {
      event.currentTarget.style.position = "static";
      event.currentTarget.style.cursor = "initial";
      this.btnPress.style.display = "none";
      this.slot.style.display = "none";
    }
  });
}
  • <header>안 의 요소 중 btnPress만 focus 될 수 있도록 처리했다.
  • Enter 클릭 시 시작화면클릭 이벤트와 같이 나타날 수 있도록 했다.
  • <header> 자체 keydown 이벤트에 리스너를 추가해주는 것이기 때문에 <header> 태그 전에 다른 마크업이 있다면 tab이 바로 btnPress에 가지 않는다. 이 프로젝트처럼 시작화면이 있다면 body 태그 안에 가장 최상단에 위치시켜야한다.

BGM 상태 전환

문제점

시작화면 클릭 시 bgm이 자동으로 재생되게 만들어놨다. 하지만

  • 웹사이트나 앱에서 소리를 자동으로 재생하면, 화면을 방문하는 사용자들에게 예상치 못한 소리가 나와 깜짝 놀라거나 불편한 경험을 줄 수 있다.
  • 특히 시각 및 청각 장애를 가진 사용자들은 이와 같은 예상치 못한 소리가 더 큰 불편을 유발할 수 있다.

그래서 끄고 킬 수 있는 버튼을 제공했지만 시각장애인의 경우 tab으로 먼저 bgm 버튼에 접근할 수 있지만 다시 접근하기 위해서는 수많은 버튼을 tab으로 이동시키고 다시 접근해야했다.

해결: 키보드 접근

단순하게 s를 눌렀을 시 bgm을 키고 끌 수 있게 했다. 스크린리더를 시험해보며 안내텍스트도 적용할 예정이다.

  keyboardEvent(bgmAudio) {
    document.addEventListener("keydown", (event) => {
      if (bgmAudio.paused === true && event.key.toLowerCase() === "s") {
        this.startMusic(bgmAudio);
      } else if (bgmAudio.paused === false && event.key.toLowerCase() === "s") {
        this.pasueMusic(bgmAudio);
      }
    });
  }

form 태그 연동

문제점

스크린리더를 위해 눈을 감고 직접 확인하던 중 입금액 입력을 했을때 엔터를 쳐도 바로 입금되지 않아서 불편했다. 제작할 당시에는 서버에 보낼 데이터가 없다고 생각해서 form 태그 없이 썼는데 form을 써야하는 이유를 깨달았다.

해결: form 태그 추가

<div class="counter-wrapper">
    <!-- 잔액 표시 -->
    <p class="inserted">
        잔액: <span class="money">0원</span> <span class="a11y-hidden">이 남았습니다.</span>
    </p>
    <!-- 거스름돈 반환 -->
    <button class="btn-return" type="button">거스름돈 반환</button>
    <!-- 입금액 입력 -->
    <form>
        <label class="a11y-hidden" for="inputPayment">입금액 입력</label>
        <input class="input-payment" id="inputPayment" type="number" step="1000" placeholder="입금액 입력" min="0" max="100000" required>
        <!-- 입금 버튼 -->
        <button class="btn-payment" type="submit">입금</button>
    </form>
    <!-- 현재 카트 -->
    <ul class="list-currentCart">
    </ul>
    <!-- 획득 버튼 -->
    <button class="btn-gain" type="button">획득</button>
</div>
  • 입금액 입력 인풋과 버튼을 form 태그로 감싸고 입금 버튼의 type을 submit으로 변경했다.
  • 입금 버튼 이벤트 리스너에 event.preventDefault()를 추가해주었다.
  • 엔터로 처리해야할 것 같은 것들은 form 태그를 필수로 넣자!

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

유익한 자료 감사합니다.

답글 달기