모달 대화상자 예제

이 예제 사용에 대한 중요한 안내

주목: 이것은 ARIA 명세를 준수하는 ARIA 사용법 중 한 가지 방법의 실사례입니다.

다음은 모달 대화상자에 대한 설계 패턴의 구현 예제입니다. 다음 배송지 추가 버튼은 다른 대화상자를 여는 두 개의 버튼을 포함하는 모달 대화상자를 엽니다. 접근성 기능 섹션은 초기 초점 배치와 각 대화상자에서 aria-describedby의 사용의 근거를 설명합니다.

유사한 예는 다음과 같습니다:

예제

접근성 특성

  1. 작은 화면에 표시된 콘텐츠를 더 쉽게 읽을 수 있도록, 대화상자는 화면의 100%를 채웁니다. 배경 창을 완전히 덮는 것은 대화상자 내부의 콘텐츠를 스크롤할 때 일부 모바일 기기에서 발생하는 배경 움직임도 숨겨집니다.
  2. 초점과 접근 가능한 설명은 각 대화상자의 콘텐를 기반으로 설정됩니다.
    1. 배송지 추가 대화상자 (id=dialog1):
      • 초기 초점이 첫 번째 초점을 얻을 수 있는(focusable) 엘리먼트인 첫 번째 input에 설정됩니다.
      • 대화상자는 이를 설명하는 정적 텍스트가 없기 때문에 aria-describedby가 필요하지 않습니다.
      • 취소 행위의 결과로 대화상자가 닫힐 경우, 초점을 배송지 추가 버튼으로 되돌려 사용자의 관점이 유지됩니다.
      • 추가 행위의 결과로 대화상자가 닫히고 주소 추가 대화상자가 배송지 추가 대화상자를 교체하는 경우, 배송지 추가 대화상자는 배송지 추가 버튼에 대한 참조를 주소 추가 대화상자에 전달하여 닫힐 때 사용자의 관점을 유지할 수 있습니다.
    2. 확인 결과 대화상자 (id=dialog2):
      • 첫 번째 인터랙티브 엘리먼트가 바닥에 있고 이는 텍스트의 길이로 인해 보이지 않기 때문에 초기 초점이 첫 번째 문장에 설정됩니다.
      • 스크린리더 사용자가 대화상자 텍스트를 인식하는 것을 지원하기 위해, 대화상자 텍스트는 aria-describedby로 참조되는 div으로 래핑됩니다.
      • 대화상자가 닫힐 때, 사용자의 관점을 유지하기 위해, 초점이 주소 확인 버튼으로 돌아갑니다.
      • 이 대화상자의 텍스트는 많은 양의 텍스트를 가진 대화상자의 초기 초점과 접근 가능한 설명에 대한 설계 고려 사항을 기술합니다.
    3. 주소 추가 대화상자 (id=dialog3):
      • 초기 초점은 마지막 초점을 얻을 수 있는(focusable) 엘리먼트인 확인 버튼에 설정됩니다. 이는 대부분의 사용자가 메세지를 읽는 즉시 대화상자를 간단하게 닫을 것이기 때문에 효율성을 위한 것입니다. 사용자는 프로필 링크에 초점을 주기 위해 Tab을 누를 수 있습니다.
      • 대화상자 메세지를 포함하는 엘리먼트는 대화상자가 열릴 때 스크린리더 사용자에게 알려져야 함을 암시하기 위해 aria-describedby에 의해 참조됩니다.
      • 대화상자가 닫힐 때, 배송지 추가 버튼에 초점을 설정하여 사용자의 관점이 유지됩니다.
    4. 마지막! 대화상자 (id=dialog4):
      • 이 대화상자는 대화상자가 열릴 때 초점을 얻는 단 하나의 초점을 얻을 수 있는(focusable) 엘리먼트만 가집니다.
      • dialog3와 마찬가지로, aria-describedby는 스크린리더 사용자를 위한 메세지 알림을 용이하게 하는 데 사용됩니다.
      • 주소 추가 확인 대화상자를 제외하고 이 예제의 모든 다른 대화상자와 마찬가지로, 대화상자가 닫힐 때, 대화상자 표시를 트리거한 엘리먼트에 초점을 반환하여 사용자의 관점이 유지됩니다.

키보드 지원

기능
Tab
  • 대화상자 내부의 다음 초점을 얻을 수 있는(focusable) 엘리먼트로 초점을 이동시키세요.
  • 초점이 대화상자의 마지막 초점을 얻을 수 있는 엘리먼트에 있다면, 대화상자의 첫 번째 초점을 얻을 수 있는 엘리먼트로 초점을 이동시키세요.
Shift + Tab
  • 대화상자 내부의 이전 초점을 얻을 수 있는(focusable) 엘리먼트로 초점을 이동시키세요.
  • 초점이 대화상자의 첫 번째 초점을 얻을 수 있는 엘리먼트에 있다면, 대화상자의 마지막 초점을 얻을 수 있는 엘리먼트로 초점을 이동시키세요.
Escape 대화상자를 닫으세요.

Role, Property, State, Tabindex 어트리뷰트

역할(role) 어트리뷰트 엘리먼트 사용법
dialog div 대화상자 컨테이터로 제공되는 엘리먼트를 식별시킵니다.
aria-labelledby=IDREF div 대화상자 제목을 제공하는 엘리먼트를 참조하여 대화상자에 접근 가능한 이름을 제공합니다.
aria-describedby=IDREF div
  • 주요 메세지나 대화상자의 목적을 설명하는 대화상자 콘텐츠를 참조하여 대화상자에 접근 가능한 설명을 제공하세요.
  • 예제에 포함 된 4개의 대화상자 중 3개에서 사용됩니다. 설명은 위의 접근성 기능 섹션을 참고하세요.
aria-modal=true div 현재 대화상자 아래 창이 상호작용 할 수 없음(비활성)을 보조 기술에 알립니다.

aria-modalaria-hidden에 대한 참고 사항

  1. aria-modal 속성(property)은 ARIA 1.1에서 도입되었습니다. 새로운 속성으로서, 스크린리더 사용자는 다양한 수준의 지원을 경험할 수 있습니다.
  2. aria-modal 속성(property)을 dialog 엘리먼트에 적용하는 것은 대화상자 외부 콘텐츠가 비활성임을 보조 기술에 알리기 위해 배경에 aria-hidden을 사용하는 기술을 대체합니다.
  3. 보조 기술 사용자를 위해 대화상자 외부 콘텐츠를 비활성으로 만드는데 aria-hidden가 사용되는 레거시 대화상자 구현에서는, 다음이 중요합니다:
    1. 비활성 레이어의 일부를 포함하는 각 엘리먼트에 aria-hiddentrue로 설정됩니다.
    2. 대화상자 엘리먼트는 true로 설정 된 aria-hidden을 가진 엘리먼트의 하위 항목이 아닙니다.

Javascript와 CSS 소스 코드

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>