notes
UI - Aria
UI 접근성을 위한 ARIA role·attribute 사용 기준과 마크업 메모.
// ARIA 속성들의 타입 정의
type AriaRole =
| 'button'
| 'checkbox'
| 'menuitem'
| 'tab'
| 'alert'
| 'progressbar'
| 'tooltip';
interface AriaAttributes {
'aria-label'?: string;
'aria-expanded'?: boolean;
'aria-hidden'?: boolean;
'aria-selected'?: boolean;
'aria-describedby'?: string;
'aria-controls'?: string;
}
// ARIA 사용 예시 컴포넌트들
const AccessibleButton: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
return (
<button
role="button"
aria-expanded={isExpanded}
aria-controls="content-panel"
onClick={() => setIsExpanded(!isExpanded)}
>
메뉴 펼치기
</button>
);
};
const AccessibleTab: React.FC = () => {
return (
<div
role="tab"
aria-selected={true}
aria-controls="panel-1"
tabIndex={0}
>
첫 번째 탭
</div>
);
};
const AccessibleAlert: React.FC = () => {
return (
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
>
중요한 알림이 있습니다!
</div>
);
};
ARIA의 주요 개념
interface ARIARoles {
// 1. 랜드마크 역할
main: "문서의 주요 콘텐츠를 나타냄",
navigation: "탐색 섹션을 나타냄",
banner: "헤더 영역을 나타냄",
// 2. 위젯 역할
button: "클릭 가능한 버튼",
tablist: "탭 목록",
menuitem: "메뉴 항목",
// 3. 라이브 리전 역할
alert: "중요한 알림",
status: "상태 업데이트"
}
status
interface ARIAStates {
expanded: boolean; // 확장/축소 상태
selected: boolean; // 선택 상태
checked: boolean; // 체크 상태
disabled: boolean; // 비활성화 상태
hidden: boolean; // 숨김 상태
}
예시
const AccessibleAccordion: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button
aria-expanded={isOpen}
aria-controls="accordion-content"
onClick={() => setIsOpen(!isOpen)}
>
아코디언 버튼
</button>
<div
id="accordion-content"
aria-hidden={!isOpen}
style={{ display: isOpen ? 'block' : 'none' }}
>
아코디언 내용
</div>
</div>
);
};
class ARIABestPractices {
static rules = {
rule1: "시맨틱 HTML을 우선 사용하기",
rule2: "ARIA는 필요한 경우에만 추가하기",
rule3: "role과 state를 올바르게 매칭하기",
rule4: "키보드 접근성 함께 고려하기"
};
static example(): void {
// ❌ 잘못된 사용
const bad = <div role="button">클릭</div>;
// ✅ 좋은 사용
const good = <button>클릭</button>;
// ✅ ARIA가 필요한 경우
const complex = (
<div
role="tabpanel"
aria-labelledby="tab-1"
>
탭 내용
</div>
);
}
}