모달 대화상자 예제
이 예제 사용에 대한 중요한 안내
주목: 이것은 ARIA 명세를 준수하는 ARIA 사용법 중 한 가지 방법의 실사례입니다.
- 일부 브라우저와 보조 기술 조합, 특히 모바일/터치 기기에서 지원 격차가 있을 수 있습니다. 보조 기술을 사용한 이 예제를 기반으로 한 코드를 테스트하는 것이 프로덕션 시스템에 사용을 고려하기 전에 반드시 필요합니다.
- ARIA와 보조기술 프로젝트는 APG 예제에 대한 보조 기술 지원에 대한 정책을 개발하고 있습니다.
- 의미론적인 HTML의 사용을 극대화하는 구현 패턴을 선택하고 잘못된 ARIA보다 ARIA을 쓰지 않는게 낫다는 경고를 제목으로 붙여 강력한 접근성이 더 최적화 될 수 있습니다.
다음은 모달 대화상자에 대한 설계 패턴의
구현 예제입니다.
다음 배송지 추가
버튼은 다른 대화상자를 여는 두 개의 버튼을 포함하는 모달 대화상자를 엽니다.
접근성 기능 섹션은 초기 초점 배치와 각 대화상자에서 aria-describedby
의 사용의 근거를 설명합니다.
유사한 예는 다음과 같습니다:
- 알림(alert) 대화상자 예제: 알림 대화상자를 보여주는 확인 프롬프트.
- 날짜 선택기 대화상자 예제: 날짜를 선택하기 위한 달력 그리드를 포함하는 대화상자를 보여줍니다.
예제
배송지 추가
확인 결과
이것은 시연일 뿐입니다. 실제 어플리케이션이었다면, 입력된 주소가 유효한지 여부를 알려주는 메세지가 제공될 것입니다.
시연 목적을 위해, 이 대화상자는 많은 텍스트를 가집니다. 다음과 같은 시나리오를 보여줍니다:
- 첫 번째 대화형 엘리먼트, 도움말 링크는 대화상자의 바닥에 있습니다.
- 대화상자가 열릴 때 첫 번째 인터랙티브 엘리먼트에 초점이 배치되는 경우, 유효성 검사 메세지가 보이지 않을 수 있습니다.
- 유효성 검사 메세지가 표시되고 도움말 링크에 초점이 있는 경우, 초점이 보이지 않을 수 있습니다.
-
대화상자가 열릴 때, 다음 두 가지가 중요합니다:
- 사용자가 읽기 위해 뒤로 스크롤 할 필요가 없도록 텍스트의 시작 부분이 보입니다.
- 키보드 초점은 항상 시각적으로 표현됩니다.
이 문제를 해결하는 몇 가지 방법이 있습니다:
- 인터랙티브 엘리먼트, 예를 들어 버튼이나 링크를 대화상자의 최상단에 배치합니다.
- 정적 엘리먼트, 예를 들어, 대화상자 제목이나 텍스트의 첫 번째 블록을 초점을 얻을 수 있게(focusable) 만드세요.
dialog 역할(role)을 가진 엘리먼트를 초점을 얻을 수 있게(focusable) 만들지 마세요!
- 초점을 얻을 수 있는(focusable) 엘리먼트가 클 수록, 특히 좁은 시야를 가진 사용자에게, 초점의 위치를 시각적으로 식별하는 것은 더 어렵습니다.
- 대화상자가 시각적 테두리를 가지므로, 전체 대화상자가 초점을 가질 때 명확한 초점의 시각적 표시기를 만드는 것은 매우 실현 가능하지 않습니다.
- 스크린리더는 초점을 얻을 수 있는(focusable) 엘리먼트의 레이블과 컨텐츠를 읽습니다. 대화상자는 자체 레이블과 많은 컨텐츠를 포함합니다! 이와 같은 대화상자가 초점을 가지고 있는 경우, 실제 초점을 이해하기 어렵습니다.
이 대화상자에서, 첫 번째 문단은 tabindex=
를 가지고 있습니다.
첫 번째 문단은 대화상자 설명을 제공하는 엘리먼트 내부에도 포함됩니다, 즉, 엘리먼트는 -1
aria-describedby
에 의해 참조 됩니다.
일부 스크린리더의 경우, 이는 대화상자가 열릴 때 부정적이지만
상대적으로 사소한 부작용 --
첫 번째 문단이 두 번 낭독될 수 있습니다.
그럼에도 불구하고, 첫 번째 문단이 초점을 얻을 수 있게(focusable) 만들고 초기에 초점을 설정하는 것이 가장 광범위한 접근 가능한 선택사항 입니다.
마지막!
어디로도 이동하지 않는 가짜 링크나 버튼을 활성화했습니다! 링크나 버튼은 데모용으로만 제공됩니다.
접근성 특성
- 작은 화면에 표시된 콘텐츠를 더 쉽게 읽을 수 있도록, 대화상자는 화면의 100%를 채웁니다. 배경 창을 완전히 덮는 것은 대화상자 내부의 콘텐츠를 스크롤할 때 일부 모바일 기기에서 발생하는 배경 움직임도 숨겨집니다.
- 초점과 접근 가능한 설명은 각 대화상자의 콘텐를 기반으로 설정됩니다.
배송지 추가
대화상자 (id=dialog1):- 초기 초점이 첫 번째 초점을 얻을 수 있는(focusable) 엘리먼트인 첫 번째 input에 설정됩니다.
- 대화상자는 이를 설명하는 정적 텍스트가 없기 때문에
aria-describedby
가 필요하지 않습니다. 취소
행위의 결과로 대화상자가 닫힐 경우, 초점을배송지 추가
버튼으로 되돌려 사용자의 관점이 유지됩니다.-
추가
행위의 결과로 대화상자가 닫히고주소 추가
대화상자가배송지 추가
대화상자를 교체하는 경우,배송지 추가
대화상자는배송지 추가
버튼에 대한 참조를주소 추가
대화상자에 전달하여 닫힐 때 사용자의 관점을 유지할 수 있습니다.
확인 결과
대화상자 (id=dialog2):- 첫 번째 인터랙티브 엘리먼트가 바닥에 있고 이는 텍스트의 길이로 인해 보이지 않기 때문에 초기 초점이 첫 번째 문장에 설정됩니다.
- 스크린리더 사용자가 대화상자 텍스트를 인식하는 것을 지원하기 위해, 대화상자 텍스트는
aria-describedby
로 참조되는div
으로 래핑됩니다. - 대화상자가 닫힐 때, 사용자의 관점을 유지하기 위해, 초점이
주소 확인
버튼으로 돌아갑니다. - 이 대화상자의 텍스트는 많은 양의 텍스트를 가진 대화상자의 초기 초점과 접근 가능한 설명에 대한 설계 고려 사항을 기술합니다.
주소 추가
대화상자 (id=dialog3):-
초기 초점은 마지막 초점을 얻을 수 있는(focusable) 엘리먼트인
확인
버튼에 설정됩니다. 이는 대부분의 사용자가 메세지를 읽는 즉시 대화상자를 간단하게 닫을 것이기 때문에 효율성을 위한 것입니다. 사용자는프로필
링크에 초점을 주기 위해 Tab을 누를 수 있습니다. - 대화상자 메세지를 포함하는 엘리먼트는 대화상자가 열릴 때 스크린리더 사용자에게 알려져야 함을 암시하기 위해
aria-describedby
에 의해 참조됩니다. - 대화상자가 닫힐 때,
배송지 추가
버튼에 초점을 설정하여 사용자의 관점이 유지됩니다.
-
초기 초점은 마지막 초점을 얻을 수 있는(focusable) 엘리먼트인
마지막!
대화상자 (id=dialog4):- 이 대화상자는 대화상자가 열릴 때 초점을 얻는 단 하나의 초점을 얻을 수 있는(focusable) 엘리먼트만 가집니다.
- dialog3와 마찬가지로,
aria-describedby
는 스크린리더 사용자를 위한 메세지 알림을 용이하게 하는 데 사용됩니다. 주소 추가
확인 대화상자를 제외하고 이 예제의 모든 다른 대화상자와 마찬가지로, 대화상자가 닫힐 때, 대화상자 표시를 트리거한 엘리먼트에 초점을 반환하여 사용자의 관점이 유지됩니다.
키보드 지원
키 | 기능 |
---|---|
Tab |
|
Shift + Tab |
|
Escape | 대화상자를 닫으세요. |
Role, Property, State, Tabindex 어트리뷰트
역할(role) | 어트리뷰트 | 엘리먼트 | 사용법 |
---|---|---|---|
dialog |
div |
대화상자 컨테이터로 제공되는 엘리먼트를 식별시킵니다. | |
aria-labelledby= |
div |
대화상자 제목을 제공하는 엘리먼트를 참조하여 대화상자에 접근 가능한 이름을 제공합니다. | |
aria-describedby= |
div |
|
|
aria-modal= |
div |
현재 대화상자 아래 창이 상호작용 할 수 없음(비활성)을 보조 기술에 알립니다. |
aria-modal
과 aria-hidden
에 대한 참고 사항
-
aria-modal
속성(property)은 ARIA 1.1에서 도입되었습니다. 새로운 속성으로서, 스크린리더 사용자는 다양한 수준의 지원을 경험할 수 있습니다. -
aria-modal
속성(property)을dialog
엘리먼트에 적용하는 것은 대화상자 외부 콘텐츠가 비활성임을 보조 기술에 알리기 위해 배경에aria-hidden
을 사용하는 기술을 대체합니다. -
보조 기술 사용자를 위해 대화상자 외부 콘텐츠를 비활성으로 만드는데
aria-hidden
가 사용되는 레거시 대화상자 구현에서는, 다음이 중요합니다:- 비활성 레이어의 일부를 포함하는 각 엘리먼트에
aria-hidden
이true
로 설정됩니다. - 대화상자 엘리먼트는
true
로 설정 된aria-hidden
을 가진 엘리먼트의 하위 항목이 아닙니다.
- 비활성 레이어의 일부를 포함하는 각 엘리먼트에
Javascript와 CSS 소스 코드
- CSS: dialog.css
- Javascript: dialog.js, utils.js
HTML Source Code
<button type="button" onclick="openDialog('dialog1', this)">
배송지 추가
</button>
<div id="dialog_layer" class="dialogs">
<div role="dialog"
id="dialog1"
aria-labelledby="dialog1_label"
aria-modal="true"
class="hidden">
<h2 id="dialog1_label" class="dialog_label">
배송지 추가
</h2>
<div class="dialog_form">
<div class="dialog_form_item">
<label>
<span class="label_text">
Street:
</span>
<input type="text" class="wide_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
City:
</span>
<input type="text" class="city_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
State:
</span>
<input type="text" class="state_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
Zip:
</span>
<input type="text" class="zip_input">
</label>
</div>
<div class="dialog_form_item">
<label for="special_instructions">
<span class="label_text">
특이 사항:
</span>
</label>
<input id="special_instructions"
type="text"
aria-describedby="special_instructions_desc"
class="wide_input">
<div class="label_info" id="special_instructions_desc">
예를 들어, 현관 비밀번호나 기사가 당신을 찾는데 도움이 되는 다른 정보
</div>
</div>
</div>
<div class="dialog_form_actions">
<button type="button" onclick="openDialog('dialog2', this, 'dialog2_para1')">
주소 확인
</button>
<button type="button" onclick="replaceDialog('dialog3', undefined, 'dialog3_close_btn')">
추가
</button>
<button type="button" onclick="closeDialog(this)">
취소
</button>
</div>
</div>
<!-- Second modal to open on top of the first modal -->
<div id="dialog2"
role="dialog"
aria-labelledby="dialog2_label"
aria-describedby="dialog2_desc"
aria-modal="true"
class="hidden">
<h2 id="dialog2_label" class="dialog_label">
확인 결과
</h2>
<div id="dialog2_desc" class="dialog_desc">
<p tabindex="-1" id="dialog2_para1">
이것은 시연일 뿐입니다. 실제 어플리케이션이었다면,
입력된 주소가 유효한지 여부를 알려주는 메세지가 제공될 것입니다.
</p>
<p>
시연 목적을 위해, 이 대화상자는 많은 텍스트를 가집니다.
다음과 같은 시나리오를 보여줍니다:
</p>
<ul>
<li>
첫 번째 대화형 엘리먼트, 도움말 링크는 대화상자의 바닥에 있습니다.
</li>
<li>
대화상자가 열릴 때 첫 번째 인터랙티브 엘리먼트에 초점이 배치되는 경우,
유효성 검사 메세지가 보이지 않을 수 있습니다.
</li>
<li>
유효성 검사 메세지가 표시되고 도움말 링크에 초점이 있는 경우,
초점이 보이지 않을 수 있습니다.
</li>
<li>
대화상자가 열릴 때, 다음 두 가지가 중요합니다:
<ul>
<li>
사용자가 읽기 위해 뒤로 스크롤 할 필요가 없도록
텍스트의 시작 부분이 보입니다.
</li>
<li>
키보드 초점은 항상 시각적으로 표현됩니다.
</li>
</ul>
</li>
</ul>
<p>
이 문제를 해결하는 몇 가지 방법이 있습니다:
</p>
<ul>
<li>
인터랙티브 엘리먼트, 예를 들어 버튼이나 링크를 대화상자의 최상단에 배치합니다.
</li>
<li>
정적 엘리먼트, 예를 들어, 대화상자 제목이나 텍스트의 첫 번째 블록을
초점을 얻을 수 있게(focusable) 만드세요.
</li>
</ul>
<p>
dialog 역할(role)을 가진 엘리먼트를 초점을 얻을 수 있게(focusable) 만들지
<em>
마세요
</em>
!
</p>
<ul>
<li>
초점을 얻을 수 있는(focusable) 엘리먼트가 클 수록, 특히 좁은 시야를 가진 사용자에게,
초점의 위치를 시각적으로 식별하는 것은 더 어렵습니다.
</li>
<li>
대화상자가 시각적 테두리를 가지므로, 전체 대화상자가 초점을 가질 때
명확한 초점의 시각적 표시기를 만드는 것은 매우 실현 가능하지 않습니다.
</li>
<li>
스크린리더는 초점을 얻을 수 있는(focusable) 엘리먼트의 레이블과 컨텐츠를 읽습니다.
대화상자는 자체 레이블과 많은 컨텐츠를 포함합니다! 이와 같은 대화상자가 초점을 가지고 있는 경우,
실제 초점을 이해하기 어렵습니다.
</li>
</ul>
<p>
이 대화상자에서, 첫 번째 문단은
<code>
tabindex=
<q>
-1
</q>
</code>
를 가지고 있습니다.
첫 번째 문단은 대화상자 설명을 제공하는 엘리먼트 내부에도 포함됩니다, 즉, 엘리먼트는
<code>
aria-describedby
</code>
에 의해 참조 됩니다.
일부 스크린리더의 경우, 이는 대화상자가 열릴 때 부정적이지만
상대적으로 사소한 부작용 --
첫 번째 문단이 두 번 낭독될 수 있습니다.
그럼에도 불구하고, 첫 번째 문단이 초점을 얻을 수 있게(focusable) 만들고 초기에 초점을 설정하는 것이 가장 광범위한 접근 가능한 선택사항 입니다.
</p>
</div>
<div class="dialog_form_actions">
<a href="#" onclick="openDialog('dialog4', this)">
도움말로 연결
</a>
<button type="button" onclick="openDialog('dialog4', this)">
대체 양식 허용
</button>
<button type="button" onclick="closeDialog(this)">
닫기
</button>
</div>
</div>
<!-- Dialog that replaces dialog 1. -->
<div id="dialog3"
role="dialog"
aria-labelledby="dialog3_label"
aria-describedby="dialog3_desc"
aria-modal="true"
class="hidden">
<h2 id="dialog3_label" class="dialog_label">
주소 추가
</h2>
<p id="dialog3_desc" class="dialog_desc">
제공 된 주소가 배송지 목록에 추가되었습니다.
즉시 사용할 수 있습니다. 삭제를 원하시면,
<a href="#" onclick="openDialog('dialog4', this)">
프로필
</a>
에서 삭제할 수 있습니다.
</p>
<div class="dialog_form_actions">
<button type="button"
id="dialog3_close_btn"
onclick="closeDialog(this)">
확인
</button>
</div>
</div>
<div id="dialog4"
role="dialog"
aria-labelledby="dialog4_label"
aria-describedby="dialog4_desc"
class="hidden"
aria-modal="true">
<h2 id="dialog4_label" class="dialog_label">
마지막!
</h2>
<p id="dialog4_desc" class="dialog_desc">
어디로도 이동하지 않는 가짜 링크나 버튼을 활성화했습니다!
링크나 버튼은 데모용으로만 제공됩니다.
</p>
<div class="dialog_form_actions">
<button type="button"
id="dialog4_close_btn"
onclick="closeDialog(this)">
닫기
</button>
</div>
</div>
</div>