ARIA 접근성 가이드

적용 방법

콘텐츠 숨기기

display: none, hidden, visibility, aria-hidden, 시각적 숨김(sr-only), inert가 화면·보조기술·포커스에 각각 어떤 영향을 주는지 비교하고, 목적에 맞는 숨김 방법을 고르는 법을 정리합니다.

무엇을, 누구에게서 숨기나요

“숨긴다”는 말에는 세 가지 서로 다른 뜻이 있습니다. 어떤 방법을 쓸지는 누구에게서 숨기려는지에 따라 정해집니다.

  • 모든 사람에게서 숨기기 — 닫힌 메뉴, 선택되지 않은 탭 패널처럼 지금은 존재하지 않는 것으로 다룰 콘텐츠입니다. display: none, hidden 속성을 씁니다.
  • 화면에서만 숨기기 — 아이콘 버튼의 보충 텍스트, 표 제목처럼 눈으로는 맥락에서 알 수 있지만 화면낭독기 사용자에게는 필요한 정보입니다. 시각적 숨김 클래스(.sr-only, .visually-hidden, 국내에서는 .blind라고도 부름)를 씁니다.
  • 보조기술에서만 숨기기 — 장식 아이콘, 같은 내용이 옆에 텍스트로 이미 있는 그림처럼 읽히면 오히려 방해가 되는 요소입니다. aria-hidden="true"를 씁니다.

여기에 더해, 모달이 열려 있는 동안 뒤쪽 페이지처럼 보이지만 조작할 수 없게 해야 하는 영역에는 inert 속성을 씁니다.

방법별 비교

숨김 방법이 화면·보조기술·포커스에 미치는 영향
방법화면레이아웃 공간보조기술(접근성 트리)키보드 포커스
display: none안 보임차지하지 않음제외불가
hidden 속성안 보임차지하지 않음제외불가
visibility: hidden안 보임차지함제외불가
aria-hidden="true"보임차지함제외 (하위 전체)가능 — 막지 않음
시각적 숨김(.sr-only)안 보임사실상 없음(1px)노출가능
inert 속성보임차지함제외 (하위 전체)불가 (클릭도 불가)
opacity: 0안 보임차지함노출가능
role="presentation"/none보임차지함역할만 제거, 내용은 노출요소에 따라 다름

role="none"은 숨김이 아닙니다

presentation/none은 요소의 의미(역할)만 지우고 안쪽 텍스트는 그대로 읽힙니다. 레이아웃용 <table>을 평범한 텍스트처럼 읽히게 할 때 쓰는 것이지, 콘텐츠를 숨기는 수단이 아닙니다.

display: none, hidden, visibility

display: none과 HTML hidden 속성은 요소를 모두에게서 완전히 없앱니다. 화면에 그려지지 않고, 접근성 트리에서도 빠지며, 포커스도 받을 수 없습니다. 접힌 아코디언 패널, 닫힌 드롭다운 메뉴처럼 “지금은 없는 것”에 씁니다.

HTML
<button type="button" aria-expanded="false" aria-controls="faq-1">배송은 얼마나 걸리나요?</button>
<div id="faq-1" hidden>
  주문 후 2~3일 안에 도착합니다.
</div>
  • hidden 속성은 브라우저 기본 스타일(display: none)로 동작합니다. CSS에서 display: flex 등을 지정하면 덮어써져 화면에 다시 보이므로, 필요하면 [hidden] { display: none !important; }를 둡니다.
  • hidden="until-found"는 브라우저의 페이지 내 찾기로 내용을 찾으면 자동으로 펼쳐지는 값입니다. 펼쳐지기 전에는 접근성 트리에서도 빠집니다. 지원하지 않는 브라우저에서는 일반 hidden처럼 동작합니다.
  • 숨겨진 요소라도 aria-labelledby·aria-describedby로 직접 참조하면 그 텍스트는 이름·설명 계산에 쓰입니다(접근 가능한 이름 계산 참고).

visibility: hidden

visibility: hidden도 화면과 접근성 트리에서 요소를 빼고 포커스를 막지만, 레이아웃 공간은 남깁니다. 자리를 비워 둔 채 페이드 애니메이션을 하는 경우(opacity와 visibility를 함께 전환)에 유용합니다.

애니메이션과 함께 숨기기
.menu { opacity: 0; visibility: hidden; transition: opacity .2s, visibility .2s; }
.menu.is-open { opacity: 1; visibility: visible; }

opacity: 0만으로는 숨겨지지 않습니다

opacity: 0, transform: scale(0), height: 0만 쓰면 눈에만 안 보일 뿐, 화면낭독기는 내용을 읽고 키보드 포커스도 들어갑니다. 사용자는 보이지 않는 링크에 포커스가 가는 상황을 겪습니다. 닫힌 상태에서는 반드시 visibility: hidden이나 display: none을 함께 적용합니다.

참고로 visibility: hidden인 부모 안에서 자식에 visibility: visible을 주면 그 자식은 다시 보이고 접근성 트리에도 들어갑니다. display: none은 자식이 되살릴 수 없습니다.

aria-hidden="true"

aria-hidden="true"는 화면은 그대로 두고 요소와 그 하위 전체를 접근성 트리에서 뺍니다. 장식 아이콘, 옆에 같은 텍스트가 있는 아이콘, 시각 효과용 복제 요소를 숨길 때 씁니다.

권장장식 아이콘 숨기기
<button type="button">
  <svg aria-hidden="true" focusable="false"><use href="#icon-trash"></use></svg>
  삭제
</button>

<p><span aria-hidden="true">★★★★☆</span><span class="sr-only">별점 5점 만점에 4점</span></p>

aria-hidden는 포커스와 클릭을 막지 않습니다. 그래서 포커스할 수 있는 요소에 쓰면 키보드 포커스는 가는데 화면낭독기는 아무것도 읽지 않는, 가장 혼란스러운 상태가 됩니다. WAI-ARIA 1.2와 ARIA 사용 규칙도 포커스를 받거나 받을 수 있는 요소(또는 그 조상)에 aria-hidden="true"를 쓰지 말라고 권고합니다.

비권장포커스 가능한 요소를 aria-hidden으로 숨김
<div aria-hidden="true">
  <a href="/event">이벤트 보기</a>   <!-- Tab으로 도달하지만 읽히지 않음 -->
</div>
권장모두에게서 숨기거나, inert로 비활성화
<div hidden>
  <a href="/event">이벤트 보기</a>
</div>
<!-- 보이게 두되 조작을 막아야 하면 -->
<div inert>
  <a href="/event">이벤트 보기</a>
</div>
  • aria-hidden="false"는 display: none 등으로 숨긴 콘텐츠를 다시 노출하지 않습니다. 보조기술 지원도 일관되지 않으므로, 값을 false로 바꾸기보다 속성을 제거합니다.
  • <body>나 본문 전체를 감싼 요소에 aria-hidden="true"를 주면 페이지 전체가 읽히지 않습니다. 모달 뒤 배경 처리에 이 방법을 쓸 때는 대화상자가 그 범위 밖에 있어야 하며, 지금은 inert나 <dialog>의 showModal()을 쓰는 편이 안전합니다.

시각적으로만 숨기기 (sr-only)

화면낭독기에는 읽히되 화면에서는 보이지 않게 하려면, 요소를 1픽셀 크기로 줄이고 잘라 내는 CSS 클래스를 씁니다. display: none이나 visibility: hidden은 보조기술에서도 빠지므로 이 용도로 쓸 수 없습니다.

시각적 숨김 클래스
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;  /* 단어 사이 공백이 사라져 붙어 읽히는 문제 방지 */
  border: 0 !important;
}

/* 포커스를 받으면 다시 보이게 (건너뛰기 링크 등) */
.sr-only-focusable:focus,
.sr-only-focusable:focus-within {
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  overflow: visible !important;
  clip: auto !important;
  clip-path: none !important;
  white-space: normal !important;
}

대표적인 쓰임은 다음과 같습니다.

  • 아이콘만 있는 버튼·링크의 이름 텍스트 (aria-label 대신 쓰면 자동 번역에도 반영됩니다)
  • “새 창 열림”, “(필수)”처럼 시각적으로는 기호로 표현한 정보의 텍스트
  • 시각적으로는 디자인상 생략한 제목(<h2 class="sr-only">상품 목록</h2>)
  • 상태 메시지를 담는 라이브 리전

포커스 가능한 요소는 시각적으로 숨기지 마세요

링크나 버튼을 .sr-only로 숨기면 키보드 사용자는 보이지 않는 곳에 포커스가 가서 위치를 잃습니다(WCAG 2.4.7 포커스 표시). 건너뛰기 링크처럼 꼭 필요하다면 포커스를 받을 때 보이도록 .sr-only-focusable 같은 규칙을 함께 씁니다.

inert 속성

HTML inert 속성을 준 요소와 하위 전체는 화면에는 보이지만 포커스·클릭·텍스트 선택이 불가능해지고, 접근성 트리에서도 빠집니다. 모달 대화상자가 열린 동안 뒤쪽 페이지, 화면 밖으로 밀려난 사이드 메뉴, 로딩 중인 영역에 적합합니다.

모달 뒤 배경 비활성화
<div id="app" inert>   <!-- 모달이 열린 동안 배경 전체 -->
  ...
</div>
<div role="dialog" aria-modal="true" aria-labelledby="dlg-h">
  <h2 id="dlg-h">로그인</h2>
  ...
</div>
  • inert는 시각적으로 흐리게 바뀌지 않습니다. 사용자가 조작할 수 없다는 것을 알 수 있도록 별도의 스타일(반투명 배경 등)을 함께 줍니다.
  • <dialog> 요소를 showModal()로 열면 브라우저가 나머지 문서를 자동으로 inert 상태로 만듭니다. 대화상자 패턴과 키보드와 포커스 문서를 참고하세요.

직접 비교해 보기

숨김 방법을 고른 뒤 아래 “대상 버튼”이 어떻게 바뀌는지 확인해 보세요. Tab 키로 이동해 보면 포커스가 들어가는지 알 수 있습니다.

금지 사례와 점검 포인트

  • 포커스 가능한 요소(또는 그 조상)에 aria-hidden="true" — 포커스는 가지만 읽히지 않습니다. 가장 흔하고 심각한 실수입니다.
  • 의미 있는 콘텐츠에 aria-hidden="true" — 보이는 사람만 정보를 얻습니다. 대체 텍스트 없이 숨긴 정보성 아이콘·차트가 대표적입니다(WCAG 1.1.1).
  • opacity: 0, height: 0만으로 닫힌 메뉴 숨기기 — 보이지 않는 링크에 포커스가 들어갑니다.
  • 화면낭독기용 텍스트를 display: none으로 숨기기 — 아무에게도 읽히지 않습니다. .sr-only를 씁니다.
  • 라이브 리전을 display: none이나 aria-hidden으로 숨기기 — 변화가 알려지지 않습니다.
  • 글자색을 배경색과 같게 하거나 font-size: 0으로 숨기기 — 고대비 모드나 사용자 스타일에서 드러나고, 일부 보조기술은 읽지 않습니다.
  • 보이는 텍스트와 다른 내용을 숨겨서 덧붙여 이름을 바꾸기 — 음성 인식 사용자를 위해 보이는 텍스트가 이름 앞에 오게 합니다(WCAG 2.5.3).

확인 방법

브라우저 개발자 도구의 접근성 트리에서 요소가 “무시됨(ignored)”으로 표시되는지 확인하고, 키보드 Tab 이동과 화면낭독기 가상 커서 이동을 둘 다 해 봅니다. 자세한 내용은 테스트 도구 문서를 참고하세요.