본문 바로가기

HTML & CSS

[HTML/JS] 웹 접근성을 고려한다면, 꼭 알아야 하는 속성 aria-controls 와 aria-selected 에 대한 정리

반응형

WAI-ARIA

웹 개발을 할 때 편리한 레이아웃을 셋팅하여 사용성과 편의성을 증진시키는 것도, 화려한 화면과 인터렉션을 통해 살아움직이면서도 아름다운 사이트를 만드는 것도 웹 페이지에 접근하는 사용자에게 흥미를 유발하는 점에서 중요하지만, 간과하지 말아야 하는 점은 스크린 리더 및 보조도구를 사용하는 사용자의 웹 접근성을 보장하는 일인 것 같다. 특히, 아무런 의미 없는 태그인 <div> ,<span> 의 무분별한 사용은 스크린 리더를 사용하는 사용자에게 웹 페이지의 유의미한 정보를 제공해주지 못하기 때문에 되도록 사용을 지양하는 것이 좋다고 하는데, 그럼에도 불구하고 사용해야 하는 상황에서는  웹 접근성을 향상시키기 위해 사용되는 WAI-ARIA (Web Accessibility Initiative - Accessible Rich Internet Applications) 속성을 적용하면, 사용자의 웹 접근성을 보장할 수 있다.

 

WAI-ARIA
웹 애플리케이션과 동적 콘텐츠에 접근성을 제공하는 데 사용되는 기술 표준이다. 이 표준은 레이블, 롤, 상태, 속성 등을 포함하는 다양한 속성을 제공하여 웹 페이지나 웹 애플리케이션의 요소에 의미를 부여하고 접근성을 향상시킨다.

 

오늘 정리하고자 하는  aria-controls  이라는 속성도 이 중 하나이며, 흔히 모달창, 체크박스, 탭 메뉴 등을 사용하는 콘텐츠에 대한 웹 접근성을 높이기 위해 사용될 수 있다.

 

aria-controls

우선 aria-controls 를 지정하는 요소(태그)는 특정 컴포넌트가 눈에 보이게 하거나, 안 보이게 하기 위해 사용하는 버튼 등에 지정한다. 예를 들어, tab1, tab2, tab3 이라는 탭 요소들이 있을 때 tab1 을 클릭하면 tab1 과 관련한 내용이 화면에 표시되고, tab2 를 클릭하면 tab2와 관련된 내용이 표시되는데, 이 때 해당 탭 요소(태그)의 속성에 aria-controls 를 적용한다.

 

  <div role="tablist">
        <!-- 탭 버튼 목록 -->
        <button role="tab" aria-controls="tab-content-1" class="tab-button" tabindex="0">Tab 1</button>
        <button role="tab" aria-controls="tab-content-2" class="tab-button" tabindex="0">Tab 2</button>
        <button role="tab" aria-controls="tab-content-3" class="tab-button" tabindex="0">Tab 3</button>
  </div>

 

위 예시를 보면 tab 역할(role)를 부여 받은 button 요소의 속성으로 aria-controls 를 지정한 것을 볼 수 있는데, 여기서 해당 속성의 값으로 tab-content-1 .. 이 입력되어 있다. 

 

    <!-- 탭 내용 -->
    <div id="tab-content-1" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 1.</p>
    </div>
    <div id="tab-content-2" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 2.</p>
    </div>
    <div id="tab-content-3" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 3.</p>
    </div>

 

앞서 aria-controls 의 값으로 지정된 tab-content-1.. 은 위와 같이 탭의 내용을 표시하는 요소를 식별하는 데 사용되며, 탭 버튼과 내용 사이의 관계를 식별하는 데 id 속성의 값과 탭 버튼의 aria-controls 속성의 값이 일치하는 지 여부로 판단한다. 즉,  스크린 리더 및 보조기기를 통해 사용자가 해당 요소에 접근하게 되면, 스크린 리더는 해당 탭 버튼과 탭 내용 간의 관계를 파악하고, 사용자에게 특정 탭을 클릭 시 보여지는 탭 내용을 읽어서 전달하게 된다.

 

만약에 div 와 button 태그에 아무런 속성이 지정되어 있지 않다면 어떻게 될까? 시각적으로 문제가 없는 사람은 해당 내용을 바로 식별해서 어떤 역할을 하는 것인지 파악할 수 있겠으나, 음성을 통해서 해당 탭과 내용을 파악해야 하는 사용자 입장에서는 파악하기 매우 어려울 것이다.


 

그런데, 해당 탭 메뉴가 선택되었는지는 어떻게 분간할까?

여기 까지 따라왔다면 한 가지 의문이 들 수 있다. 그럼 스크린 리더는 선택된 탭 내용이 무엇인지 어떻게 식별할까? 앞서 예시대로만 본다면, 스크린 리더는 어떤 탭 내용이 활성화 되어있는지 파악하기 어렵다. 즉, aria-controls 와 해당 내용의 id 를 일치시킨다면 탭 버튼과 해당 내용 간에 연관성은 인지할 수 있으나 사용자가 각 버튼을 클릭 했을 때 무엇이 활성화되어있는지는 파악하기 어렵다. 따라서 이 때  활성화 사실을 전달할 수 있도록 돕는 속성이 바로 aria-selected 속성이다. 


aria-selected

aria-selected 는  값으로 false 와 true 을 가지는데, false 라면 비활성화, true 라면 활성화되어 있음을 스크린리더가 인식하도록 돕는다. 

    <!-- 탭 내용 -->
    <div id="tab-content-1" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 1.</p>
    </div>
    <div id="tab-content-2" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 2.</p>
    </div>
    <div id="tab-content-3" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 3.</p>
    </div>

여기서 다시 탭 내용을 살펴보면,  이와 같은 형식으로만 존재한다면 스크린 리더는 어떤 탭 내용이 활성화되어 있는지 알 수가 없다. 

    <div id="tab-content-1" role="tabpanel" class="tab-content" aria-selected="true">
        <p>This is the content for Tab 1.</p>
    </div>

그러나 위와 같이 aria-selected가 true 로 지정되어 있다면, 스크린리더는 해당 내용을 식별하고 사용자에게 알려줄 수 있다.

 

이 다음 부터는 자바스크립트를 통해서 동적으로 속성을 값을 전달함으로써 각 탭 버튼을 클릭했을 때 해당 탭 내용이 스크린 리더가 인식하도록 지정할 수 있다.

 

스크린 리더가 동적으로 탭 내용을 인식하게 하려면

 // JavaScript를 사용하여 탭을 관리
        const tabButtons = document.querySelectorAll('[role="tab"]');
        const tabContents = document.querySelectorAll('[role="tabpanel"]');

        tabButtons.forEach((button) => {
            button.addEventListener("click", () => {
                // 모든 탭 버튼과 탭 콘텐츠를 초기화
                tabButtons.forEach((btn) => {
                    btn.classList.remove("active");
                    btn.setAttribute("aria-selected", "false");
                });
                tabContents.forEach((content) => {
                    content.classList.remove("active");
                });

                // 선택한 탭 버튼과 연결된 탭 콘텐츠를 활성화
                const controlId = button.getAttribute("aria-controls");
                const tabContent = document.getElementById(controlId);
                button.classList.add("active");
                button.setAttribute("aria-selected", "true");
                tabContent.classList.add("active");
            });
        });

위 코드를 보면 각 탭 버튼을 순회하며  'click' 이벤트가 발생하는 순간 선택된 버튼을 제외한 aria-selected 속성의 값을 false 로 지정하고, 선택한 탭 버튼이 가지고 있는 aria-controls와 일치하는 id 값을 가진 탭 내용 요소에 aria-selected 를 true 로 지정하는 것을 볼 수 있다.

 

이렇게 함으로써 스크린 리더는 사용자가 어떤 탭을 선택했는지, 해당 탭의 내용은 무엇인지를 식별하고 사용자에게 알려줄 수 있게 된다.

 

포스트에 사용된 코드(전체)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Accessible Tab Menu</title>
    <style>
        /* 스타일링은 간단한 예시를 위해 최소한으로 유지. */
        .tab-button {
            cursor: pointer;
            padding: 10px 20px;
            border: 1px solid #ccc;
            background-color: #f0f0f0;
        }

        .tab-content {
            display: none;
            padding: 10px;
            border: 1px solid #ccc;
        }

        .tab-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div role="tablist">
        <!-- 탭 버튼 목록 -->
        <button role="tab" aria-controls="tab-content-1" class="tab-button" tabindex="0">Tab 1</button>
        <button role="tab" aria-controls="tab-content-2" class="tab-button" tabindex="0">Tab 2</button>
        <button role="tab" aria-controls="tab-content-3" class="tab-button" tabindex="0">Tab 3</button>
    </div>

    <!-- 탭 내용 -->
    <div id="tab-content-1" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 1.</p>
    </div>
    <div id="tab-content-2" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 2.</p>
    </div>
    <div id="tab-content-3" role="tabpanel" class="tab-content">
        <p>This is the content for Tab 3.</p>
    </div>

    <script>
        // JavaScript를 사용하여 탭을 관리
        const tabButtons = document.querySelectorAll('[role="tab"]');
        const tabContents = document.querySelectorAll('[role="tabpanel"]');

        tabButtons.forEach((button) => {
            button.addEventListener("click", () => {
                // 모든 탭 버튼과 탭 콘텐츠를 초기화
                tabButtons.forEach((btn) => {
                    btn.classList.remove("active");
                    btn.setAttribute("aria-selected", "false");
                });
                tabContents.forEach((content) => {
                    content.classList.remove("active");
                });

                // 선택한 탭 버튼과 연결된 탭 콘텐츠를 활성화
                const controlId = button.getAttribute("aria-controls");
                const tabContent = document.getElementById(controlId);
                button.classList.add("active");
                button.setAttribute("aria-selected", "true");
                tabContent.classList.add("active");
            });
        });
    </script>
</body>
</html>

 

포스트에 사용된 코드 구현(결과)

Accessible Tab Menu

This is the content for Tab 1.

This is the content for Tab 2.

This is the content for Tab 3.

 

참고자료

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-selected

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-controls

반응형