밴딩머신 프로젝트를 다시 진행하면서 작은 프로젝트인만큼 사소한 디테일도 신경쓸 수 있는 기회가 될 것 같아 여러가지 웹 접근성을 향상시켜보려 했다. 키보드 접근성과 스크린 리더를 사용해 장애인분들도 사용할 수 있을 정도로 접근성을 향상시켜보는 시도를 해보았다.
커스텀 모달을 사용할 때 발생할 수 있는 모달 탭 키 이동과 관련된 문제점은 이러하다
내 밴딩머신 코드 또한 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를 사용하여 스타일링할 수 있다.dialog::backdrop
선택자를 사용해 쉽게 스타일을 변경할 수 있다. esc
를 사용해 모달에서 나갈 수 있다. <dialog>
간단한 사용법display: none
을 줬다면 <dialog>
는 기본적으로 display: none
이 적용되어 있다. 키고 닫기 위해서는 dialog.showModal()
과 dialog.close()
메서드를 사용하면 된다. <dialog>
는 기본적으로 여러가지 스타일링이 되어있는데 살펴봐야할 것은 display: none;
position: fixed;
padding: 1em;
이 정도이다. 기본적으로 fixed가 되어 있고 화면 중간에 위치하게 되어 있다. 그리고 padding이 이미 들어가있어 dialog를 계속 쓰게 된다면 reset.css에서 padding: 0
값을 주는 것도 좋을 것 같다.
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 }
);
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 될 수 있도록 처리했다. <header>
자체 keydown 이벤트에 리스너를 추가해주는 것이기 때문에 <header>
태그 전에 다른 마크업이 있다면 tab이 바로 btnPress에 가지 않는다. 이 프로젝트처럼 시작화면이 있다면 body
태그 안에 가장 최상단에 위치시켜야한다. 시작화면 클릭 시 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을 써야하는 이유를 깨달았다.
<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>
event.preventDefault()
를 추가해주었다.
유익한 자료 감사합니다.