Skip to content

fix(a11y): 머리글 메뉴·분석 이력 표를 키보드로 조작한다 — role="menu" 키 계약 · 언어 변경 포커스 복귀 · 행 링크·정렬 버튼 (#1746 K15) - #1776

Merged
xzawed merged 1 commit into
mainfrom
fix/k15-keyboard-operability
Oct 28, 2026

Conversation

@xzawed

@xzawed xzawed commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

pytest tests/unit → 8632 passed / 4 skipped

무엇을 고쳤나 (#1746 K15 — 키보드 조작성)

마우스 없이 «조작» 이 안 되던 네 자리. 포커스 «표시» 스윕은 CDP 로 :focus 를 강제해 칠해진 모양만 재서, 포커스가 그 자리에 «닿는지»·키가 무엇을 하는지는 아무도 재지 않았다.

원장 자리 수정 전 수정 후
P2-67 테마·언어 메뉴(role="menu") Enter/Space 만 — ↓/↑ 로 안 열리고 항목 사이 이동·Escape 없음, 항목이 tabindex="0" APG «메뉴 버튼» 계약: ↓ 첫 항목 · ↑ 마지막 항목 · Enter/Space(키보드 클릭, detail 0) 첫 항목 · ↑↓ 순환 · Home/End · Escape 닫고 버튼으로 · Tab 닫고 지나감. 마우스로 열면 포커스를 옮기지 않는다. 항목 tabindex="-1" · 메뉴 aria-labelledby · 버튼 aria-controls
P2-68 키보드로 언어 변경 body 가 outerHTML 로 바뀌며 포커스가 문서 처음으로 고르면 먼저 언어 버튼에 포커스 → htmx 가 교체 뒤 같은 id(#langToggle)로 되돌린다(테마 팔의 toggleBtn.focus() 와 짝)
P1-59 리포 상세 분석 이력 행 <tr onclick> 만 — Tab 으로 닿지 않음 첫 칸(날짜·커밋 메시지)이 진짜 <a href> · 행 클릭은 tbody 위임 처리기(링크·컨트롤 위 클릭은 제 동작)
P1-59 형제 같은 표의 정렬 머리글 th 의 click 처리기뿐 th 안의 <button class="sort-btn"> — click 이 th 로 올라가 기존 처리기가 받는다. 상태는 그대로 th[aria-sort], 글리프는 aria-hidden
P1-60 위 처리기들의 가드 keydown 처리기를 지워도 스위트 전건 초록 e2e/test_keyboard_operability.py 4건 — 실제 키를 누르고 document.activeElement 를 본다
P2-69 24px 타깃 스윕 셀렉터 [role=menuitem] 이 menuitemradio 를 못 맞혀 머리글 항목 7개가 밖 [role^=menuitem] + 두 메뉴를 진짜 토글로 열고 재기 + «잰 항목 ≥ 7» 되짚기

첫 칸에 링크를 둔 이유: 커밋 열은 ≤480px 에서 숨는다(.tbl td:nth-child(2) { display: none }) — 거기 두면 좁은 화면 키보드·보조기술 사용자가 링크를 잃는다.

증거

Grok claim-review

  • session: aabf8c80-b28c-4443-962c-1e04f7e10c42
  • claim: 메뉴 버튼 쪽 — ↓/↑ 는 첫/마지막 항목으로 열고, Enter·Space 는 한 번만 열어 첫 항목, 마우스 클릭은 포커스를 옮기지 않으며, 열린 채 버튼에서 Escape 는 닫는다
  • verdict: SURVIVES
  • session: 2c2eb8a4-3c95-4196-aae3-49f3979f5b30
  • claim: 항목 쪽 — ↑↓ 순환·Home/End, Enter·Space 는 한 번 고르고 버튼으로, Escape 는 닫고 버튼으로, Tab 은 닫고 메뉴 밖 요소로
  • verdict: SURVIVES
  • session: c2bc0e39-6bd3-4454-a00c-049dbc117f26
  • claim: 키보드로 언어를 고르면 htmx 의 body outerHTML 교체 뒤 포커스가 새 #langToggle 에 있다
  • verdict: SURVIVES

진행: 처음 묶어 물은 질문(주장 7개, f2d38ef0)과 htmx 질문이 300 s 를 넘겼다 → 키보드 계약을 버튼/항목 두 조각으로 쪼갰고 둘 다 다시 300 s 초과 → 지침대로 같은 세션 600 s 재개, 셋 다 1턴에 끝났다. 판정은 질문에 넣은 코드 발췌 범위만이다 — 실제 브라우저 동작은 위 e2e 4건이 잰다.

사람이 눈으로 볼 것

  1. 리포 상세 분석 이력 표 — 첫 칸(날짜·커밋 메시지)이 링크가 됐지만 글자 색·모양은 그대로여야 한다(전역 a 의 accent 색을 끊었다). 행 hover 강조선·배경도 그대로. Tab 으로 행 링크·정렬 머리글에 포커스 링이 보이는지.
  2. 정렬 머리글 — 대문자·자간·색이 이전과 같은지. 좁은 화면(375px)에서 머리글 높이가 몇 px 늘 수 있다(버튼 최소 24px, 2.5.8).
  3. 머리글 테마·언어 메뉴 — 마우스로 쓸 때는 이전과 똑같아야 한다(열어도 포커스가 버튼에 남는다). 키보드: 버튼에서 ↓ → 항목 이동 → Enter.

스스로 판단한 것

  • 원장 P1-59 는 «행» 만 적었는데, 같은 표의 정렬 머리글도 같은 결함(마우스 전용)이라 함께 고쳤다. 템플릿 전체 cursor:pointer 선택자를 훑어 나머지는 전부 진짜 컨트롤(button·a·select·summary)임을 확인했다.
  • 언어 변경 포커스 복귀는 htmx 1.9.12 의 «교체 직전 포커스 요소의 id 로 새 body 에서 다시 포커스» 동작에 기댄다(vendored 원문 var e=document.activeElement … if(t.elt&&!se(t.elt)&&ee(t.elt,"id")){… i.focus(a)} 확인). settle 의 [autofocus] 경로는 앱에 autofocus 가 0건이라 끼어들지 않는다. 회귀하면 e2e 가 red.
  • role="tab" 이슈 탭(분석 상세)도 APG 상 화살표 이동을 «기대» 하지만, 버튼이라 Tab+Enter 로는 조작된다 — 이번 범위(«약속한 계약이 아예 없음») 밖으로 두고 원장에 후보로 적는다.

문서

docs/workflow/ui-i18n.md «화면» 에 10번 한 줄(누르는 것은 button·a[href] / 메뉴 키 계약 앵커). +46자 · 줄 수 동일 — 뺀 것: 8번 «Chromium 이 열로 센다» · 9번 «칩 면이라 catppuccin 에서 그 위 등급 글자가 AA 미달이다»(둘 다 «왜» 문구) · 설정 토글 5번 «(선례: ai_review_enabled 는 프리셋 밖)».

K15 남은 것 (이 PR 밖)

P1-57(랜딩 모션 감소) · P1-58(모션 감소의 유일한 관측이 부분문자열) · P1-61 · P2-57 · P2-59 · P2-60 · P2-64 · P2-65 · P2-66 · P2-70 · P2-74 — 원장 #1746 에 그대로 둔다.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R4jYRzichV75XnYLSEFguX

@xzawed

xzawed commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

⚠️ AI 리뷰가 완전 검증되지 않음 — 기본값이 적용됐습니다(비활성/API 키 없음/빈 diff). AI 배점은 측정값이 아니므로 총점을 부분 점수로 보세요.
ℹ️ 범용 검사기만 본 언어 포함: html — 이 배포에서 해당 언어의 전담 분석기가 하나도 실행되지 않아, 다언어 범용 분석기 하나에만 기대었습니다. 그 규칙 밀도는 언어마다 편차가 큽니다 — 여기서의 만점은 "깨끗함" 이 아니라 "거의 검사 안 됨" 입니다 — AI 리뷰와 수동 검토로 보완하세요.

🟡 SCAManager 분석 결과

총점: 62/100 (등급 C)

점수 상세

항목 점수 만점
커밋 메시지 13 15
코드 품질 0 25
보안 18 20
구현 방향성 (AI) 21 25
테스트 10 15

정적 분석 이슈 (상위 10건)

  • [pylint] Variable name "_TARGET_AUDIT_JS" doesn't conform to snake_case naming style (e2e/test_theme_mobile_guards.py:1832)
  • [pylint] Variable name "_TARGET_AUDIT_JS" doesn't conform to snake_case naming style (e2e/test_theme_mobile_guards.py:1872)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2216)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2323)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2646)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:2648)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:2940)
  • [pylint] Import outside toplevel (itertools.permutations) (e2e/test_theme_mobile_guards.py:2949)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:3052)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:3339)

@codecov

codecov Bot commented Oct 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…귀 · 행 링크·정렬 버튼 (#1746 K15)

- 테마·언어 메뉴: APG 메뉴 버튼 계약(↓/↑ 열기 · ↑↓ 순환 · Home/End · Escape · Tab) — 마우스로 열면 포커스 유지
- 키보드로 언어를 바꾸면 body 교체 뒤에도 포커스가 언어 버튼에 남는다
- 리포 상세 분석 이력: 첫 칸에 진짜 링크, 정렬 머리글은 th 안의 버튼
- e2e/test_keyboard_operability.py 4건(수정 전 3 red) · 24px 스윕이 menuitemradio 를 보게 함
- e2e 431 → 435

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4jYRzichV75XnYLSEFguX
@xzawed
xzawed force-pushed the fix/k15-keyboard-operability branch from 3e75bd5 to d148657 Compare October 28, 2026 23:07
@xzawed

xzawed commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

⚠️ AI 리뷰가 완전 검증되지 않음 — 기본값이 적용됐습니다(비활성/API 키 없음/빈 diff). AI 배점은 측정값이 아니므로 총점을 부분 점수로 보세요.
ℹ️ 범용 검사기만 본 언어 포함: html — 이 배포에서 해당 언어의 전담 분석기가 하나도 실행되지 않아, 다언어 범용 분석기 하나에만 기대었습니다. 그 규칙 밀도는 언어마다 편차가 큽니다 — 여기서의 만점은 "깨끗함" 이 아니라 "거의 검사 안 됨" 입니다 — AI 리뷰와 수동 검토로 보완하세요.

🟡 SCAManager 분석 결과

총점: 62/100 (등급 C)

점수 상세

항목 점수 만점
커밋 메시지 13 15
코드 품질 0 25
보안 18 20
구현 방향성 (AI) 21 25
테스트 10 15

정적 분석 이슈 (상위 10건)

  • [pylint] Variable name "_TARGET_AUDIT_JS" doesn't conform to snake_case naming style (e2e/test_theme_mobile_guards.py:1832)
  • [pylint] Variable name "_TARGET_AUDIT_JS" doesn't conform to snake_case naming style (e2e/test_theme_mobile_guards.py:1872)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2216)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2323)
  • [pylint] Import outside toplevel (urllib.parse.quote) (e2e/test_theme_mobile_guards.py:2646)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:2648)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:2940)
  • [pylint] Import outside toplevel (itertools.permutations) (e2e/test_theme_mobile_guards.py:2949)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:3052)
  • [pylint] Import outside toplevel (src.i18n.loader.get_text) (e2e/test_theme_mobile_guards.py:3339)

@sonarqubecloud

Copy link
Copy Markdown

@xzawed
xzawed merged commit fc6c0f8 into main Oct 28, 2026
15 checks passed
@xzawed
xzawed deleted the fix/k15-keyboard-operability branch October 28, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant