MacOS에서 기본으로 탑재되어 있는 VoiceOver를 사용하였다. 스크린 리더 사용자는 윈도우 사용자가 많긴 하지만 내가 가지고 있는 노트북이 맥북 밖에 없어서 VoiceOver를 사용하게 되었다.
VoiceOver는 Cmd + f5를 클릭하면 간단하게 사용할 수 있다.
스크린리더와 함께 수정하다 보니 앞서 키보드 접근성으로 위해 수정했던 것에서 더 발전시켜야할 것들이 보였다.
aria-label과 a11y-hidden을 사용해 스크린 리더에 읽힐 텍스트를 적는 중 언제 무엇을 사용하면 좋을 지 모르겠었다. 대체 텍스트를 추가하면서 어떨 때 어떤 것을 써야할 지 고민이 많았다. 무작정 aria-label을 추가해봤을때 제대로 작동하지 않고 validation에서 misuse 일 수도 있다고 떠서 a11y-hidden을 써야할 곳도 있구나라고 생각했다.
aria-label은 웹 접근성을 향상시키기 위한 WAI-ARIA(웹 접근성 이니셔티브 - Accessible Rich Internet Applications) 속성 중 하나이다. 이 속성은 요소에 접근 가능한 이름을 제공하는 데 사용된다. 즉, 해당 요소의 의미나 역할을 설명하는 텍스트를 제공하여 스크린 리더 사용자나 다른 보조 기술을 통해 해당 요소의 의미를 이해할 수 있게 돕는다.
aria-label 속성은 다음과 같은 상황에서 사용될 수 있다:
<button aria-label="검색하기"><img src="search.png" alt="검색"></button>
<div role="button" aria-label="삭제하기">x</div>
<span class="icon" aria-label="메일 보내기"></span>
<div role="button" aria-label="알림 확인">1</div>
aria-label 속성은 해당 요소가 가지고 있는 의미나 역할을 확실히 설명하는 텍스트를 제공하는 것이 중요하다. 이를 통해 스크린 리더 사용자나 보조 기술을 사용하는 사용자들도 웹 콘텐츠를 이해하고 상호 작용할 수 있게 된다.
필요에 따라
aria-labelledby
나aria-describedby
도 같이 사용하면 좋다. 개인적으로 VoiceOver에 읽히지 않아서 사용하지 않았다. 브라우저나 스크린리더마다 다르다고 하니 사용하기 전에 확인하는 것이 좋다.
- aria-labelledby 속성은 요소에 접근 가능한 이름을 제공하는 데 사용된다.
- aria-describedby 속성은 요소에 접근 가능한 설명을 제공하는 데 사용된다.
웹 접근성을 고려하여 특정 콘텐츠를 시각적으로 감추면서도 스크린 리더 사용자에게는 정보를 제공하는 방법 중 하나이다. 이를 통해 시각적으로는 보이지 않지만 스크린 리더 사용자에게는 의미 있는 정보를 전달할 수 있다.
참고로 프로젝트에 사용된 a11y-hidden 코드는 이것이다.
.a11y-hidden {
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
찾아본 결과 aria-label과 a11y-hidden은 각각 다른 목적과 상황에서 사용된다. 어떤 것을 선택해야 하는지에는 상황과 의도에 따라 달라진다.
시각적으로 보이는 콘텐츠에 대체 텍스트 제공: 이미지나 버튼과 같이 시각적으로 보이는 요소에 대체 텍스트를 제공하고 싶을 때 aria-label을 사용한다. 스크린 리더 사용자에게 해당 요소의 역할이나 목적을 설명하는 데 사용된다.
레이블이나 안내문을 시각적으로 감추면서 스크린 리더 사용자에게만 제공: 특정 요소의 레이블이나 안내문을 시각적으로는 표시하지 않으면서 스크린 리더 사용자에게만 제공하고자 할 때 a11y-hidden 클래스를 사용한다. 이는 스크린 리더 사용자에게 중요한 정보를 전달하면서 시각적으로는 레이블을 보이지 않게 할 때 유용하다.
일반적으로 aria-label은 시각적으로 보이는 요소에 대한 대체 텍스트를 제공할 때 사용되며, a11y-hidden 클래스는 시각적으로는 숨기고 스크린 리더 사용자에게만 정보를 제공할 때 사용된다. 어떤 것을 선택할지는 웹 접근성의 목표와 콘텐츠의 특성을 고려하여 결정해야 한다.
<header>
<h1><img src="./img/logo.png" alt="STUDY FRONT-END 밴딩머신"></h1>
<button class="btn-press" aria-label="시작 버튼"><img src="./img/press-start.png" alt="press start">
<span class="a11y-hidden">시작하기 위해 화면을 클릭하거나 엔터를 누르세요.</span>
</button>
<div class="slot-wrapper"></div>
</header>
무엇을 쓰는 것에 대한 정답은 없지만 최대한 용도에 맞게 잘 사용하고 스크린리더를 직접 시도해보면서 작업해야할 것 같다.
동적으로 바뀌는 것들은 스크린리더가 읽어주지 않았다. 그때 aria-live
란 속성을 사용해야 한다고 알게 되었다.
aria-live 속성은 동적으로 업데이트되는 콘텐츠를 스크린 리더 사용자에게 즉시 알릴 때 사용되는 접근성 속성이다. 이 속성을 사용하면 사용자의 주의를 끌고, 변경된 정보를 놓치지 않도록 도와준다.
aria-live 속성은 다양한 값으로 설정될 수 있으며, 다음과 같은 값을 가질 수 있다:
aria-live 속성은 일반적으로 동적으로 변경되는 내용이나 상태에 사용된다. 예를 들어, 채팅 메시지, 실시간 업데이트되는 콘텐츠, 알림 등의 경우에 사용될 수 있다.
<div class="button-wrapper">
<p class="a11y-hidden">이 사이트는 bgm이 자동으로 재생됩니다. 멈추거나 다시 재생하기 위해서는 s를 눌러주세요.</p>
<p class="notice-playing" aria-live="polite">bgm is playing ~ ♪</p>
<button class="btn-pause" type="button" aria-label="bgm 멈춤 버튼" aria-live="polite">
<span class="text">||</span>
</button>
<button class="btn-reset" type="button">reset</button>
</div>
.notice-playing
과 btn-pause
내 텍스트와 aria-label
속성이 바뀌게 된다. aria-live="polite"
속성을 추가해주었다.4000원 입금 후 잔액 액수 스크린 리더
4000원 입금 후 소지금 액수 스크린 리더
aria-live="polite"
속성을 가진 요소 내의 내용을 갱신한 후 스크린 리더에게 알려주는 방식으로 구현한다. 나는 2번의 방법을 택했다.
/* aria-live를 글 전체에다 적용하는 함수 */
allAriaLive(target) {
target.setAttribute("aria-live", "off");
target.offsetWidth; // Reflow를 트리거하여 스크린 리더가 내용을 다시 읽도록 함
target.setAttribute("aria-live", "polite");
setTimeout(() => {
target.removeAttribute("aria-live");
}, 1000);
}
aria-live
속성을 껐다가 다시 켜준다. aria-live="polite"
을 주면 작동이 안되기 때문에 주면 안된다. aria-live="polite"
가 남아있으면 작동이 안되서 setTimeout으로 1초 있다가 삭제해주었다. <div class="possessed">
<p>소지금:
<span class="money">50,000원</span>
<span class="a11y-hidden">이 남았습니다.</span>
</p>
</div>
앞서 설정했듯이 변경된 값은 모두 잘 읽어주었지만 특정 행동을 했을 때 아무 알림이 없어서 무슨 행동을 했는지 확신하기 힘들었다.
announceMessage(message) {
const liveRegion = document.createElement("div");
liveRegion.setAttribute("role", "region");
liveRegion.setAttribute("aria-live", "assertive");
liveRegion.classList.add("a11y-hidden");
liveRegion.textContent = message;
document.body.appendChild(liveRegion);
setTimeout(() => {
liveRegion.remove();
}, 3000);
}
aria-live
속성에 assertive
값을 준다assertive
값을 가진 요소는 즉시 업데이트 내용을 읽어준다. acconceMessage
함수를 불렀다. this.announceMessage(item.dataset.item + " 1개를 장바구니에 담았습니다.");
밴딩머신 내 장바구니 아이템 수량조절을 하기 위해서는 해당 아이템 요소 위에 hover 해야지만 아이템 수량 조절 또는 아이템 삭제 버튼에 접근할 수 있다. 즉 tab 이동으로는 잡히지 않고 잡힌다고 해도 tab으로 버튼을 누를 수 없었다.
tabindex
속성을 부여한다. 밴딩머신 프로젝트 같은 경우 <li>
가 하나의 아이템 요소이므로 tabindex
속성을 부여했다. currentItem.setAttribute("tabindex", 0);
.cart-item:hover,
.cart-item:focus {
.drink-count {
position: absolute;
left: 50%;
transform: translateX(-50%);
z-index: 10;
}
&::after {
display: inline-block;
}
.btn-sub, .btn-add, .btn-remove {
display: inline-block;
}
}
btnKeyboard(currentItem) {
currentItem.addEventListener("keydown", (event) => {
if (event.key === "ArrowLeft") {
currentItem.querySelector(".btn-sub").click();
this.allAriaLive(currentItem);
} else if (event.key === "ArrowRight") {
currentItem.querySelector(".btn-add").click();
this.allAriaLive(currentItem);
} else if (event.key === "ArrowUp") {
currentItem.querySelector(".btn-remove").click();
}
});
}