Files
HMDesign/docs/reports/accessibility-residual-2026-10-10.md
T

11 KiB
Raw Blame History

HMDesign 접근성 잔여 검증 — 2026-10-10

RESULT: PARTIAL. 재현된 ARIA·접근성 이름 결함을 최소 수정했고 해당 동작의 선택 검증은 통과했다. 확정 색의 대비 부족, 실제 스크린리더·실기기 검증과 자동 도구의 일부 미확정 진단은 남아 있다. 전체 WCAG 적합성 승인으로 해석하지 않는다.

기준 Local/지정 GitHub main SHA: 8817135f504b836a8d1566f3672ed50b8dd392b9. 시작 Working Tree는 CLEAN. 범위 BOTH, Runtime React. 기존 화면 생성 실험·Claude·하위 에이전트 호출 0, 승인 장부 10/12·잔여 2 보존. 공식 Gitea NOT ACCESSED / NOT MODIFIED.

실제 결함과 최소 수정

대상 재현·원인 변경·확인
Combobox Enter로 연 Trigger는 aria-haspopup=listbox지만 controls 대상은 실제 role=dialog. 내부 cmdk 검색 입력은 별도 listbox를 제어한다. Trigger 기본값만 dialog로 수정. 기존 ID/이름/disabled/검색/값/API·연결형 스타일 보존. 네 폭에서 실제 키보드 선택·Escape·focus 복귀·expanded/controls 역할 정합 확인.
GNBUser / LNBProfile 화면은 이름+역할을 표시하지만 기본 이름은 이름+사용자 메뉴만 포함. 이름과 설명 DOM 텍스트 경계도 붙어 한국어 Label-in-Name 검사에 실패. 기본 이름에 description 포함, 설명이 있을 때 이름 뒤의 의미상 공백 추가. 명시적 aria-label/labelledby 우선권 보존. 두 버튼의 실제 이름·키보드 메뉴/Arrow/Escape/focus 복귀 및 axe Label-in-Name 통과. GNB 138.64×53, LNB 140.64×48로 경계 보완 전후 크기 일치.

구현·Combobox MD·GNB MD·LNB MD·관련 All Story의 설명을 맞췄다. AI 화면 계약에 팝업 실제 role과 명시적 접근성 이름의 소비 책임만 추가했다. AGENTS.md의 기존 Label/ID/키보드/모바일 규칙은 충분해 수정하지 않았다. 디자인·Token·Typography·Spacing·Radius·공개 export/API·Story ID·D-01/D-02·퍼블리셔 버튼 배치/Sheet radius 변경 없음.

실행 검증과 한계

  • 신규 실제 TSX 렌더 회귀 3개 PASS. 이름 경계 보완 후 영향받는 GNB/LNB 2개만 재검사 PASS; Combobox PASS는 재실행하지 않음. 기존 publisher C5 반복 popup/ID 테스트 1개 PASS, 나머지 8개 선택 skip.
  • Combobox 390/768/1280/1440: Tab/Shift+Tab, Enter/Space 열기, 검색·ArrowDown·Enter 선택, Escape·focus 복귀, 팝업 bounds, 페이지 가로 overflow, 중복 ID 확인 PASS. 브라우저 CDP AX tree로 dialog 이름을 별도 확인. Playwright 일부 popup snapshot에서 이름을 생략하는 것과 Chrome AX tree의 실제 이름을 구분한다.
  • 최종 static Storybook 빌드에서 위 네 폭과 사용자 메뉴 두 경로의 6개 선택 회귀가 모두 PASS, pageerror 0. Button의 Enter·Space가 각각 실제 native click을 발생시키는 것도 별도 확인했다. 390px Combobox와 Drawer PNG를 시각 확인했다.
  • Dialog/AlertDialog/Sheet/Drawer: 1280에서 실제 열기·초기 focus·Tab 8회 순환·Escape·trigger 복귀 PASS. Sheet/Drawer는 열린 상태로 네 폭 변경 시 내부 focus와 페이지 overflow 유지, 모바일 버튼 크기/배치 측정 PASS. Drawer 모바일 시각 실행 위/취소 아래와 기존 DOM 취소→실행 순서 보존. 실제 손가락 drag·스크롤 충돌은 NOT VERIFIED.
  • Button의 키보드 진입·disabled Tab 제외, Input readonly 실제 키 입력 보호, Accordion collapsible Enter/Space·Arrow/Home/End, DataTable 페이지 크기 Label/키보드 선택·정렬 aria-sort, GNB/LNB 실제 Tab 50회에서 숨긴 배경 focus 잔류 없음을 선택 확인했다. 검사한 경로에 한정한다.
  • 모바일 GNB/LNB 단일 fixture 390/768px에서 Enter 열기·Tab 8회 modal 내부 탐색·Escape·trigger focus 복귀·페이지 overflow 확인 PASS. 두 메뉴 trigger는 32×32px. 전체 대상 간격·실제 터치·44px 강화 기준 적합성을 인증하지 않는다.
  • Table 자동 scrollable-region-focusable 경고는 Chrome 154에서 실제 Tab 진입·ArrowRight 0→40px 스크롤이 가능했다. 수정하지 않았다. 다른 브라우저의 스크롤 키보드 접근성은 NOT VERIFIED.
  • Select는 실제 Space/Home/End/Tab/escape·focus 복귀가 정상이며 숨긴 배경 focus 진단은 원본 보존했다. 독립 listbox 이름과 실제 보조기술의 Trigger 관계 안내는 NOT VERIFIED. 경고를 삭제하거나 SR PASS로 바꾸지 않았다.
  • 실제 스크린리더 발화, 음성 조작, 실기기 터치·safe area·확대/텍스트 확대 전체, 모든 Theme/상태/브라우저 조합은 NOT VERIFIED. NVDA 실행 세션과 일반 설치 위치의 실행 파일은 확인되지 않았다. 접근성 트리와 자동 결과를 스크린리더 검증으로 대체하지 않았다.

자동 검사는 axe-core 4.14.0의 관련 WCAG 2/2.1/2.2 A/AA 태그를 사용했다. 판정 근거는 WCAG 2.2 검사 항목, Combobox 팝업 역할, Label in Name, 비텍스트 대비, 최소 터치 대상을 구분해 적용했다. Disabled 대비 예외는 활성 placeholder/오류 문구/버튼에 적용하지 않는다. 24px 미만 대상은 연결 Label·간격 등 예외 판정이 필요하며 전체 모바일 AA PASS로 확대하지 않는다.

색상 대비 — 디자인 변경 보류

실제 적용 대상 대비·판정
Combobox/DatePicker 빈 값 흰 Trigger 이전 5.60:1 증거와 현재 mapping 동일 범위 REUSED PASS
일반 Input 값 / Field Label 16.67:1 / Beige 배경 4.93:1, 확인 범위 PASS
Input 활성 placeholder / Select 활성 placeholder 약 2.85:1, FAIL. 확정 mapping/색 변경은 DEFERRED
기존 Input 오류 문구 Red/Beige 4.38:1, 기존 소스·배경 동일 FAIL 증거 보존; 변경 DEFERRED
Solid Modify Button White/#cc5200 4.40:1, FAIL; 상태/확정색 변경 DEFERRED
Destructive Outline hover/pressed #d32f2f/#fbeeee 약 4.40:1, FAIL; 변경 DEFERRED
Primary Brand 기본 gradient 실제 스크린샷의 글자 없는 padding 세로 구간 합성 픽셀 기준 최소 5.68:1. 해당 gradient/샘플 범위 PASS, 다른 gradient/상태는 NOT VERIFIED
Table selected·selected+hover 실제 20% 반투명 면과 White 합성 배경의 본문 15.61:1, 해당 상태 PASS
Focus 실제 키보드 진입 확인. Input 최종 focus border는 내부 White 6.77:1·외부 Beige 5.96:1, Primary Solid 버튼 focus border는 외부 Beige 14.67:1. 해당 표시 PASS; 전체 아이콘/상태/ring/gradient의 대비는 NOT VERIFIED

색 부족을 숨기기 위해 Token이나 확정 Component Color를 변경하지 않았다. Gradient는 투명 backgroundColor만으로 대비를 계산하지 않고 실제 렌더된 픽셀을 사용했다. 모든 픽셀·모든 Theme의 대비 인증은 아니다.

재사용과 중복 검증

  • 이전 8817135 보고서의 Guard100/100과 로그, 이번 작업에서 미변경인 Guard/테스트/의존성 소스를 대조해 REUSED PASS. 새 전체 회귀를 실행한 것으로 세지 않는다.
  • 기존 Checkbox/Radio/Toggle/Tabs·보호 상태·Label/ID 증거는 해당 구현·CSS·테스트 본문이 동일한 범위만 재사용. 수정된 Combobox/GNB/LNB는 새 검증으로 대체.
  • 별도 실험 폴더 T01/T02/T06의 source fingerprint와 원래 Runtime source 연결·네 폭 PASS가 일치. T02 ID 한 줄, T06 숨김 알림 위치/내부 스크롤 수정 보존. 새 계약의 whole-screen Guard/결합 인증으로 확대하지 않음.
  • 전체 292 Story Runtime, 전체 Component/Guard/문서 링크, 외부 모델 화면 재생성, 의존성 재설치 반복 없음. 초기 테스트의 연결/locator/계약 오류와 실패 로그는 별도 보존하고 필요한 항목만 재개했다.
  • 최종 통합에서 root/UI/CENTER Typecheck, package 선언 build, root Vite 및 Storybook build PASS. 이름 경계 보완으로 소스가 변경된 뒤 영향받는 UI/산출물과 테스트만 다시 검증했다. 기존 500kB chunk·Storybook bundler 성능 경고는 남아 있다.
  • Storybook PARTIAL Guard의 FAIL은 0. 초기 세 Story 검사 후 이름 경계 수정의 영향 범위인 GNB/LNB만 PARTIAL 재검사했다. 최종 source/contract fingerprint는 현재 파일과 일치한다. 원본 status와 staticStatus는 NOT VERIFIED이며 native fixture·Reference slot·미해석 Story style의 미검증을 유지한다. styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT NOT VERIFIED / FINAL NOT VERIFIED다. 별도 브라우저 동작 결과를 Guard의 스타일 인증에 연결하지 않았으므로 결합 styleVerification은 생성하지 않았다. 컴포넌트 자체의 WCAG 검증을 소비 화면 Guard가 수행한 것으로 해석하지 않는다.
  • 변경·연결 문서 10개의 내부 링크 27개 PASS. 최종 index의 292 Story ID가 기존 inventory와 모두 일치하고 공개 export·CSS·Theme 소스가 작업 기준과 동일하다.

선택 회귀 실행 방법

node --test scripts/accessibility-regression.test.mjs
# 기존 C5 조합에 영향이 있는 경우만:
node --test --test-name-pattern C5 scripts/publisher-components.test.mjs

실제 브라우저 회귀는 이미 실행 중인 로컬 Storybook(개발 또는 최종 static)을 사용한다. Playwright/Chrome을 설치된 QA 환경에서 제공한다. 이 스크립트는 패키지 설치나 모델 호출을 수행하지 않는다.

$env:HM_PLAYWRIGHT_DIR='<설치된 playwright 패키지 디렉터리>'
$env:HM_CHROME_PATH='<설치된 Chrome 실행 파일>'
$env:HM_A11Y_URL='http://127.0.0.1:6024'
$env:HM_A11Y_OUTPUT='_local/accessibility-runtime'
# 선택: 설치된 axe.min.js. 없으면 해당 자동 검사는 NOT VERIFIED.
$env:HM_AXE_PATH='<설치된 axe-core/axe.min.js>'
node scripts/verify-accessibility-runtime.mjs
# 사용자 메뉴만 변경됐을 때:
node scripts/verify-accessibility-runtime.mjs --profiles-only

이 검사는 named checks만 보고한다. 실제 발화·손가락 터치·전체 앱의 접근성을 인증하지 않는다. 로컬 상세 JSON·로그·PNG·재사용/보존 manifest·Git 결과는 _local/a11y-residual-20261010/에 보관한다. 원본 과거 자료는 수정하지 않는다. 최종 Commit/Remote SHA와 KST 완료 시간은 작업 최종 보고에 기록한다.