Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8740795
🎨 Palette: [UX improvement] Add :active state to links
seonghobae Sep 2, 2026
c92d138
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 2, 2026
76d6022
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 2, 2026
d90be1c
test(ui): target visible link text for active feedback
seonghobae Sep 2, 2026
3e4f3bb
fix(ui): underline the visible entry name
seonghobae Sep 2, 2026
8ecb57a
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 3, 2026
25a4d26
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 3, 2026
2dda26f
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 3, 2026
d5e21ba
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 4, 2026
92eb0bc
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 4, 2026
1f63c9e
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 5, 2026
dbc9917
fix(ui): restore semantic filename interaction target
seonghobae Sep 5, 2026
31ddf8c
docs(palette): restore protected interaction guidance
seonghobae Sep 5, 2026
b811d47
merge(ui): restack filename interaction repair onto protected master
seonghobae Sep 5, 2026
a5516fc
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 5, 2026
8d15f88
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 5, 2026
cf5b329
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 5, 2026
2c8f78d
🎨 Palette: [UX 개선] 링크에 :active 상태 추가
seonghobae Sep 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ a {
border-radius: 4px;
transition: background-color 0.2s ease, outline-color 0.2s ease;
}
a:hover, a:focus-visible {
a:hover, a:focus-visible, a:active {
background-color: #f6f8fa;
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, a:active .entry-name {
text-decoration: underline;
}
@media (prefers-reduced-motion: reduce) {
Expand Down Expand Up @@ -94,7 +94,7 @@ li + li {
a {
color: #58a6ff;
}
a:hover, a:focus-visible {
a:hover, a:focus-visible, a:active {
background-color: #161b22;
outline-color: #58a6ff;
}
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>
"""

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
24 changes: 19 additions & 5 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 @@ -133,11 +133,24 @@ class GeneratedIndexReadabilityTest {
}

@Test
fun hoverAndKeyboardFocusUnderlineOnlyLinkText() {
process_dir(temporaryDirectory, setOf("index.html"), emptyArray())
fun interactiveStatesUnderlineVisibleFilenameOnly() {
val linkedFile = File(temporaryDirectory, "visible-name.txt").apply { writeText("target") }
process_dir(temporaryDirectory, setOf("index.html"), arrayOf(linkedFile))

val html = generatedHtml()
val style = emittedStyle()
val completeTargetRule = Regex("""a:hover, a:focus-visible \{([\s\S]*?)\}""")
val linkedRow = Regex("""<a class="dir-link"[^>]*>([\s\S]*?visible-name\.txt[\s\S]*?)</a>""")
.find(html)
?.groupValues
?.get(1)
assertNotNull(linkedRow)
assertTrue(linkedRow.contains("<span class=\"entry-name\">visible-name.txt</span>"))
assertTrue(linkedRow.contains("<span class=\"icon\" aria-hidden=\"true\">"))
assertTrue(linkedRow.contains("<span class=\"visually-hidden\">파일</span>"))
assertFalse(linkedRow.contains("class=\"icon entry-name\""))
assertFalse(linkedRow.contains("class=\"visually-hidden entry-name\""))

val completeTargetRule = Regex("""a:hover, a:focus-visible, a:active \{([\s\S]*?)\}""")
.find(style)
?.groupValues
?.get(1)
Expand All @@ -147,12 +160,13 @@ 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, a:active .entry-name {
text-decoration: underline;
}
""".trimIndent()
Comment on lines 160 to 166

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Regression test misses rendered filenames

The test generates no filename link and requires the positional selector. It cannot detect whether interactive underlining reaches rendered filenames.

(Refers to this code)

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

)
)
assertFalse(style.contains("span:last-child"))
assertTrue(style.contains("@media (prefers-reduced-motion: reduce)"))
}

Expand Down
Loading