적용 방법
콘텐츠 숨기기
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 속성은 요소를 모두에게서 완전히 없앱니다. 화면에 그려지지 않고, 접근성 트리에서도 빠지며, 포커스도 받을 수 없습니다. 접힌 아코디언 패널, 닫힌 드롭다운 메뉴처럼 “지금은 없는 것”에 씁니다.
<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"를 쓰지 말라고 권고합니다.
<div aria-hidden="true">
<a href="/event">이벤트 보기</a> <!-- Tab으로 도달하지만 읽히지 않음 -->
</div><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 이동과 화면낭독기 가상 커서 이동을 둘 다 해 봅니다. 자세한 내용은 테스트 도구 문서를 참고하세요.