접근성
@lh-ui/react는 패밀리의 다른 라이브러리와 동일한 접근성 계약을 공유합니다: 시맨틱 태그, ARIA 속성, 키보드 인터랙션이 모두 @lh-ui/share/core에 정의되어 있으며, 각 플랫폼 구현이 일치합니다. 이 라이브러리는 추가로 React 생태계의 일반적인 a11y 함정이 발생하지 않도록 보장합니다.
시맨틱과 ARIA
- **
LhSwitch**는 시각적으로 위장한 checkbox가 아닌<div role="switch" aria-checked>를 렌더링합니다 LhSelect트리거는role="combobox", 펼쳐진 패널은role="listbox", 옵션은role="option"이며aria-activedescendant로 키보드 하이라이트를 동기화합니다- **
LhDialog**는role="dialog" aria-modal이고, 제목에aria-label폴백과aria-labelledby정확한 연결을 동시에 제공합니다 - **
LhTooltip**는aria-describedby로 힌트를 트리거에 연결합니다 - **
LhTag**는 클릭 가능할 때role="button"으로 승격되고 Enter / Space에 반응합니다 - **
LhFormItem**는 검증 실패 시role="alert"에러 힌트를 렌더링하고aria-invalid를 설정합니다
키보드 인터랙션
| 컴포넌트 | 키 |
|---|---|
LhButton / LhTag | Enter / Space로 클릭 트리거 |
LhSwitch | Enter / Space로 전환 |
LhSelect | ArrowUp / ArrowDown 옵션 하이라이트, Enter 선택, Escape 닫기 |
LhDialog | Escape 닫기(스택 최상단 오버레이만 반응) |
LhTooltip | Escape 닫기 |
오버레이가 여러 개 겹쳤을 때는 useLhOverlayStack가 열린 순서 스택을 유지하고, Escape는 최상단만 닫아 한 번의 키 입력에 모든 오버레이가 닫히는 것을 막습니다.
포커스 관리
- **
LhDialog**는 열릴 때useLhFocusTrap로 포커스를 dialog 내부에 제한하고(Tab / Shift+Tab이 외부로 나가지 않음), 닫은 뒤 포커스를 트리거로 되돌립니다 - **
LhFormItem**는 label 클릭 시 콘텐츠 영역의 첫 번째 포커스 가능 요소에 포커스를 맞춥니다(focusControl). 복합 컨트롤에서는 네이티브 label 래핑 동작이 신뢰할 수 없기 때문입니다
장식용 아이콘
LhIcon은 기본적으로 decorative입니다: aria-hidden="true"를 출력해 스크린 리더에 보이지 않습니다. 의미가 필요하면 decorative={false} + ariaLabel을 전달하세요:
<LhIcon name="search" decorative={false} ariaLabel="검색" />
이때 <svg role="img" aria-label="검색">으로 렌더링됩니다.
StrictMode 안전성
모든 컴포넌트는 React StrictMode에서 안전합니다: effect의 cleanup과 중복 마운트를 모두 멱등하게 처리했습니다(오버레이 스택 등록 / 해제, 스크롤 락 소유자 검증, focus trap 해제).
패밀리 일관성
접근성 동작은 docs/设计语言基线.md에 등록되어 있으며, 두 혈통(web / 네이티브)의 통일 계약입니다. 각 플랫폼은 동일한 @lh-ui/share/core 로직으로 구현되어, aria 속성과 키 의미론이 일치합니다.