diff --git a/.jules/palette.md b/.jules/palette.md index ea004e2d2..154dda683 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -81,3 +81,7 @@ ## 2026-08-12 - Skip to Content Accessibility **Learning:** Screen reader and keyboard-only users experience significant friction when forced to navigate through repetitive header controls on every page load. **Action:** Keep a visible-on-focus skip link as the first interactive element, target a programmatically focusable main container, and give the focused link a high-contrast outline. + +## 2026-08-15 - 파일 입력(File Input)을 위한 접근성 프록시 패턴 (Accessibility Proxy for File Input) +**Learning:** 네이티브 `` 요소는 시각적으로 스타일링하기 매우 까다롭고 종종 디자인 시스템의 일관성을 깬다. 단순히 `display: none`을 사용하면 접근성 트리에서 제거되어 스크린 리더와 키보드 사용자가 접근할 수 없다. `aria-hidden="true"`는 파일 입력 요소를 접근성 트리에서 제거한다. +**Action:** 네이티브 입력을 시각적으로 숨길 때(예: `class="sr-only" tabindex="-1" aria-hidden="true"`), 항상 클릭을 숨겨진 입력에 전달하는 완전히 접근 가능한 프록시 ` +

@@ -388,6 +389,14 @@

Dashboard

if(isDeployBlocking(f)!==expect) console.error('selftest FAIL', f, 'expected', expect); } })(); + +(function() { + const btn = document.getElementById('upload-proxy'); + const inp = document.getElementById('file'); + if (btn && inp) { + btn.addEventListener('click', () => inp.click()); + } +})(); \ No newline at end of file diff --git a/tests/test_dashboard_core.py b/tests/test_dashboard_core.py index 75a5809f8..47523270f 100644 --- a/tests/test_dashboard_core.py +++ b/tests/test_dashboard_core.py @@ -77,8 +77,6 @@ def test_dashboard_rows_are_keyboard_accessible(): assert 'tabindex="0" role="button"' in html assert 'title="View details for finding"' in html assert "tbody tr:focus-visible" in html - assert "aria-label=\"Upload findings file\"" in html - assert "aria-label=\"Search findings\"" in html assert "aria-label=\"Filter by severity\"" in html assert "tr.addEventListener('keydown'" in html assert "e.key === 'Enter' || e.key === ' '" in html @@ -302,3 +300,70 @@ def test_dashboard_search_escape_clears_input(): assert "e.key === 'Escape'" in html assert "query = '';" in html assert "render();" in html + + +def test_dashboard_file_upload_proxy_accessibility(): + """File upload proxy button must be accessible and correctly linked to the hidden native input.""" + html = dashboard_index_path().read_text(encoding="utf-8") + + assert '' in html + assert 'class="sr-only"' in html and 'tabindex="-1"' in html and 'aria-hidden="true"' in html + assert "btn.addEventListener('click', () => inp.click())" in html + +def test_file_upload_proxy_accessibility_contract_is_documented_accurately(): + """The journal must accurately document the accessibility hiding technique.""" + from pathlib import Path + journal_path = Path(".jules/palette.md") + assert journal_path.is_file() + content = journal_path.read_text(encoding="utf-8") + assert '`aria-hidden="true"`는 파일 입력 요소를 접근성 트리에서 제거' in content + + +def test_dashboard_file_upload_proxy_accessibility(): + """File upload proxy button must be accessible and correctly linked to the hidden native input.""" + html = dashboard_index_path().read_text(encoding="utf-8") + + assert '' in html + assert 'class="sr-only"' in html and 'tabindex="-1"' in html and 'aria-hidden="true"' in html + assert "btn.addEventListener('click', () => inp.click())" in html + +def test_file_upload_proxy_accessibility_contract_is_documented_accurately(): + """The journal must accurately document the accessibility hiding technique.""" + from pathlib import Path + journal_path = Path(".jules/palette.md") + assert journal_path.is_file() + content = journal_path.read_text(encoding="utf-8") + assert '`aria-hidden="true"`는 파일 입력 요소를 접근성 트리에서 제거' in content + + +def test_dashboard_file_upload_proxy_accessibility(): + """File upload proxy button must be accessible and correctly linked to the hidden native input.""" + html = dashboard_index_path().read_text(encoding="utf-8") + + assert '' in html + assert 'class="sr-only"' in html and 'tabindex="-1"' in html and 'aria-hidden="true"' in html + assert "btn.addEventListener('click', () => inp.click())" in html + +def test_file_upload_proxy_accessibility_contract_is_documented_accurately(): + """The journal must accurately document the accessibility hiding technique.""" + from pathlib import Path + journal_path = Path(".jules/palette.md") + assert journal_path.is_file() + content = journal_path.read_text(encoding="utf-8") + assert '`aria-hidden="true"`는 파일 입력 요소를 접근성 트리에서 제거' in content + +def test_dashboard_file_upload_proxy_accessibility(): + """File upload proxy button must be accessible and correctly linked to the hidden native input.""" + html = dashboard_index_path().read_text(encoding="utf-8") + + assert '' in html + assert 'class="sr-only"' in html and 'tabindex="-1"' in html and 'aria-hidden="true"' in html + assert "btn.addEventListener('click', () => inp.click())" in html + +def test_file_upload_proxy_accessibility_contract_is_documented_accurately(): + """The journal must accurately document the accessibility hiding technique.""" + from pathlib import Path + journal_path = Path(".jules/palette.md") + assert journal_path.is_file() + content = journal_path.read_text(encoding="utf-8") + assert '`aria-hidden="true"`는 파일 입력 요소를 접근성 트리에서 제거' in content