<BaseInput> 같은 커스텀 컴포넌트에서는 Vue의 구조 특성상 <label for="..."> 구조가 잘못되기 쉽습니다.

 

스크린리더는 이 input이 어떤 필드인지 알 수 없습니다.

 

<!-- 문제 있는 코드 -->
<template>
  <div>
    <label>{{ label }}</label>
    <input :value="modelValue" />
  </div>
</template>

 

 

<!-- 개선 포인트 -->

  1. input과 label 연결을 위한 id 생성
    <template>
      <div>
        <label :for="inputId">{{ label }}</label>
        <input
          :id="inputId"
          :value="modelValue"
          @input="$emit('update:modelValue', $event.target.value)"
        />
      </div>
    </template>

    <script setup>
    const inputId = `input-${Math.random().toString(36).substr(2, 9)}`;
    </script>
  2. aria-labelledby 대체 방식
    시각적으로 라벨을 숨겨야 할 경우:
    <label :id="labelId" class="sr-only">{{ label }}</label>
    <input :aria-labelledby="labelId" ... />

  3. v-model 호환 개선
    defineProps(['modelValue', 'label']);
    defineEmits(['update:modelValue']);

QA 도구(Axe, Lighthouse, NVDA) 모두에서 라벨 오류가 사라졌으며, 해당 방식은 이후 전체 입력 컴포넌트에 공통 적용되어 코드 품질 및 접근성 준수도를 동시에 끌어올릴 수 있었습니다.

'퍼블리싱 로그 > 웹접근성' 카테고리의 다른 글

IR(Image Replacement)  (0) 2024.03.11
버튼 / TAB aria 속성  (0) 2024.03.06
웹 접근성 키보드 포커스 처리  (0) 2024.03.05
탭 UL 접근성  (0) 2024.02.27
반응형 웹 vs 적응형 웹  (0) 2023.06.29

IR(image replacement) 란?

웹 접근성 준수를 위해 이미지 사용 시 대체 텍스트를 제공해야 하며,
단순히 스크린리더 사용자뿐 아니라 검색 엔진의 효과적인 내용 수집을 위해서도 필수적입니다.

 

* 의미있는 이미지를 배경으로 처리하고 그에 상응하는 내용을 text로 기입하는 방법입니다.

 

간단하게는 아래와 같이 img 태그 내 alt 속성을 넣는 방법이지만 background-image 를 사용하는 경우도 많기 때문에

IR 방법을 알고 계시면 좋을 것 같습니다.

<img src="/img/image.jpg" alt="대체 텍스트">

 

IR 작성 방법
1. display:none

2.visibility:hidden

3. overflow:hidden

1. display:none는 센스리더, Jaws, NVDA에서 모두 내용을 읽지 못함
2. visibility: hidden는 센스리더에서는 내용을 읽을 수 있었으나 Jaws, NVDA는 모두 내용을 읽지 못함
3. overflow:hidden의 경우 센스, Jaws, NVDA 모두 내용을 읽을 수 있음

display:none은 화면에서 아주 없애 버리는 것으로 공간을 차지 하지 아니하며,
visibility:hidden은 화면에는 안보이나 공간은 차지하고 있습니다.

접근성 있게 숨김 콘텐츠를 제공하는 방법으로 overflow:hidden을 사용한
포지셔닝 기법을 많이 사용하므로 참고하시기 바랍니다.

포지션 예

.hidden {position:absolute; left:-10000px; top:auto; height:1px; overflow:hidden; }
.hidden {display: block; width: 0; text-indent: -9000px; overflow: hidden;}

 

주의

IR기법으로 콘텐츠를 숨김처리 할 때 visibility: hidden으로 구현하는 경우 특정 센스리더에서 내용을 읽지 못하는 경우가 있으니 overflow:hidden으로 구현을 해야 합니다.

 

 

참고자료

https://nuli.navercorp.com/community/article/1132804?email=true

버튼 속성 추가

aria-haspopup="dialog" 속성 추가, aria-controls 연결된 팝업의 id값 적용,

팝업 열림 유무에 따라 aria-expanded="false | true" 상태 값 변화 적용해주시면 됩니다.

<button
    type="button"
    class="btn-filter"
    aria-haspopup="dialog"
    aria-controls="연결된 팝업의 id"
    aria-expanded="false | true"
>

 

 

참고:

https://www.w3.org/TR/wai-aria-1.1/#tab

 

Accessible Rich Internet Applications (WAI-ARIA) 1.1

A section of a page that consists of a composition that forms an independent part of a document, page, or site. An article is not a navigational landmark, but may be nested to form a discussion where assistive technologies could pay attention to article ne

www.w3.org


TAB UI 적용 사례
연결된 tab의 contents의 활성화는 aria-selected 가 아닌 aria-expanded로 구분해 주어야 합니다
https://aoa.gitbook.io/skymimo/aoa-2018/2018-aria/tab

 

TAB키 이동 웹 접근성

https://osmosemococoro.github.io/

 

디자이너분의 요청으로 포커스 라인을 제거를 하는 경우가 있습니다.

이렇게 

a, input, button {
  outline: 0;
}

:focus {
  outline: none;
}

 

이런 경우 브라우저 상에는 라인이 노출 되지 않게 됩니다.

시각장애인 등 키보드만 이용해 웹사이트를 이용 할 경우 키보드 요소를 노출 시켜 주어야 합니다.

 

크롬에 추가된 :focuis-visible이란 가상 클래스(pseudo class)를 통해 이를 디자인을 해치지 않고 구현할 수 있습니다.

/* 키보드로 버튼에 포커스 시 */
button:focus-visible {
  outline: 3px solid #000;
}

/* 마우스, 터치로 버튼에 포커스 시 */
button:focus:not(:focus-visible) {
  outline: none;
  box-shadow: 1px 1px 5px rgba(1, 1, 0, .7);
}

'퍼블리싱 로그 > 웹접근성' 카테고리의 다른 글

커스텀 Input 컴포넌트의 라벨 연결 오류 와 해  (0) 2025.06.12
IR(Image Replacement)  (0) 2024.03.11
버튼 / TAB aria 속성  (0) 2024.03.06
탭 UL 접근성  (0) 2024.02.27
반응형 웹 vs 적응형 웹  (0) 2023.06.29

탭 경우 "tab-linker"(탭영역) , "tab-contents" (탭패널) 부분으로 나뉘어 지고, 

접근성 작업을 하면서 수정 부분들이 전달 왔습니다.

 

아래와 같이 코드를 작업했는데 뭐가 잘 못 된건지 모르겠어서

자료를 찾아보니 컨텐츠에는 selected 가 아니라 expanded 를 적용해야 한다고 합니다.
이유는 role="tabpanel"에는 제공하지 않고 role="tab" 등 초점을 받을 수 있는 요소에 제공하는 것이 원칙이라서

 

이 와 같은 실수를 하지 않기위해 기록하겠습니다.

 

<div class="tab-area">
    <div class="tab-linker">  탭 버튼  </div>
    <div class="tab-contents">
        <ul>
            <li role="tabpanel" id="tab1-tab2" aria-labelledby="tab1" aria-selected="true" tabindex="0">            
                <ul class="comp-payment-list">
                    <li class="comp-list">
                        <a href="">컨텐츠 리스트-1</a>
                    </li>
                </ul>
            </li>
        </ul>
    </div>
</div>

 

<div class="tab-area">
    <div class="tab-linker">
        <ul role="tablist" lass="tab-accessibility">
        <li role="presentation">
            <a href="javascript:;" role="tab" aria-selected="true" aria-controls="tab1-tab" tabindex="0" id="tab1"> TAB-1</a>
        </li>
        <li role="presentation">
            <a href="javascript:;" role="tab" aria-selected="false" aria-controls="tab2-tab" tabindex="-1" id="tab2"> TAB-2</a>
        </li>
        </ul>
    </div>
    <div class="tab-contents">
        <ul>            
            <li role="tabpanel" id="tab1-tab2" aria-labelledby="tab1" aria-expanded="true" tabindex="0">
                <ul class="comp-payment-list">
                    <li class="comp-list">
                        <a href="">컨텐츠 리스트-1</a>
                    </li>
                    <li class="comp-list">
                        <a href="">컨텐츠 리스트-2</a>
                    </li>
                </ul>
            </li>
            <li role="tabpanel" id="tab1-tab2" aria-labelledby="tab2" aria-expanded="true">
               컨텐츠-2
            </li>
        </ul>
    </div>
</div>

 

 

자세한 내용 참고 

w3c 공식문서: https://www.w3.org/TR/wai-aria-1.1/#tab

https://aoa.gitbook.io/skymimo/aoa-2018/2018-aria/tab

+ Recent posts