- 간단한 요약
- macOS에서 키보드 탐색 기능 활성화
- 빠른 체크리스트
- 좋은 문서 개요 제공
- 스크린 리더용 접근 가능한 이름 제공
- 역할
- 키보드 전용 사용 지원
-
tabindex
- 아이콘
- 툴팁
- 요소 숨기기
- ARIA를 사용해야 하는 경우
웹 접근성 모범 사례
간단한 요약
나쁜 ARIA보다는 ARIA를 사용하지 않는 것이 더 좋다는량을 검토한 후 aria-*
, role
, 및 tabindex
를 사용하기 전에 다음 권장 사항을 검토하세요.
웹 접근성이 내장된 의미있는 HTML을 사용하고 이상적으로 스크린 리더 및 브라우저의 관련 조합으로 테스트하세요.
WebAIM의 상위 백만 홈페이지 접근성 분석에서 “ARIA가 높은 검출 가능한 오류와 연관이 있는” 것으로 나타났습니다.
ARIA의 잘못된 사용이 증가된 오류의 큰 원인일 가능성이 높으므로, 의심스러울 때는 aria-*
, role
, 및 tabindex
대신 의미 있는 HTML을 사용하는 것이 좋습니다.
macOS에서 키보드 탐색 기능 활성화
기본적으로 macOS는 탭 키를 텍스트 상자 및 목록에만 제한합니다. 전체 키보드 탐색 기능을 사용하려면:
- 시스템 환경설정을 엽니다.
- 키보드를 선택합니다.
- 단축키 탭을 엽니다.
- 컨트롤 사이를 이동하기 위해 키보드 탐색 사용 설정을 활성화합니다.
macOS용 브라우저별 키보드 탐색 기능을 활성화하는 방법에 대해 자세히 알아보려면 a11yproject를 참조하세요.
빠른 체크리스트
- 텍스트, 선택, 체크박스, 라디오, 파일, 및 토글 입력에는 접근 가능한 이름이 있습니다.
- 버튼, 링크, 및 이미지에는 설명적인 접근 가능한 이름이 있습니다.
- 아이콘
-
장식용이 아닌 아이콘에는
aria-label
이 있습니다. -
클릭 가능한 아이콘은 버튼이며, 즉
<gl-button icon="close" />
가 사용되고<gl-icon />
는 사용되지 않습니다. - 아이콘 전용 버튼에는
aria-label
이 있습니다.
-
장식용이 아닌 아이콘에는
- 상호 작용 요소는 Tab 키로 액세스할 수 있으며 시각적 포커스 상태가 있습니다.
- 툴팁이 있는 요소는 Tab 키를 사용하여 포커스할 수 있습니다.
- 불필요한
role
,tabindex
또는aria-*
속성이 있나요? - 어떤
div
또는span
요소가p
,button
, 또는time
과 같은 더 의미론적인 HTML 요소로 대체될 수 있을까요?
좋은 문서 개요 제공
스크린 리더 사용자가 콘텐츠를 탐색하는데 사용하는 주요 메커니즘은 제목입니다. 따라서 페이지의 제목 구조는 좋은 목차처럼 이해하기 쉬워야 합니다. 다음을 확인해야 합니다:
- 페이지에는 하나의
h1
요소만 있습니다. - 제목 수준이 건너뛰어지지 않습니다.
- 제목 수준이 올바르게 중첩됩니다.
스크린 리더용 접근 가능한 이름 제공
접근 가능한 이름을 제공하기 위해 모든:
- 입력에는 연관된
label
이 있습니다(#examples-of-providing-accessible-names). - 버튼과 링크에는 보이는 텍스트 또는, 보이는 텍스트가 없는 경우
aria-label
이 있습니다(예: 내용이 없는 아이콘 버튼의 경우). - 이미지에는
alt
속성이 있습니다. -
fieldset
는 첫 번째 자식으로legend
를 갖습니다. -
figure
는 첫 번째 자식으로figcaption
를 갖습니다. -
table
는 첫 번째 자식으로caption
을 갖습니다.
체크박스와 라디오 입력의 그룹은 fieldset
와 legend
로 묶어야 합니다.
legend
는 체크박스와 라디오 입력 그룹에 레이블을 제공합니다.
label
, 자식 텍스트, 또는 자식 요소가 시각적으로 원치 않는 경우 .gl-sr-only
를 사용하여 화면 판독기를 제외한 모든 것에서 해당 요소를 숨깁니다.
접근 가능한 이름 제공의 예
다음 소목은 접근 가능한 이름을 갖는 HTML 요소를 렌더링하는 마크업 예제를 포함합니다.
-
label
속성만 전달하면label
값이 포함된legend
를 렌더링하는fieldset
가 렌더링됩니다. -
label
및label-for
속성을 모두 전달하면label
이 동일한label-for
ID를 갖는 양식 입력을 가리키는label
이 렌더링됩니다.
접근 가능한 이름을 갖는 텍스트 입력
GlFormGroup
를 사용할 때, label
속성만으로는 입력에 접근 가능한 이름이 부여되지 않습니다. 입력에 접근 가능한 이름을 부여하려면 label-for
속성도 제공해야 합니다.
텍스트 입력 예시:
<!-- 레이블이 있는 입력 -->
<gl-form-group :label="__('이슈 제목')" label-for="issue-title">
<gl-form-input id="issue-title" v-model="title" />
</gl-form-group>
<!-- 숨겨진 레이블이 있는 입력 -->
<gl-form-group :label="__('이슈 제목')" label-for="issue-title" label-sr-only>
<gl-form-input id="issue-title" v-model="title" />
</gl-form-group>
textarea
예시:
<!-- 레이블이 있는 텍스트 영역 -->
<gl-form-group :label="__('이슈 설명')" label-for="issue-description">
<gl-form-textarea id="issue-description" v-model="description" />
</gl-form-group>
<!-- 숨겨진 레이블이 있는 텍스트 영역 -->
<gl-form-group :label="__('이슈 설명')" label-for="issue-description" label-sr-only>
<gl-form-textarea id="issue-description" v-model="description" />
</gl-form-group>
대신 일반적인 label
요소를 사용할 수도 있습니다:
<!-- `label`을 사용한 레이블이 있는 입력 -->
<label for="issue-title">{{ __('이슈 제목') }}</label>
<gl-form-input id="issue-title" v-model="title" />
<!-- `label`을 사용한 숨겨진 레이블이 있는 입력 -->
<label for="issue-title" class="gl-sr-only">{{ __('이슈 제목') }}</label>
<gl-form-input id="issue-title" v-model="title" />
접근 가능한 이름이 있는 입력 항목 선택
접근 가능한 이름의 선택 입력 예시:
<!-- 라벨이 있는 선택 입력 -->
<gl-form-group :label="__('이슈 상태')" label-for="issue-status">
<gl-form-select id="issue-status" v-model="status" :options="options" />
</gl-form-group>
<!-- 숨은 라벨이 있는 선택 입력 -->
<gl-form-group :label="__('이슈 상태')" label-for="issue-status" label-sr-only>
<gl-form-select id="issue-status" v-model="status" :options="options" />
</gl-form-group>
접근 가능한 이름이 있는 확인란 입력
단일 확인란:
<!-- 라벨이 있는 단일 확인란 -->
<gl-form-checkbox v-model="status" value="task-complete">
{{ __('작업 완료') }}
</gl-form-checkbox>
<!-- 숨은 라벨이 있는 단일 확인란 -->
<gl-form-checkbox v-model="status" value="task-complete">
<span class="gl-sr-only">{{ __('작업 완료') }}</span>
</gl-form-checkbox>
여러 개의 확인란:
<!-- fieldset 내에서 그룹화된 라벨이 있는 여러 확인란 -->
<gl-form-group :label="__('작업 목록')">
<gl-form-checkbox value="task-1">{{ __('작업 1') }}</gl-form-checkbox>
<gl-form-checkbox value="task-2">{{ __('작업 2') }}</gl-form-checkbox>
</gl-form-group>
<!-- 또는 -->
<gl-form-group :label="__('작업 목록')">
<gl-form-checkbox-group v-model="selected" :options="options" />
</gl-form-group>
<!-- 숨은 범례가 있는 fieldset 내에서 그룹화된 라벨이 있는 여러 확인란 -->
<gl-form-group :label="__('작업 목록')" label-sr-only>
<gl-form-checkbox value="task-1">{{ __('작업 1') }}</gl-form-checkbox>
<gl-form-checkbox value="task-2">{{ __('작업 2') }}</gl-form-checkbox>
</gl-form-group>
<!-- 또는 -->
<gl-form-group :label="__('작업 목록')" label-sr-only>
<gl-form-checkbox-group v-model="selected" :options="options" />
</gl-form-group>
접근 가능한 이름이 있는 라디오 입력
단일 라디오 입력:
<!-- 라벨이 있는 단일 라디오 -->
<gl-form-radio v-model="status" value="opened">
{{ __('개방됨') }}
</gl-form-radio>
<!-- 숨은 라벨이 있는 단일 라디오 -->
<gl-form-radio v-model="status" value="opened">
<span class="gl-sr-only">{{ __('개방됨') }}</span>
</gl-form-radio>
여러 개의 라디오 입력:
<!-- fieldset 내에서 그룹화된 라벨이 있는 여러 라디오 입력 -->
<gl-form-group :label="__('이슈 상태')">
<gl-form-radio value="opened">{{ __('개방됨') }}</gl-form-radio>
<gl-form-radio value="closed">{{ __('닫힘') }}</gl-form-radio>
</gl-form-group>
<!-- 또는 -->
<gl-form-group :label="__('이슈 상태')">
<gl-form-radio-group v-model="selected" :options="options" />
</gl-form-group>
<!-- 숨은 범례가 있는 fieldset 내에서 그룹화된 라벨이 있는 여러 라디오 입력 -->
<gl-form-group :label="__('이슈 상태')" label-sr-only>
<gl-form-radio value="opened">{{ __('개방됨') }}</gl-form-radio>
<gl-form-radio value="closed">{{ __('닫힘') }}</gl-form-radio>
</gl-form-group>
<!-- 또는 -->
<gl-form-group :label="__('이슈 상태')" label-sr-only>
<gl-form-radio-group v-model="selected" :options="options" />
</gl-form-group>
접근 가능한 이름이 있는 파일 입력
파일 입력 예시:
<!-- 라벨이 있는 파일 입력 -->
<label for="attach-file">{{ __('파일 첨부') }}</label>
<input id="attach-file" type="file" />
<!-- 숨은 라벨이 있는 파일 입력 -->
<label for="attach-file" class="gl-sr-only">{{ __('파일 첨부') }}</label>
<input id="attach-file" type="file" />
접근 가능한 이름이 있는 GlToggle 구성 요소
GlToggle
예시:
<!-- 라벨이 있는 GlToggle -->
<gl-toggle v-model="notifications" :label="__('알림')" />
<!-- 숨은 라벨이 있는 GlToggle -->
<gl-toggle v-model="notifications" :label="__('알림')" label-position="hidden" />
접근 가능한 이름이 있는 GlFormCombobox 구성 요소
GlFormCombobox
예시:
<!-- 라벨이 있는 GlFormCombobox -->
<gl-form-combobox :label-text="__('키')" :token-list="$options.tokenList" />
접근 가능한 이름이 있는 이미지
이미지 예시:
<img :src="imagePath" :alt="__('이미지 설명')" />
<!-- SVG는 기본적으로 그래픽 역할을 가지기 때문에 의미적으로 이미지인 경우 `role="img"`를 적용해야 합니다 -->
<svg role="img" :alt="__('이미지 설명')" />
<!-- 장식용 이미지, 스크린 리더에서 숨겨짐 -->
<img :src="imagePath" :alt="" />
설명적이고 접근 가능한 이름을 가진 버튼 및 링크
버튼과 링크는 독립적으로 이해될 수 있을 만큼 충분히 설명적인 접근 가능한 이름을 가져야 합니다.
<!-- 바람직하지 않음 -->
<gl-button @click="handleClick">{{ __('제출') }}</gl-button>
<gl-link :href="url">{{ __('페이지') }}</gl-link>
<!-- 바람직함 -->
<gl-button @click="handleClick">{{ __('리뷰 제출') }}</gl-button>
<gl-link :href="url">{{ __('GitLab의 접근성 페이지') }}</gl-link>
버튼과 유사한 스타일이 적용된 링크
링크는 GlButton
을 사용하여 버튼과 유사한 스타일을 적용할 수 있습니다.
<gl-button :href="url">{{ __('버튼으로 스타일이 적용된 링크') }}</gl-button>
역할
일반적으로 role
사용을 피하십시오.
암시적으로 role
이 있는 의미론적 HTML 요소를 사용하십시오.
나쁨 | 좋음 |
---|---|
<div role="button">
| <button>
|
<div role="img">
| <img>
|
<div role="link">
| <a>
|
<div role="header">
|
<h1> ~ <h6>
|
<div role="textbox">
|
<input> 또는 <textarea>
|
<div role="article">
| <article>
|
<div role="list">
|
<ol> 또는 <ul>
|
<div role="listitem">
| <li>
|
<div role="table">
| <table>
|
<div role="rowgroup">
|
<thead> , <tbody> , 또는 <tfoot>
|
<div role="row">
| <tr>
|
<div role="columnheader">
| <th>
|
<div role="cell">
| <td>
|
키보드 전용 사용 지원
키보드 사용자는 페이지 상에서 자신이 어디에 있는지 이해하기 위해 포커스 아웃라인에 의존합니다. 따라서 다음 사항을 확인해야 합니다.
- 키보드 포커스를 받을 수 있어야 합니다.
- 시각적으로 포커스 상태여야 합니다.
기본적으로 이러한 동작을 제공하는 a
(GlLink
) 및 button
(GlButton
)과 같은 의미 있는 HTML을 사용하세요.
다음 사항을 염두에 두세요.
- Tab 및 Shift-Tab은 대화형 요소 간에만 이동해야 하며 정적 콘텐츠로 이동해서는 안됩니다.
- 보통 경우에
:hover
스타일을 추가할 때에는 마우스 와 키보드 사용자 모두에게 스타일이 적용되도록:focus
스타일을 추가해야 합니다. - 상호작용 요소의
outline
을 제거하는 경우에는box-shadow
와 같은 다른 방법으로 시각적 포커스 상태를 유지해야 합니다.
자세한 내용은 Pajamas 키보드 전용 페이지를 참조하세요.
tabindex
tabindex
대신 상대적으로 tabindex
를 사용하세요.
-
button
(GlButton
)과 같은 의미 있는 HTML을 사용하면 암시적으로tabindex="0"
이 제공됩니다. - 탭 순서는 시각적 읽기 순서와 일치해야 하며 양수의
tabindex
는 이를 방해할 수 있습니다.
tabindex="0"
대신 요소를 상호작용 가능하게 만드는 것을 피하세요
div
및 span
태그 대신 상호작용 요소를 사용하세요.
예:
- 요소를 클릭할 수 있어야 하는 경우
button
(GlButton
)을 사용합니다. - 텍스트를 편집할 수 있어야 하는 경우
input
또는textarea
를 사용하세요.
마크업이 의미적으로 완료되면 CSS를 사용하여 원하는 시각적 상태로 업데이트하세요.
<!-- 나쁨 -->
<div role="button" tabindex="0" @click="expand">확장</div>
<!-- 좋음 -->
<gl-button class="gl-p-0!" category="tertiary" @click="expand">확장</gl-button>
상호작용 요소에 tabindex="0"
을 사용하지 마세요
이미 상호작용 요소는 탭으로 접근할 수 있으므로 tabindex
를 추가하는 것은 중복입니다.
<!-- 나쁨 -->
<gl-link href="help" tabindex="0">도움말</gl-link>
<gl-button tabindex="0">제출</gl-button>
<!-- 좋음 -->
<gl-link href="help">도움말</gl-link>
<gl-button>제출</gl-button>
스크린 리더가 읽을 요소에 tabindex="0"
을 사용하지 마세요
스크린 리더가 탭으로 접근할 수 없는 텍스트를 읽을 수 있습니다.
tabindex="0"
의 사용은 불필요하며 문제를 일으킬 수 있습니다.
스크린 리더 사용자는 상호작용할 수 있을 것으로 기대하기 때문입니다.
<!-- 나쁨 -->
<p tabindex="0" :aria-label="message">{{ message }}</p>
<!-- 좋음 -->
<p>{{ message }}</p>
양수의 tabindex
를 사용하지 마세요
항상 tabindex="1"
이상의 사용을 피하세요.(tabindex="1"
이상 사용을 항상 피하세요).
아이콘
아이콘은 다음과 같이 세 가지 유형으로 나뉠 수 있습니다.
- 정보를 전달하지 않는 장식용 아이콘
- 의미를 전달하는 아이콘
- 클릭할 수 있는 아이콘
정보를 전달하지 않는 장식용 아이콘
아이콘이 UI에서 제거되어도 사용자에게 정보가 손실되지 않을 때 장식용으로 간주됩니다.
GitLab 내의 대다수 아이콘은 장식적이므로 GlIcon
은 암시적으로 스크린 리더로부터 아이콘을 숨깁니다.
따라서 장식용 GlIcon
에 aria-hidden="true"
을 추가할 필요가 없습니다.
<!-- 불필요함 — gl-icon은 스크린 리더로부터 아이콘을 기본적으로 숨깁니다 -->
<gl-icon name="rocket" aria-hidden="true" />
<!-- 좋음 -->
<gl-icon name="rocket" />
의미를 전달하는 아이콘
아이콘이 UI에서 제거되면 사용자에게 정보가 손실될 때 정보를 전달합니다. 예를 들어 기밀 아이콘은 이슈가 기밀임을 전달하며 옆에 “기밀”이라는 텍스트가 없습니다. 이러한 아이콘은 스크린 리더 사용자에게도 정보가 전달되도록 액세스 가능한 이름을 가져야 합니다.
<!-- 나쁨 -->
<gl-icon name="eye-slash" />
<!-- 좋음 -->
<gl-icon name="eye-slash" :aria-label="__('기밀 이슈')" />
클릭할 수 있는 아이콘
클릭할 수 있는 아이콘은 의미적으로 버튼이므로 액세스 가능한 이름이 있는 버튼으로 렌더링되어야 합니다.
<!-- 나쁨 -->
<gl-icon name="close" :aria-label="__('닫기')" @click="handleClick" />
<!-- 좋음 -->
<gl-button icon="close" category="tertiary" :aria-label="__('닫기')" @click="handleClick" />
툴팁
툴팁을 추가할 때, 키보드 사용자가 툴팁을 볼 수 있도록 툴팁이 있는 요소가 포커스를 받을 수 있어야 합니다.
요소가 아이콘과 같이 정적인 경우에는 이미 포커스를 받을 수 있는 버튼으로 둘러싸 줄 수 있으므로 tabindex=0
를 추가할 필요가 없습니다.
다음 코드 스니펫은 툴팁이 있는 아이콘의 좋은 예시입니다.
- 이미 버튼이므로 자동으로 포커스를 받을 수 있습니다.
- 버튼이므로
aria-label
로 접근 가능한 이름을 제공합니다. - 버튼의 배경이 호버 시에 회색으로 변하지 않도록
gl-hover-bg-transparent!
클래스를 사용할 수 있습니다. - 필요에 따라 버튼의 패딩을 제거하기 위해
gl-p-0!
클래스를 사용할 수 있습니다.
<gl-button
v-gl-tooltip
class="gl-hover-bg-transparent! gl-p-0!"
icon="warning"
category="tertiary"
:title="tooltipText"
:aria-label="__('경고')"
/>
요소 숨기기
상황에 맞게 사용자들로부터 요소를 숨기려면 다음 표를 사용하세요.
시각적으로 숨기기 | 스크린 리더로부터 숨기기 | 시각적으로 및 스크린 리더로부터 숨기기 |
---|---|---|
.gl-sr-only
| aria-hidden="true"
|
display: none , visibility: hidden , 또는 hidden 속성
|
스크린 리더로부터 장식적 이미지 숨기기
스크린 리더의 소음을 줄이기 위해 장식용 이미지를 alt=""
로 숨깁니다.
이미지가 img
요소가 아닌 경우(예: 인라인 SVG), role="img"
와 alt=""
를 추가하여 숨길 수 있습니다.
gl-icon
구성요소는 기본적으로 스크린 리더로부터 아이콘을 숨기므로 aria-hidden="true"
를 사용할 필요가 없습니다.
<!-- 좋음 - 스크린 리더로부터 장식용 이미지 숨김 -->
<img src="decorative.jpg" alt="">
<svg role="img" alt="" />
<gl-icon name="epic" />
ARIA를 사용해야 하는 경우
의미 있는 HTML을 사용할 때는 ARIA가 필요하지 않습니다. 왜냐하면 이미 접근성이 내재되어 있기 때문입니다.
그러나 시맨틱 HTML과 동등한 UI 패턴이 몇 가지 있습니다. 이러한 예시로는 대화상자(모달) 및 탭이 일반적으로 있습니다. GitLab 특정 예시로는 담당자 및 라벨 드롭다운이 있습니다. 이러한 위젯을 구축할 때는 스크린 리더가 이해할 수 있도록 ARIA가 필요합니다. WCAG 준수를 보장하기 위해 적절한 연구와 테스트가 수행되어야 합니다.