Skip to content
Draft
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@
## 2026-08-17 - 브라우저 번역과 화면 판독기의 호환성을 위한 텍스트 처리
**Learning:** `aria-label` 속성으로 지정된 화면 판독기용 대체 텍스트는 Chrome Translate 등 브라우저 번역 도구에 의해 번역되지 않는 경우가 많습니다. 이로 인해 문서 언어가 변환되어도 스크린 리더에서는 원본 언어(예: 영어)로 읽혀 다국어 접근성이 저하됩니다.
**Action:** 화면 판독기를 위한 숨겨진 설명 텍스트를 제공할 때 `aria-label` 대신 CSS `.visually-hidden` 클래스를 적용한 `<span>` 요소를 사용하여, 브라우저가 일반 텍스트로 인식하고 번역할 수 있도록 하여 다국어 접근성 호환성을 확보하십시오.

## 2024-09-23 - 숨겨진 텍스트로 인한 CSS 스타일링 누수 방지
**Learning:** 상호작용 요소(예: 링크) 안에서 화면 판독기를 위한 `.visually-hidden` 텍스트를 사용할 때, `span:last-child`와 같은 구조적 선택자를 사용하면 시각적으로 보이지 않는 요소가 마지막 자식으로 인식되어 의도한 요소(텍스트)에 호버 스타일(예: 밑줄)이 적용되지 않거나 숨겨진 요소로 스타일이 누수되는 문제가 발생합니다.
**Action:** 숨겨진 텍스트가 포함된 요소의 상태(hover, focus, active 등)를 스타일링할 때는 구조적 선택자 대신 텍스트 콘텐츠 자체에 명시적인 클래스(예: `.entry-name`)를 부여하고 해당 클래스를 타겟팅하여 스타일 충돌과 누수를 방지하십시오.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ All notable changes to this project are documented in this file.

### Fixed

- 시각적으로 숨겨진 화면 판독기용 텍스트에 호버 스타일이 누수되어 실제 파일 이름에 밑줄이 표시되지 않던 문제 수정.

- Generate the inline-style Content Security Policy SHA-256 source expression
from the exact normalized UTF-8 stylesheet bytes emitted into each generated
`index.html` file, preventing template whitespace from invalidating the policy.
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ a:hover, a:focus-visible {
outline: 2px solid #0969da;
outline-offset: -2px;
}
a:hover span:last-child, a:focus-visible span:last-child {
a:hover .entry-name, a:focus-visible .entry-name {
text-decoration: underline;
}
@media (prefers-reduced-motion: reduce) {
Expand Down Expand Up @@ -429,7 +429,7 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
<h1>${directoryName.escapeHtml()}</h1>
<nav aria-label="디렉토리 목록">
<ul role="list">
<li><a class="dir-link" href="./.." title="상위 디렉토리로 이동"><span class="icon" aria-hidden="true">&#x21B0;</span> <span aria-hidden="true">..</span> <span class="visually-hidden">상위 디렉토리로 이동</span></a></li>
<li><a class="dir-link" href="./.." title="상위 디렉토리로 이동"><span class="icon" aria-hidden="true">&#x21B0;</span> <span class="entry-name" aria-hidden="true">..</span> <span class="visually-hidden">상위 디렉토리로 이동</span></a></li>
Comment thread
seonghobae marked this conversation as resolved.
"""

val index_middle = fun():String{
Expand Down Expand Up @@ -460,7 +460,7 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml()
val typeLabel = if (isLinkedDirectory) { "디렉토리" } else { "파일" }
val icon = if (isLinkedDirectory) { "&#128193;" } else { "&#128196;" }
l.append(""" <li><a class="dir-link" href="${encodedHref}" title="${ariaLabel}"><span class="icon" aria-hidden="true">${icon}</span> <span>${fileName.escapeHtml()}</span> <span class="visually-hidden">${typeLabel}</span></a></li>""")
l.append(""" <li><a class="dir-link" href="${encodedHref}" title="${ariaLabel}"><span class="icon" aria-hidden="true">${icon}</span> <span class="entry-name">${fileName.escapeHtml()}</span> <span class="visually-hidden">${typeLabel}</span></a></li>""")
l.append('\n')
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/html4tree/GeneratedIndexReadabilityTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class GeneratedIndexReadabilityTest {
)

val generatedHtml = generatedHtml()
val parentIndex = generatedHtml.indexOf("<span aria-hidden=\"true\">..</span>")
val parentIndex = generatedHtml.indexOf("<span class=\"entry-name\" aria-hidden=\"true\">..</span>")
val firstIndex = generatedHtml.indexOf("alpha.txt")
val middleIndex = generatedHtml.indexOf("middle.txt")
val lastIndex = generatedHtml.indexOf("zulu.txt")
Expand Down Expand Up @@ -147,7 +147,7 @@ class GeneratedIndexReadabilityTest {
assertTrue(
style.contains(
"""
a:hover span:last-child, a:focus-visible span:last-child {
a:hover .entry-name, a:focus-visible .entry-name {
text-decoration: underline;
}
""".trimIndent()
Expand Down
Loading