CSS 음성 지원: CSS로 만들어진 내용에 대한 대체 설명 텍스트 ("CSS to speech: alternative text for CSS-generated content" 정리)

okorion·2025년 10월 3일

1) 이 글이 다루는 문제와 결론 한 줄 요약

  • 문제: ::before/::after/::marker로 넣은 CSS 생성 텍스트·아이콘도 접근 가능한 이름(accName) 계산에 참여한다. 의미 전달용이면 스크린리더가 뭐라고 읽을지 통제해야 하고, 장식용이면 읽히지 않도록 해야 한다.
  • 해결: content:슬래시(/) 뒤에 대체 텍스트(alt) 를 적는다. 의미가 있으면 설명을, 장식이면 빈 문자열 ""을 넣어 발화/노출 제어를 한다.
  • 주의: “CSS로 의미 있는 콘텐츠” 자체는 여전히 권장되지 않는다(번역/검색/가시 라벨 불일치/리더 모드 등). 가능한 한 의미 있는 것은 HTML로.

2) 배경 지식 — 왜 CSS 생성 콘텐츠가 “읽히는가”

브라우저의 Accessible Name and Description Computation 1.2 알고리즘에는 “Name From Generated Content” 단계가 있다. 여기서 ::before/::after(또는 ::marker)의 텍스트 콘텐츠를 누적 텍스트에 포함시킨다. 그래서 아래 링크는 실제 accName이 "CSS Generated Content specification ↗"가 된다.

<a href="..." target="_blank" class="external">CSS Generated Content specification</a>
a.external::after { content: " \2197"; } /* ↗ */

문제는 스크린리더가 이 유니코드 화살표를 “North East Arrow” 같이 기본 대체문구로 읽는다는 점이다. 우리가 의도한 “새 창에서 열림” 의미와 거리가 있다. (유니코드/이모지의 기본 레이블은 리더마다/플랫폼마다 조금 다름)


3) 해법 — content:슬래시(/) 대체 텍스트

스펙(CSS Generated Content Level 3)은 content 뒤에 / "대체 텍스트" 를 허용한다. “시각 매체용(이미지/기호) 콘텐츠에 대해 음성 출력 등 비시각 매체 대체 텍스트를 지정”하는 용도다. 제공되면 음성 출력에서 이 텍스트를 우선 사용해야 한다.

(1) 의미 있는 아이콘/문자에 적용

a.external::after {
  content: " \2197" / "Opens in a New Window";
}

→ accName은 “텍스트 + Opens in a New Window”가 되고, 의도가 정확히 전달된다.utm_source=chatgpt.com)

(2) 장식용 CSS 생성물은 빈 문자열로 숨김

.disclosure-widget::before {
  content: url("data:image/svg+xml,...") / "";
}

→ 접근성 트리에서 발화 제외 효과. 장식용 텍스트 이펙트, 아이콘 반복 등에 필수.

요점: CSS 생성물은 기본적으로 accName에 섞인다. 장식이면 / ""로 무음화, 의미가 있으면 설명 텍스트를 넣어라.


4) 지원 상황과 호환성 포인트

  • 문법 지원: MDN/스펙 기준으로 / "…", / <counter>를 포함한 대체 텍스트 문법이 문서화되어 있고, 2025-08 기준 문서도 이를 명시한다.
  • 알고리즘 포함: accName 1.2가 ::before/::after/::marker 텍스트를 포함하도록 규정.
  • 실측 결과(글에 제시):
    • url() 이미지에 alt 미지정 → 기본적으로 접근성 트리 제외.
    • alt가 빈 문자열 → 역시 제외(기대 동작).
    • alt가 비어있지 않음 → 해당 alt가 발화되고 요소 accName에 포함.
    • 끊어진 이미지여도 alt가 있으면 발화/노출(기대 동작).
    • VoiceOver(Safari/Chrome/Firefox on macOS, iOS 26)에서는 전반적으로 일관. 다만 NVDA+Chrome 조합은 현재 CSS alt 발화를 하지 않는 케이스가 있어 의미 상실 위험이 있다(커뮤니티 관찰).
  • 비호환 대비: / 문법을 지원하지 않는 옛 브라우저에서는 해당 선언이 전체 무효가 된다. 필요하면 앞에 폴백 선언을 두고, 그 다음 줄에 슬래시 문법을 배치한다. (오늘날 IE 등 레거시 지원이 아니면 대개 불필요)

5) “CSS로 의미 있는 콘텐츠”를 권장하지 않는 이유 (리스크)

  1. 가시 라벨 vs accName 불일치
    CSS 이미지가 표시되지 않더라도 alt는 accName에 남아 음성/스피치 컨트롤 기준과 어긋날 수 있다(특히 인터랙티브 요소). WCAG 2.5.3(라벨=이름) 위반 소지.
  2. 렌더 실패 시 대체문구 표시 문제
    HTML <img>는 alt가 대신 노출되는 경우가 많지만, CSS 이미지의 alt는 화면에 표시되지 않는다(예외: Safari iOS, 또는 macOS에서 VoiceOver ON 시 사각 플레이스홀더에 alt 표시).
  3. 자동 번역·현지화 비지원
    현재 CSS 생성 텍스트는 자동 번역 도구가 다루지 못함. 향후 attr("data-alt") 같은 기법이 도움이 될 수 있으나, 자동 번역을 요구한다면 CSS 접근은 부적합.
  4. 리더 모드/대체 스타일 시트
    일부 보조기술/리더 모드는 작성자 CSS를 대체한다. 그러면 CSS로 넣은 의미 콘텐츠는 사라진다.
  5. 검색/선택 불가
    2025년 기준 CSS 생성 텍스트는 페이지 찾기/텍스트 선택 대상이 아니다(스펙은 “검색/선택 가능해야 한다”고 권고하지만, 아직 구현은 들쑥날쑥).

6) 실무 가이드—올바른 적용 패턴

A. 원칙

  • 의미 있는 콘텐츠HTML로(텍스트면 텍스트, 이미지면 <img alt="">).
  • CSS 생성은 기본 장식/중복 표시용. 이 경우 / ""로 무음화.
  • 불가피하게 의미를 CSS로 넣는다면 / "설명"을 함께 넣고, 지원/발화 편차를 감안해 대체 수단(시각 라벨, HTML 보강)을 둔다.

B. 패턴별 코드

1) 외부 링크 화살표(의미 전달)

a.external::after { content: " \2197" / "Opens in a new window"; }

→ accName: 링크 텍스트 + Opens in a new window (의도 정확).

2) 토글 화살표(장식)

button.disclosure::before { content: url("chevron.svg") / ""; }

→ 장식이므로 발화 제외.

3) 텍스트 이펙트(중복 방지)

.hero::after { content: attr(data-text) / ""; } /* 시각 중복 텍스트 무음화 */

→ 시각 효과만 남기고 스크린리더 중복 낭독 제거.

4) 폴백(아주 구형 브라우저 필요 시)

a.external::after { content: " \2197"; }            /* 폴백 */
a.external::after { content: " \2197" / "Opens…"; }  /* 최신 */

지원하지 않는 UA는 첫 줄만 적용, 최신은 두 번째 줄이 우선.


7) 테스트 체크리스트(팀 내 QA용)

  • AOM/DevTools: 접근성 패널에서 accName 문자열 확인(유니코드가 아니라 우리가 준 alt가 붙어 있는지).
  • 스크린리더 조합:
    • macOS: VoiceOver + Safari/Chrome/Firefox
    • Windows: NVDA + Firefox, NVDA + Chrome(주의: CSS alt 미발화 이슈 케이스), JAWS + Chrome
    • iOS: VoiceOver
      조합별로 “읽힘/비읽힘” 상이점을 표로 기록. sarasoueidan.com+1
  • 가시 라벨 일치: 버튼/링크에서 화면 라벨과 accName이 동일한지(스피치 제어 호환, WCAG 2.5.3). sarasoueidan.com
  • 리더 모드/스타일 오프: 스타일 제거 시 핵심 의미가 사라지지 않는지(의미는 HTML에). Social Security
  • 번역 플로우: 자동 번역 파이프라인에서 CSS 생성 텍스트가 누락되지 않는지(대체 경로 필요). sarasoueidan.com

8) 팀 적용용 의사결정 트리

  1. 이 아이콘/텍스트가 의미 전달인가?
    • 예 → HTML로 옮기고 <img alt>/텍스트로 처리.
    • 아니오(장식/중복) → CSS 생성 사용 + content: … / "".
  2. 브라우저/AT 매트릭스에서 최소 한 조합이라도 의미 손실?
    • 예 → CSS 생성 제거 또는 HTML 대체 라벨 추가.
  3. 자동 번역/리더 모드 대상?
    • 예 → HTML에 의미를 둔다.

9) 보충 자료(더 깊이)

  • 스펙: CSS Generated Content Level 3 — 대체 텍스트 / 문법 규정. W3C
  • MDN: content 문법에 대체 텍스트 문서화(2025-08 갱신). developer.mozilla.org
  • accName 1.2: ::before/::after/::marker의 텍스트 포함 규정. W3C
  • 사라 수에이단(Sara Soueidan)의 원문 글(2025-09): 현황/테스트 결과/권장사항 종합. sarasoueidan.com
  • 지원표/실사 자료: a11ysupport.io의 CSS generated content alt 테스트 스냅샷. a11ysupport.io
  • 맥락 글: CSS가 접근성 트리에 미치는 영향(“The Other C in CSS”). CSS-Tricks

10) 최종 요령(한 장 요약)

  • 의미는 HTML, CSS 생성은 장식/중복에만. sarasoueidan.com
  • CSS 생성물은 accName에 섞인다 → 장식은 / "", 의미는 / "설명". W3C+1
  • NVDA+Chrome 등 조합별 발화 편차가 있으니 꼭 교차 테스트. sarasoueidan.com+1
  • 번역/리더 모드/검색성 이슈 때문에 장기적으로 HTML 중심 구조 유지. sarasoueidan.com

원문 - CSS to speech: alternative text for CSS-generated content

profile
okorion's Tech Study Blog.

0개의 댓글