폼은 일반적으로 웹사이트와 웹 애플리케이션에서 사용자 상호 작용을 제공하는 데 사용됩니다. 예를 들어 로그인, 등록, 댓글 작성, 구매 등이 이에 해당합니다. 이 튜토리얼에서는 접근성이 높은 폼 양식을 만드는 방법을 보여줍니다. 처리되는 폼이 클라이언트 측이든 서버 측이든 모든 양식에 동일한 개념이 적용됩니다.
기술적인 고려 사항 외에도 사용자는 일반적으로 간단하고 짧은 폼을 선호합니다. 사용자에게 거래 또는 프로세스를 완료하는 데 필요한 최소한의 정보만 입력하도록 요청하세요. 관련성이 없거나 과도한 데이터를 요청하면 사용자가 폼 작성을 포기할 가능성이 높습니다.
- 레이블 컨트롤 : <label> 요소를 사용하고 특정한 경우에는 각 폼 컨트롤을 식별하기 위해 다른 메커니즘 (예: WAI-ARIA, title 속성 등)을 사용합니다.
- 컨트롤 그룹화 : <fieldset> 및 <legend> 요소를 사용하여 관련 폼 컨트롤을 그룹화하고 연결합니다.
- 폼 지침 : 사용자가 폼및 개별 폼 컨트롤을 완료하는 방법을 이해하는 데 도움이 되는 지침을 제공합니다.
- 입력 유효성 검사 : 사용자가 제공한 입력의 유효성을 검사하고 변경 사항을 실행 취소하고 데이터 입력을 확인할 수 있는 옵션을 제공합니다.
- 사용자 알림 : 사용자에게 성공적인 작업 완료 및 오류에 대해 알리고 실수를 수정하는 데 도움이 되는 지침을 제공합니다.
- 여러 페이지로 구성된 폼 : 긴 폼 입력을 일련의 논리적 단계 또는 단계를 구성하는 여러 개의 작은 폼으로 나누고 사용자에게 진행 단계를 알립니다.
- 사용자 지정 컨트롤 : 폼 요소에 스타일을 적용하고 기타 점진적 기능 향상 기법을 사용하여 사용자 지정 컨트롤을 제공합니다.
시간 제한에 대한 참고 사항
가능하면 사용자가 자신의 속도에 맞춰 폼을 작성할 수 있도록 폼에 시간 제한을 두지 않아야 합니다. 예를 들어, 보안상의 이유로 시간 제한을 적용해야 하는 경우에는 사용자가 시간 제한을 끄거나 연장할 수 있는 옵션을 제공해야 합니다. 시간 제한이 경매나 게임과 같은 실시간 이벤트로 인한 것이거나 폼을 작성하는 데 걸리는 시간이 유효한 제출에 필수적인 경우에는 이 제한이 적용되지 않습니다.
이것이 왜 중요한가요?
폼은 시각적 혹은 인지적으로 복잡하고 사용하기 어려울 수 있습니다. 접근성 있는 폼은 장애인을 포함한 모든 사람이 더 쉽게 사용할 수 있습니다.
- 폼을 접근 가능하게 만들면 레이아웃 구조, 지침 및 피드백이 개선되므로 인지 장애가 있는 사람도 양식과 폼 작성 방법을 더 잘 이해할 수 있습니다.
- 음성 입력을 사용하는 사용자는 음성 명령을 통해 레이블을 사용하여 컨트롤을 활성화하고 완료해야 하는 필드로 초점을 이동할 수 있습니다.
- 특히 라디오 버튼이나 확인란과 같은 작은 컨트롤의 경우, 손놀림에 불편함이 있는 사람들은 레이블을 포함한 클릭 가능한 큰 영역이 유용합니다.
- 스크린 리더 사용자들은 폼 컨트롤이 label, fieldset 및 기타 구조적 요소와 연결되어 있기 때문에 더 쉽게 식별하고 이해할 수 있습니다.
레이블 컨트롤
텍스트 필드, 체크박스, 라디오 버튼, 드롭다운 메뉴 등 모든 폼 컨트롤을 식별하기 위해 레이블을 제공합니다.
대부분의 경우 <label> 요소를 사용하여 이 작업을 수행합니다.
레이블은 폼 컨트롤의 목적을 설명해야 합니다. 튜토리얼의 이번 섹션에서는 폼 컨트롤과 올바르게 연결된 레이블을 제공하는 방법에 대해 설명합니다. 이후 섹션에서는 지침을 제공하고, 사용자 입력의 유효성을 검사하고, 사용자가 양식을 작성하는 데 도움이 되는 피드백을 제공하는 방법에 대해 설명합니다.
레이블과 폼 컨트롤은 암묵적 또는 명시적으로 서로 연결되어야 합니다. 예를 들어 웹 브라우저는 컨트롤을 선택하거나 활성화하기 위해 레이블을 더 확장하여 클릭 가능한 영역을 넓게 제공합니다. 또한 보조 기술이 폼 컨트롤을 표시할 때 올바른 레이블을 참조할 수 있도록 보장합니다.
레이블을 명시적으로 연결하기
가능하면 레이블 요소를 사용하여 텍스트를 폼 요소와 명시적으로 연결하세요. 레이블의 for 속성은 폼 컨트롤의 id와 정확히 일치해야 합니다.
<label for="firstname">First name:</label>
<input type="text" name="firstname" id="firstname"><br>
<input type="checkbox" name="subscribe" id="subscribe">
<label for="subscribe">Subscribe to newsletter</label>
레이블 텍스트 숨기기
폼 컨트롤의 레이블은 모든 사람이 그 목적을 더 잘 이해할 수 있도록 도와줍니다. 콘텐츠가 렌더링될 때 경우에 따라 문맥에서의 목적이 충분히 명확해 질 수 있습니다. 레이블은 시각적으로 숨길 수 있지만 스크린 리더 및 음성 입력 사용자와 같은 다른 형태의 프레젠테이션 및 상호 작용을 지원하려면 코드 내에 레이블을 제공해야 합니다. 이 자습서에서 요소를 시각적으로 숨기되 보조 기술에서 사용할 수 있도록 하는 방법은 아래 요소 숨기기 참고 사항에 설명되어 있습니다.
아래 예에서 검색 필드가 검색 버튼 바로 옆에 배치되어 있습니다. 텍스트 입력 필드의 목적은 대부분의 상황에서 문맥을 통해 명확하게 알 수 있습니다.
접근 방식 1 : 레이블 요소 숨기기
이 접근 방식에서는 코드 내에서 폼 컨트롤을 식별하기 위해 <label> 요소가 제공되지만 시각적 단서를 통해 목적을 유추할 수 있는 사용자의 중복성을 피하기 위해 화면에서 숨겨져 있습니다.
<label for="search" class="visuallyhidden">Search: </label>
<input type="text" name="search" id="search">
<button type="submit">Search</button>
접근 방식 2 : aria-label 사용하기
aria-label 속성은 폼 컨트롤을 식별하기 위해 사용할 수 있습니다. 이 접근 방식은 스크린 리더 및 기타 보조 기술에서 잘 지원되지만 title 속성(아래 참조)과 달리 시각적 사용자에게 정보가 전달되지 않습니다. 따라서 이 접근 방식은 아래 예의 버튼과 같이 컨트롤의 레이블이 주변 콘텐츠와 명확하게 구분되는 경우에만 사용해야 합니다.
<input type="text" name="search" aria-label="Search">
<button type="submit">Search</button>
접근 방식 3 : aria-labelledby 사용하기
aria-labelledby 속성은 폼 컨트롤을 식별하기 위해 사용할 수 있습니다. 이 접근 방식은 스크린 리더 및 기타 보조 기술에서 잘 지원되지만 title 속성(아래 참조)과 달리 시각적 사용자에게 정보가 전달되지 않습니다. 따라서 이 접근 방식은 아래 예시의 버튼과 같이 컨트롤의 레이블이 주변 콘텐츠에서 명확할 때만 사용해야 합니다.
레이블 텍스트가 포함된 요소의 id는 aria-labelledby 속성의 값으로 사용됩니다.
<input type="text" name="search" aria-labelledby="searchbutton">
<button id="searchbutton" type="submit">Search</button>
접근 방식 4 : title 속성 이용하기
title 속성은 폼 컨트롤을 식별하기 위해 사용할 수 있습니다. 일부 스크린 리더 및 보조 기술은 title 속성을 레이블 요소를 대체하는 것으로 해석하지 않거나 title 속성이 필수적이지 않은 정보를 제공하는데 사용되는 경우가 많기 때문에 이 접근 방식은 일반적으로 신뢰성이 떨어지며 권장되지 않습니다. title 속성의 정보는 마우스로 폼 필드 위로 마우스를 가져가면 사용자에게 시각적 툴팁으로 표시됩니다.
<input title="Search" type="text" name="search">
<button type="submit">Search</button>
요소 숨기기 관련 참고 사항
스크린 리더 및 기타 보조 기술은 웹 브라우저와 마찬가지로 display: none 및 visibility: hidden을 사용하여 스타일을 지정할 때 사용자에게 요소를 숨깁니다.
정보를 시각적으로 숨기되 스크린 리더 및 기타 보조 기술 사용자가 계속 사용할 수 있도록 하는 데 사용되는 일반적인 접근 방식은 정보를 기술적으로는 표시하지만 실질적으로는 숨긴 상태로 유지하는 CSS를 사용하는 것입니다. 예를 들어, 아래에서 visuallyhidden (CSS visibility: hidden과 혼동하지 마세요) CSS 클래스를 사용하여 1픽셀 영역에 레이블을 표시하는 것입니다.
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
이 코드를 사용할 때 다이얼로그 요소는 여전히 활성화되어 있으므로 레이블을 제외한 폼의 다른 부분을 숨기는 것은 적합하지 않습니다.
암시적으로 레이블 연결하기
일부 상황에서는 폼 컨트롤에 명시적으로 레이블을 지정할 수 없습니다. 예를 들어 콘텐츠 작성자가 스크립트에서 생성된 폼 필드의 id를 모르거나 해당 스크립트에서 id를 전혀 추가하지 않을 수 있습니다. 이 경우 label 요소는 폼 컨트롤과 레이블 텍스트의 컨테이너로 사용되므로 두 요소가 암시적으로 연결됩니다. 일반적으로 명시적 레이블이보조 기술을 통해 더 잘 지원됩니다.
<label>
First name:
<input type="text" name="firstname">
</label>
<br>
<label>
<input type="checkbox" name="subscribe">
Subscribe to newsletter
</label>
버튼 레이블 지정
<button> 요소의 레이블은 요소 내부에 설정되며 마크업을 포함할 수 있습니다. 이를 통해 언어 변경 마크업과 같은 고급 접근성 힌트를 포함할 수 있습니다. <input> 요소를 사용하여 버튼을 만들 때 레이블은 요소의 value 속성에 설정됩니다.
<button type="submit">Submit</button>
<button type="button">Cancel</button>
<input type="submit" value="Submit">
<input type="button" value="Cancel">
참고
이미지 버튼(<input type=”image”>)을 사용하는 경우, 예를 들어 alt 속성에 레이블이 설정됩니다. <input type="image" src="searchbutton.png" alt="Search"> 버튼에 이미지를 설명하는 방법에 대한 자세한 내용은 기능적 이미지를 참조하세요.
레이블 텍스트의 시각적 위치
왼쪽에서 오른쪽으로 쓰는 언어에서는 레이블을 라디오 버튼 및 확인란의 오른쪽에, 다른 폼 필드의 왼쪽 또는 바로 위에 시각적으로 배치하는 것이 일반적입니다. 이 관행을 유지하면 모든 사용자의 양식에 대한 예측 가능성과 이해도가 높아집니다.
일반적으로 레이블을 폼 필드 위에 배치하면 저시력자 및 모바일 장치 사용자의 가로 스크롤을 줄이는 데 도움이 됩니다. 그러나 이 접근 방식의 유용성은 주변의 다른 양식 필드 및 콘텐츠의 근접성 등 다른 디자인 이슈에 따라 달라지므로 개별적으로 평가해야 합니다. 목표는 레이블과 폼컨트롤 사이에 가깝고 뚜렷한 시각적 관계를 유지하는 것입니다.
연관 폼 요소 그룹화
연관된 폼 요소들을 그룹화하면 관련 요소들을 모든 사용자가 더 쉽게 식별할 수 있으므로 더 쉽게 이해할 수 있습니다. 또한 사람들이 전체 폼을 한 번에 파악하는 대신 더 작고 관리하기 쉬운 그룹에 더 쉽게 집중할 수 있습니다.
그룹화는 시각적으로나 코드에서 수행해야 합니다. (예: <fieldset> 및 <legend> 요소를 사용하여 관련 폼 요소들을 연결). 또한 <select> 요소의 관련 항목은 <optgroup>을 사용하여 그룹화할 수 있습니다.
접근 방식 1 : 관련 요소들을 fieldset과 연결
<fieldset> 요소는 관련 폼 요소들을 위한 컨테이너를 제공하며, <legend> 요소는 그룹을 식별하는 제목 역할을 합니다. 컨트롤 그룹에 대한 범례는 모든 컨트롤의 공통 속성을 강조 표시하여 그룹의 모든 필드가 필수임을 알릴 수도 있습니다.
예제 1) 라디오 버튼
아래 예시에는 사용자가 출력 형식을 선택할 수 있는 세 개의 라디오 버튼이 있습니다. 라디오 버튼 그룹은 항상 <fieldset>을 사용하여 그룹화해야 합니다.
<fieldset>
<legend>Output format</legend>
<div>
<input type="radio" name="format" id="txt" value="txt" checked>
<label for="txt">Text file</label>
</div>
<div>
<input type="radio" name="format" id="csv" value="csv">
<label for="csv">CSV file</label>
</div>
[…]
</fieldset>
예제 2) 체크박스
아래 예에서 세 개의 체크박스는 다양한 유형의 정보를 수신하기 위한 옵트인(opt-in, 반드시 항목을 선택해야 폼이 처리되는) 기능의 일부입니다.
<fieldset>
<legend>I want to receive</legend>
<div>
<input type="checkbox" name="newsletter" id="check_1">
<label for="check_1">The weekly newsletter</label>
</div>
[…]
</fieldset>
예제 3) 관련 필드
이 예에서는 배송 및 청구 주소를 입력하는 폼 필드를 보여줍니다. 두 그룹의 레이블에는 동일한 텍스트가 있으므로 fieldset 요소는 그룹별로 폼 필드를 구분하는 데 도움이 됩니다. <legend>가 스크린 리더에서 읽히지 않는 경우(아래 참고 사항 참조) 각 그룹의 첫 번째 폼 컨트롤의 레이블에는 그룹 이름이 포함되어야 합니다. 이 이름은 시각적으로 숨길 수 있습니다.
<fieldset>
<legend>Shipping Address:</legend>
<div>
<label for="shipping_name">
<span class="visuallyhidden">Shipping </span>Name:
</label><br>
<input type="text" name="shipping_name" id="shipping_name">
</div>
<div>
<label for="shipping_street">Street:</label><br>
<input type="text" name="shipping_street" id="shipping_street">
</div>
[…]
</fieldset>
<fieldset>
<legend>Billing Address:</legend>
<div>
<label for="billing_name">
<span class="visuallyhidden">Billing </span>Name:
</label><br>
<input type="text" name="billing_name" id="billing_name">
</div>
<div>
<label for="billing_street">Street:</label><br>
<input type="text" name="billing_street" id="billing_street">
</div>
[…]
</fieldset>
참고
구성에 따라 일부 스크린 리더는 모든 양식 요소에 대해 범례를 한 번만 읽거나, 드물게는 전혀 읽지 않을 수도 있습니다. 이를 수용하려면 다음을 고려하십시오.
- 매번 레이블과 함께 읽어야 하는 상황에서는 범례를 가능한 한 짧게 만듭니다.
- 범례를 소리내어 읽지 않는 상황에서는 모든 레이블에서 범례를 반복하지 않고 개별 레이블을 충분히 설명 할 수 있도록 만듭니다.
접근 방식 2 : 관련 컨트롤을 WAI-ARIA와 연결하기
WAI-ARIA는 fieldset 및 legend와 유사하게 작동하는 그룹화 역할을 제공합니다. 이 예제에서 div 요소에는 포함된 요소가 그룹의 멤버임을 나타내는 role=group이 있고 aria-labelledby 속성은 그룹의 레이블 역할을 할 텍스트의 id를 참조합니다.
이 기법은 추가적인 스타일링 가능성을 제공합니다.
모든 웹 브라우저 및 스크린 리더 조합에서 WAI-ARIA가 완벽하게 지원되는 것은 아니므로 그룹의 첫 번째 폼 컨트롤에 그룹 식별자를 추가해야 합니다.
<div role="group" aria-labelledby="shipping_head">
<div id="shipping_head">Shipping Address:</div>
<div>
<label for="shipping_name">
<span class="visuallyhidden">Shipping </span>Name:
</label><br>
<input type="text" name="shipping_name" id="shipping_name">
</div>
[…]
</div>
<div role="group" aria-labelledby="billing_head">
<div id="billing_head">Billing Address:</div>
<div>
<label for="billing_name">
<span class="visuallyhidden">Billing </span>Name:
</label><br>
<input type="text" name="billing_name" id="billing_name">
</div>
[…]
</div>
select 요소의 항목 그룹화
옵션 그룹이 있는 select 요소의 경우 optgroup 요소를 사용하여 해당 그룹을 표시할 수 있습니다. optgroup 요소의 label 속성은 그룹에 레이블을 제공하는 데 사용됩니다. 이는 관련 옵션이 많은 목록에 특히 유용합니다. 아래 예에서 사용자는 세 가지 코스 중 하나의 강의를 선택할 수 있습니다.
<select>
<optgroup label="8.01 Physics I: Classical Mechanics">
<option value="8.01.1">Lecture 01: Powers of Ten</option>
<option value="8.01.2">Lecture 02: 1D Kinematics</option>
<option value="8.01.3">Lecture 03: Vectors</option>
</optgroup>
<optgroup label="8.02 Physics II: Electricity and Magnestism">
<option value="8.02.1">Lecture 01: What holds our world together?</option>
[…]
</optgroup>
[…]
</select>
폼(form) 지침
사용자가 폼을 작성하고 개별 폼 요소들을 사용하는 방법을 이해하는 데 도움이 되는 지침을 제공하세요. 필수 및 선택 입력, 데이터 형식 및 기타 관련 정보를 표시합니다.
중요 : 스크린 리더는 <form> 요소 내의 콘텐츠를 처리할 때 종종 "폼 모드"로 전환합니다. 이 모드에서는 일반적으로 <input>, <select>, <textarea>, <legend> 및 <label>과 같은 폼 요소만 소리내어 읽습니다. 폼 지침을 소리내어 읽을 수 있는 방식으로 포함시키는 것이 중요합니다. 이에 대해서는 아래에서 자세히 설명합니다.
전체 지침
가능하다면 전체 폼에 적용되는 전반적인 지침을 제공합니다. 예를 들어 필수 및 선택 입력, 허용되는 데이터 형식 및 시간 제한을 표시합니다. 이러한 지침은 <form> 요소 앞에 제공하여 스크린 리더가 '폼 모드'로 전환하기 전에 소리로 읽을 수 있도록 합니다.
아래 예에서는 폼 지침에 필수 필드가 표시되는 방법, 주요 데이터 필드의 예상 형식, 각 입력에 대한 추가 도움말을 찾을 수 있는 위치가 표시되어 있습니다.
- '필수'로 표시된 모든 필드를 작성해야 합니다.
- 날짜는 모두 dd/mm/yyyy 형식으로 입력해야 합니다. (예: 21/07/2013)
- 비밀번호는 최소 8개 이상의 문자 및/또는 숫자를 포함해야 합니다.
- 각 필드 바로 뒤에 추가 도움말이 있습니다. </aside>
인라인 지침
전체 지침 외에도 폼 컨트롤의 레이블 내에 관련 지침을 제공하는 것도 중요합니다. 예를 들어 레이블의 텍스트에 필수 입력 필드와 데이터 형식을 표시하는 것입니다.
레이블 내에 지침 제공
간단한 사용 사례의 경우 레이블 내에 지침을 제공하는 것으로 충분할 수 있습니다. 이 접근 방식은 다양한 웹 브라우저와 보조 기술에서 안정적으로 사용할 수 있지만, 일부 스타일링 요구 사항을 지원하려면 약간의 추가 고려가 필요할 수 있습니다.
아래 예시에서 '만료일'에 필요한 형식은 동일한 라벨 내에서 'MM/YYYY' 로 표시됩니다.
<label for="expire">Expiration date (MM/YYYY): </label>
<input type="text" name="expire" id="expire">
레이블 외부에 지침 제공
레이블 외부에 지침을 제공하면 보다 유연한 위치 지정 및 디자인이 가능하지만 간혹 놓칠 수 있습니다. 또한 일부 웹 브라우저(일반적으로 이전 버전)와 WAI-ARIA를 구현하지 않는 보조 기술에서는 지원되지 않습니다.
접근 방식 1 : aria-labelledby 이용하기
한 가지 접근 방식은 WAI-ARIA aria-labelledby 속성을 사용하여 지침을 폼 요소들과 연결하는 것입니다. 이 튜토리얼을 작성하는 시점에서 이 접근 방식은 모든 웹 브라우저 및 보조 기술 (예: 점자 디스플레이)에서 완벽하게 지원되지는 않습니다. 이전 버전과의 호환성을 보장하기 위해 이 예제에서는 for 및 id 속성도 사용됩니다.
<label id="expLabel" for="expire">Expiration date:</label>
<span>
<input type="text" name="expire" id="expire" aria-labelledby="expLabel expDesc">
<span id="expDesc">MM/YYYY</span>
</span>
접근 방식 2 : aria-describedby 이용하기
추가 지침을 폼 필드와 연결하는 또 다른 접근 방식은 aria-describedby를 사용하는 것입니다. 이 속성에 의해 참조되는 정보는 레이블 및 기타 정보가 제공된 후에 사용자에게 제공됩니다.
<label id="expLabel" for="expire">Expiration date:</label>
<span>
<input type="text" name="expire" id="expire" aria-labelledby="expLabel" aria-describedby="expDesc">
<span id="expDesc">MM/YYYY</span>
</span>
placeholder 텍스트
placeholder 텍스트는 사용자가 아직 편집하지 않은 폼필드 내부에 필요한 데이터 형식에 대한 지침 또는 예시를 제공합니다. placeholder 텍스트는 일반적으로 사용자가 제공한 텍스트보다 낮은 색상 대비로 표시되며, 사용자가 텍스트 입력을 시작하면 폼 필드에서 사라집니다. placeholder 텍스트에 지침 정보나 예시가 포함되어 있다가 사라지는 경우, 사용자가 폼을 제출하기 전에 응답을 확인하기가 더 어려워집니다.
placeholder 텍스트는 많은 사용자에게 유용한 지침을 제공하지만, placeholder 텍스트가 레이블을 대체할 수는 없습니다. 스크린 리더와 같은 보조 기술은 placeholder 텍스트를 레이블로 취급하지 않습니다. 또한 이 튜토리얼을 작성하는 시점에 placeholder 텍스트는 보조 기술 전반에서 광범위하게 지원되지 않으며 구형 웹 브라우저에서는 표시되지 않습니다.
참고
이 튜토리얼의 숨겨진 레이블 섹션에서는 정보를 시각적으로 숨기되 보조 기술에서 계속 사용할 수 있도록 하는 방법에 대해 설명합니다. 이와 동일한 접근 방식을 사용하여 레이블과 placeholder 텍스트가 모두 표시되는 시각적 중복을 피할 수 있습니다. 이렇게 하면 사용자가 양식을 검토하기가 매우 어렵다는 점에 유의하세요. 이 문제를 피하기 위해 JavaScript를 사용하여 레이블이 초점이 있는 입력 위나 옆에 시각적으로(연결된 레이블 요소에) 표시되도록 할 수 있습니다.
<div>
<label for="search">Search:</label>
<input type="text" name="search" id="search" placeholder="e.g. Apple Pie">
</div>
<div>
<label for="email">Email: </label>
<input type="text" name="email" id="email" placeholder="joe@example.com">
</div>
스타일링
이 튜토리얼을 작성할 당시 웹 브라우저는 일반적으로 placeholder 텍스트를 WCAG의 최소 대비 요구 사항을 충족하지 않는 색상으로 표시했습니다. 이는 많은 사람들이 보기 어렵다는 것을 의미합니다. 웹 브라우저는 색상과 불투명도의 조합을 사용하여 이 효과를 얻습니다. 대부분의 웹 브라우저에서 placeholder의 색상은 전용 CSS 선택자를 사용하여 스타일을 지정할 수 있습니다. 다음 코드 스니펫은 요소의 배경이 흰색이라고 가정할 때 색상 대비 요구 사항을 충족하기에 충분한 대비를 가진 밝은 회색으로 색상을 설정합니다.
::placeholder {
color: #767676;
opacity: 1;
}
입력 유효성 검사
지침을 제공하는 것 외에도 사용자 입력의 유효성을 검사하여 사용자의 실수를 방지하세요. HTML5는 이메일 주소, 날짜 등 일반적인 입력 유형의 유효성을 검사하는 다양한 기본 제공 기능을 정의합니다. 사용자 지정 컨트롤의 유효성을 검사하거나 레거시 브라우저를 지원하는 등 일부 상황에서는 사용자 입력의 유효성을 검사하기 위해 추가 스크립팅이 필요할 수 있습니다.
사용자 지정 유효성 검사는 이 튜토리얼의 사용자 알림 부분에 설명된 대로 사용자에게 접근 가능한 방식으로 알려야 합니다. 클라이언트 측 유효성 검사만으로는 보안을 보장할 수 없으므로 서버 측에서도 데이터를 유효성 검사해야 합니다.
required 입력 유효성 검사
폼에는 레이블을 사용하여 명확하게 식별해야 하는 필수 입력이 포함되어 있는 경우가 많습니다. 또한 필수 속성을 프로그래밍 방식으로 폼 컨트롤에 추가하여 필수임을 나타낼 수도 있습니다. 대부분의 최신 웹 브라우저는 이 속성을 지원하며 표준 웹 브라우저 다이얼 로그 메커니즘을 사용하여 누락된 필수 입력을 사용자에게 전달합니다. 이러한 다이얼로그는 기본 font-size, color 및 언어와 같은 웹 브라우저(및 운영 체제)에서 사용자의 설정 및 기본 설정을 존중해야 합니다.
아래 예시에서는 입력 필드에 required 속성이 추가되었습니다. 웹 브라우저가 HTML5를 지원하는 경우 입력 필드에 텍스트를 입력하지 않고는 폼을 제출할 수 없습니다. 대신 웹 브라우저 자체에서 생성된 메시지가 표시됩니다.
보조 기술을 사용하지 않거나 HTML5 required 속성을 지원하지 않는 구형 웹 브라우저를 사용하는 사용자에게 알리기 위해 레이블에 '(필수)'가 표시되기도 합니다.
<label for="name">Name (required): </label>
<input type="text" name="name" id="name" required aria-required="true">
참고
aria-required 속성은 보조 기술에 필요한 컨트롤에 대한 정보를 제공하여 (입력의 유효성을 검사하는 것과는 반대로) 사용자에게 적절하게 알려줍니다. 대부분의 최신 웹 브라우저는 HTML5 required 속성이 존재할 때 자동으로 해당 값을 true로 설정합니다. 이 예제에서는 필수 속성을 보조 기술에 전달하지 않는 웹 브라우저를 지원하기 위해 중복으로 제공됩니다.
공통 입력 유효성 검사
HTML5는 email, url, number, range, date 또는 time 등 다른 데이터에 대한 input type도 제공합니다. 대부분의 최신 웹 브라우저는 이러한 기능을 지원하며 입력 유효성 검사를 처리합니다. 또한 HTML5 유효성 검사는 date picker 및 사용자 지정 온스크린 키보드와 같은 특정 컨트롤을 제공하여 사용자가 데이터를 입력하는 데 도움을 줍니다. HTML5 입력 유형은 이러한 HTML5 기능을 지원하지 않는 구형 웹 브라우저에서는 단순한 텍스트 입력 필드로 표시됩니다.
아래 예는 이러한 HTML5 입력 유형이 실제로 작동하는 모습을 보여줍니다. 웹 브라우저에 따라 'range' 입력 필드는 슬라이더 컨트롤로 표시되어 사용자가 더 쉽게 입력할 수 있습니다. 마찬가지로 'number' 입력 필드에는 숫자를 점진적으로 늘리거나 줄일 수 있는 버튼이 표시될 수 있습니다. 잘못된 이메일 주소와 같은 입력 오류는 이전 예에서와 같이 웹 브라우저 대화 상자를 사용하여 표시됩니다.
<div>
<label for="email">Email: </label>
<input type="email" name="email" id="email">
</div>
<div>
<label for="url">Website: </label>
<input type="url" name="url" id="url">
</div>
<div>
<label for="number">Number: </label>
<input type="number" name="number" id="number" min="0" max="100" step="10" value="0">
</div>
<div>
<label for="range">Range: </label>
<input type="range" name="range" id="range" min="0" max="100" step="10" value="0">
</div>
<div>
<label for="date">Date: </label>
<input type="date" name="date" id="date">
</div>
<div>
<label for="time">Time: </label>
<input type="time" name="time" id="time">
</div>
참고
이러한 HTML5 input type 중 몇 가지에는 입력을 제한하고 유효성을 검사하는 데 도움이 되는 추가 매개변수가 있습니다. 여기에는 다음이 포함됩니다.
- maxlength는 텍스트 필드의 최대 길이를 정의합니다.
- min 및 max는 number 및 range 필드의 최소 및 최대를 정의합니다.
- steps는 숫자 및 범위 필드를 증가 및 감소시킬 수 있는 단계를 정의합니다.
패턴 입력 유효성 검사
HTML5 pattern 속성을 사용하면 정규 표현식을 사용하여 입력에 대한 사용자 지정 형식을 지정할 수 있습니다. 이는 전화번호, 우편번호, 일련번호와 같은 특정 유형의 데이터 패턴에 유용합니다.
예제 1 : 자동차 번호판 번호
아래 예에서 입력 요소의 패턴 속성은 독일의 자동차 번호판(등록) 번호와 일치하는 특정 형식을 지정합니다. 필수 패턴은 1~3개의 문자(자동차가 등록된 도시의 경우), 공백, 2~4개의 임의 문자, 또 다른 공백, 1~4개의 임의 숫자로 구성됩니다.
<div>
<input type="text" id="license"
pattern="[A-ZÖÄÜ]{1,3} [A-Z]{2,4} [0-9]{1,4}"
>
</div>
다양한 input type에 대한 관용
유효성 검사는 특정 데이터 유형에 대해 가능한 한 다양한 input type을 수용하는 것을 목표로 해야 합니다. 예를 들어, 전화번호는 서로 다른 구분 기호 및 숫자 그룹으로 작성됩니다. 여러 표기법을 해석할 수 있다면 폼을 사용하기가 더 쉬워집니다. 또한 자유롭게 입력하는 것이 도움이 됩니다. 예를 들어 우편 번호는 일부 국가에서는 숫자로만 제한되지 않으므로 숫자 유형 입력을 사용하면 많은 웹사이트 사용자에게 문제가 될 수 있습니다.
클라이언트 측 유효성 검사의 이점
일반적으로 클라이언트 측 유효성 검사는 사용자 경험을 개선하고 유효성 검사 오류를 더 쉽게 해결할 수 있게 해줍니다. 또한 네트워크 및 서버 부하를 줄일 수 있습니다. 하지만 모든 웹 브라우저가 HTML5를 지원하는 것은 아니며, 사용자 지정 유효성 검사 스크립트를 지원하지 않을 수도 있습니다. 또한 클라이언트 측 유효성 검사를 쉽게 우회하거나 데이터가 서버에 도달하기 전에 변경될 수 있습니다. 즉, 서버 측에서도 유효성 검사를 수행해야 합니다.
사용자에 의한 유효성 검사
가능하면 사용자가 자신의 입력을 확인하고 필요한 경우 수정할 수 있어야 합니다. 이는 영구적이거나 다른 방식으로 중요한 작업뿐만 아니라 데이터를 자동으로 확인할 수 없는 경우에도 특히 중요합니다. 예를 들어, 사용자가 제공한 우편 주소를 확인할 수 있는 옵션을 제공하면 구매가 완료되기 전에 유용할 수 있습니다.
사용자 확인 필요
가능한 경우 데이터 영구 삭제와 같은 되돌릴 수 없는 작업에 대해 사용자 확인을 요구합니다. 예를 들면 다음과 같습니다.
- CMS를 사용하면 사용자가 휴지통 폴더에서 댓글을 영구적으로 삭제할 수 있습니다. 이 작업이 시작되면 사용자에게 작업을 확인하는 대화 상자가 표시됩니다.
- 뱅킹 애플리케이션에서는 사용자가 '이체하려는 금액이 정확한지 확인했습니다'라는 확인란을 선택하여 이체 거래를 확인하도록 요구합니다.
- 쇼핑 웹사이트에서는 구매 거래가 완료되고 주문이 접수되기 전에 사용자가 확인해야 하는 주문 요약, 배송 주소 및 청구 정보를 표시합니다.
실행 취소 기능 제공
가능한 경우 되돌릴 수 있는 작업을 취소할 수 있는 메커니즘을 제공합니다. 예를 들면 다음과 같습니다.
- 콘텐츠 관리 시스템(CMS)에서 원치 않는 댓글을 삭제할 수 있습니다. 댓글을 바로 삭제하는 대신 '휴지통' 폴더에 저장하여 복원할 수 있도록 합니다.
- 웹메일 애플리케이션을 사용하면 몇 초 동안 이메일 전송을 '취소'할 수 있습니다. 이 기능은 사용자가 파일을 첨부하는 것을 잊었거나 잘못된 수신자에게 이메일을 보낸 경우에 유용합니다.
- 쇼핑 웹사이트에서는 주문이 제출된 후 최대 24시간까지 구매를 취소할 수 있습니다. 웹사이트에서는 정책을 설명하고 사용자에게 이메일로 전송된 구매 영수증에 정책 요약을 포함합니다. 24시간이 지나면 구매 품목이 사용자에게 배송되며 더 이상 취소할 수 없습니다.
사용자 알림
성공 여부와 관계없이 폼 제출 결과에 대한 피드백을 사용자에게 제공합니다. 여기에는 폼 컨트롤 또는 그 근처의 인라인 피드백과 일반적으로 폼 제출 후에 제공되는 전반적인 피드백이 포함됩니다.
알림은 간결하고 명확해야 합니다. 특히 오류 메시지는 이해하기 쉬워야 하며 해결 방법에 대한 간단한 지침을 제공해야 합니다. 성공 메시지 역시 작업 완료를 확인하는 데 중요합니다.
전반적인 피드백
폼이 제출되면 제출이 성공했는지 또는 오류가 발생했는지 여부를 사용자에게 알려주는 것이 중요합니다. 다음 기술 중 몇 가지를 조합할 수 있습니다.
예제 1 : 기본 제목 사용
피드백을 제공하는 일반적인 방법은 웹 페이지의 기본 제목(일반적으로 가장 눈에 잘 띄게 표시되는 <h1> 또는 <h2> 요소)을 사용하는 것입니다. 이 기법은 폼이 서버에서 처리될 때 특히 유용하지만 클라이언트 측 스크립팅에도 유용할 수 있습니다.
/* Error */
<h1>3 Errors – Billing Address</h1>
/* Success */
<h1>Thank you for submitting your order.</h1>
참고
기본 제목의 주된 목적은 여전히 사용자가 현재 있는 웹 페이지를 식별하는 것입니다. 오류로 인해 사용자가 동일한 웹 페이지로 되돌아간 경우 '오류'라는 단어와 오류 횟수를 사용하여 간단하게 표시하는 것이 도움이 됩니다.
예제 2 : 페이지 타이틀 사용
웹 페이지의 <title> 요소는 성공 및 오류를 표시하는 데 유용할 수 있습니다. 특히 스크린 리더 사용자는 웹 페이지가 로드될 때 이 피드백을 즉시 받게 됩니다. 이 요소는 기본 타이틀이 탐색 메뉴 뒤와 같이 콘텐츠 내 더 깊은 곳에 위치할 때 유용할 수 있습니다.
/* Error */
<title>3 Errors – Billing Address</title>
/* Success */
<title>Thank you for submitting your order.</title>
예제 3 : 대화 창(dialog) 사용
대화 창은 사용자에게 변경 사항을 알리는 일반적인 방법입니다. 사용자에게 알리는 다른 수단이 쉽게 놓칠 수 있는 경우 사용할 수 있습니다. 대화 창은 눈에 거슬리고 주의를 집중시키므로 원하는 효과를 얻을 수 있습니다.
자바스크립트는 적절한 키보드 탐색 기능을 제공하고 글꼴 크기, 색상, 언어 등 사용자의 기본 설정을 존중하는 매우 기본적인 경고 대화 창을 제공합니다. 사용자 정의 대화 창 구현은 해당 기능과 일치해야 합니다.
아래의 기본 예는 사용자가 '저장' 버튼을 활성화할 때 표시되는 팝업을 보여줍니다. 대화 창에 메시지가 표시되고 사용자가 "확인"을 선택할 때까지 웹 페이지가 비활성화됩니다.
<button type="button" id="alertconfirm">Save</button>
document.getElementById('alertconfirm')
.addEventListener('click', function() {
/* [… code saving data …] */
alert('Thanks for submitting the form!');
});
예제 4 : 오류 목록
오류가 발생하면 페이지 상단의 폼 앞에 오류를 노출하는 것이 도움이 됩니다. 목록에는 식별하기 쉽도록 고유한 제목이 있어야 합니다. 나열된 각 오류는 다음과 같아야 합니다.
- 사용자가 컨트롤을 인식할 수 있도록 해당 폼 컨트롤의 레이블을 참조합니다.
- 모든 사람이 이해하기 쉬운 방식으로 오류에 대한 간결한 설명을 제공해야 합니다.
- 실수를 수정하는 방법을 표시하고 사용자에게 형식 요구 사항을 상기시킵니다.
- 사용자가 쉽게 액세스할 수 있도록 해당 폼 컨트롤에 대한 페이지 내 링크를 포함합니다.
예를 들어 AJAX 기술을 사용할 때 브라우저에서 새 페이지를 로드하지 않고 폼 오류와 같은 변경 사항을 페이지에 동적으로 표시하는 경우가 있습니다. 이러한 경우 사용자에게 알리기 위해 오류 목록을 눈에 잘 띄는 컨테이너의 상단에 삽입해야 합니다. 위의 조언과 더불어 이 컨테이너에는 보조 기술 사용자가 이 변경 사항을 알 수 있도록 role 속성이 alert로 설정되어 있어야 합니다.
<div role="alert">
<h4>There are 2 errors in this form</h4>
<ul>
<li>
<a href="#firstname" id="firstname_error">
The First name field is empty; it is a required field and must be filled in.
</a>
</li>
<li>
<a href="#birthdate" id="birthdate_error">
The Date field is in the wrong format; it should be similar to 17/09/2013 (use a / to separate day, month, and year).
</a>
</li>
</ul>
</div>
또한 폼 필드는 aria-describedby 속성을 사용하여 해당 오류 메시지와 연결할 수 있습니다.
<label for="firstname">First Name:</label>
<input type="text" id="firstname" aria-describedby="firstname_error">
인라인 피드백
전반적인 피드백 외에도 폼 컨트롤 또는 그 근처에서 보다 구체적인 피드백을 제공하면 사용자가 폼을 사용하는 데 더 큰 도움이 될 수 있습니다. 여기에는 입력 오류뿐만 아니라 올바르게 입력했는지를 알려주는 피드백도 포함됩니다.
일반적으로 메시지와 시각적 단서의 조합을 제공하기 위해 인라인 피드백을 사용합니다. 예를 들어, 올바른 입력은 확인 표시(✓)와 녹색 테두리로 표시하고, 오류는 오류 아이콘(예: 빨간색(✗) 권장 사항 또는 느낌표)과 빨간색 테두리로 표시할 수 있습니다. 오류 메시지에는 실수를 수정하는 방법에 대한 지침도 제공해야 합니다. 이러한 오류 메시지의 개념은 기본적으로 지침 제공과 동일합니다.
접근 방식 1 : 폼 제출 이후
아래 예시는 두 개의 입력 필드가 있는 폼을 보여줍니다. 첫 번째 입력 필드인 'username'은 사용자 아이디를 등록하는 데 사용됩니다. 두 번째 입력 필드인 'expiry date'은 등록이 만료되는 날짜를 입력하는 데 사용됩니다.
폼이 제출되면 입력 내용이 확인되고 사용자에게 피드백이 제공됩니다. 각 입력 필드에 적절한 성공 및 오류 메시지가 표시되어 사용자가 폼을 작성하는 데 도움이 됩니다.
제출된 데이터에 오류가 있는 경우, 오류가 있는 첫 번째 <input> 요소에 초점을 설정하는 것이 편리합니다.
<div class="success">
<label for="username">
<strong>OK:</strong> Username:
</label>
<input type="text" name="username"
id="username" value="spaceteddy13"
aria-describedby="userDesc">
<span id="userDesc">✓</span>
</div>
<div class="error">
<label for="expire">
<strong>Error:</strong>
Expiry date:
</label>
<input type="text" name="expire"
id="expire" value="03.2015"
aria-describedby="expDesc">
<span id="expDesc">Use the format MM/YYYY.</span>
</div>
<div>
<button type="submit">Submit</button>
</div>
.error { color: #900; }
.success { color: #007a00; }
.error input { border: 3px solid #900; }
.success input { border: 3px solid #007a00; }
접근 방식 2 : 입력 중
입력 중 즉각적인 피드백은 매우 유용할 수 있습니다. 예를 들어, 이전 예제에서 사용자 이름의 사용 가능 여부를 확인하려면 사용자가 폼을 여러 번 다시 제출해야 했습니다. 사용자가 입력하는 동안 피드백을 제공하면 적합한 사용자 아이디를 찾을 때까지 더 쉽게 시도할 수 있습니다. 그러나 이러한 기능을 사용하려면 클라이언트 측 스크립팅이 필요하며 모든 상황이 이러한 피드백에 적합한 것은 아닙니다.
예제 1 : 바이너리 메시지
다음 예에서는 사용자가 입력 필드에 텍스트를 입력하는 동안 사용자 이름의 사용 가능 여부가 즉시 확인됩니다. 사용자가 폼을 제출할 필요 없이 입력 값에 따라 성공 및 오류 메시지가 표시됩니다.
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<span id="username_feedback" aria-live="polite"></span>
</div>
document.getElementById('username').addEventListener('keyup', function(){
function setError(el, msg) {
el.parentNode.querySelector('strong').innerHTML = "Error:";
el.parentNode.className='error';
el.parentNode.querySelector('span').innerHTML = msg;
}
function setSuccess(el) {
el.parentNode.querySelector('strong').innerHTML = "OK:";
el.parentNode.className='success';
el.parentNode.querySelector('span').innerHTML = "✓";
}
var val = this.value;
if (val !== "") {
if (taken_usernames.includes(val.trim())) {
setError(this, '✗ Sorry, this username is taken.');
} else {
setSuccess(this, '✓ You can use this username.');
}
} else {
document.getElementById('username_feedback').innerHTML = '';
document.getElementById('username_feedback').parentNode.className = '';
document.querySelector('[for="username"] strong').innerHTML = '';
}
});
참고
이 예제에서 표시된 메시지는 aria-live 속성 값이 polite 인 <span> 요소를 사용하여 코딩 되었습니다. 소위 "라이브 영역(live region)"이라고 하는 이 콘텐츠는 스크린 리더 및 기타 보조 기술에 전달됩니다. "polite" 값은 메시지의 중요성을 덜 강조하며 스크린 리더가 이 메시지를 소리내어 읽기 위해 현재 작업을 중단하지 않도록 합니다. 따라서 사용자가 키 입력을 할 때마다 메시지가 읽히는 것이 아니라 사용자가 입력을 멈출 때 한 번만 읽힙니다.
예제 2 : 확장된 피드백
아래 예는 성공 및 오류 메시지와 더불어 가능한 다양한 유형의 피드백을 보여줍니다. 이 예에서는 사용자가 비밀번호를 입력할 때 비밀번호의 강도를 확인합니다. 피드백은 비밀번호가 얼마나 강력한지 정도를 나타냅니다. 피드백은 색상 적용, '약함', '보통', '강함' 등의 라벨과 정도를 나타내는 간단한 그래픽, 비밀번호를 해독하는 데 필요한 시간 등 여러 가지 단서를 사용하여 표시됩니다.
참고
다음 예제 입력 필드에서는 데이터가 전송되지 않습니다. 비밀번호는 데모용으로 일반 텍스트로 표시됩니다.
비밀번호 입력 예제는 아래 링크에서 확인 가능합니다.
https://www.w3.org/WAI/tutorials/forms/examples/password/
접근 방식 3 : 초첨 변경 시
대부분의 경우 오류 메시지가 표시되므로 사용자가 입력하는 동안 입력을 확인하는 것이 의미가 없는 경우도 있습니다. 날짜와 같은 특정 형식으로 데이터를 입력해야 하는 경우가 이에 해당합니다.
아래 예제에서는 사용자가 만료일을 입력해야 합니다. 사용자가 탭 키를 사용하여 포커스를 다음 필드로 이동하거나 다른 필드를 클릭하는 등 폼 필드를 떠날 때 입력이 확인됩니다.
<div>
<label for="expire"><strong></strong> Expiry date:</label>
<input type="text" name="expire" id="expire" value="03.2015" aria-describedby="expDesc3">
<span id="expDesc3" aria-live="assertive"></span>
</div>
참고
이 예제에서 표시된 메시지는 aria-live 속성의 값이 assertive 인 <span> 요소를 사용하여 코딩 되었습니다. 소위 "라이브 영역(live region)"이라고 하는 이 콘텐츠는 스크린 리더 및 기타 보조 기술에 전달됩니다. "assertive" 값은 메시지의 중요성을 강조하고 스크린 리더가 현재 진행 중인 작업을 중단하고 이 메시지를 소리내어 읽도록 합니다. 따라서 포커스를 받은 다음 요소가 사용자에게 알려지기 전에 메시지가 큰 소리로 읽혀집니다.
여러 페이지로 구성된 폼
가능하면 하나의 긴 폼은 논리적 순서나 단계를 구성하는 여러 개의 작은 폼으로 나누세요. 이렇게 하면 컴퓨터 사용 경험이 적거나 다양한 인지 장애가 있는 사람들이 긴 폼을 덜 어렵고 이해하기 쉽게 만들 수 있습니다.
여러 페이지로 구성된 폼에는 다음과 같은 기본 원칙이 적용되어야 합니다.
- 모든 페이지에서 전체 지침을 반복합니다.
- 컨트롤의 논리적 그룹에 따라 폼을 분할합니다. 예를 들어 온라인 상점에서는 일반적으로 배송 정보와 결제 정보가 분리되어 있습니다.
- 선택적 단계를 쉽게 인식하고 건너뛸 수 있도록 하세요. 예를 들어 웹 페이지의 기본 제목에 선택적 단계를 강조 표시하고 건너뛸 수 있는 옵션을 제공하세요.
- 가능하면 폼 작성에 시간 제한을 설정하지 마세요. 시간 제한이 필요한 경우 사용자가 시간 제한을 조정하거나 연장할 수 있는 기능을 제공하세요.
진행 상황 표시
가능하면 폼의 첫 단계에서 전체 폼이 몇 개의 단계로 되어 있는지 설명해야 합니다. 각 단계는 사용자에게 진행 상황을 알려야 합니다.
접근 방식 1 : 페이지 타이틀 사용
<title> 요소는 스크린 리더 사용자와 같이 많은 사람들이 가장 먼저 읽는 항목입니다. 진행 상황을 포함하도록 페이지 제목을 변경하면 즉각적인 피드백이 제공됩니다. 이 정보는 단계 이름 또는 오류 알림과 같이 제목에 제공된 다른 정보 보다 먼저 표시되어야 합니다.
<title>Step 2 of 4: Shipping Address – Complete Purchase – Galactic Teddy Bears Shop</title>
접근 방식 2 : 주요 헤드라인 사용
주요 헤드라인을 사용하여 사용자에게 정보를 제공하는 것은 일반적으로 문서에서 기본 제목이 눈에 잘 띄기 때문에 페이지를 스캔하는 사람들에게 시각적으로 동일한 정보를 제공할 수 있는 좋은 방법입니다.
<h1>Shipping Address (Step 2 of 4)</h1>
접근 방식 3 : HTML5 progress 요소 사용
HTML5는 사용자에게 진행 상황을 알리는 데 사용할 수 있는 <progress> 요소를 제공합니다. 이는 질문에 대한 답변에 따라 단계를 건너뛰는 설문조사와 같이 사용자 입력에 따라 단계 수가 달라지는 상황에서 특히 유용할 수 있습니다.
Survey <progress max="7" value="1">(Step 1 of circa 7)</progress><br>
Survey <progress max="7" value="2">(Step 2 of circa 7)</progress><br>
[…]
Survey <progress max="7" value="6">(Step 6 of circa 7)</progress><br>
Survey <progress max="7" value="7">(Finished)</progress>
progress 요소는 다른 폼 요소와 마찬가지로 웹사이트에서 직접 렌더링되는 운영 체제의 구성 요소입니다. 이러한 구성 요소는 일부 운영 체제에서 애니메이션으로 표시되므로 사용자가 자동으로 시작되고 5초 이상 지속되며 다른 콘텐츠와 병렬로 표시되는 애니메이션을 비활성화할 수 있어야 한다는 WCAG의 2.2.2 일시 중지, 중지, 숨기기 성공 기준을 위반하게 됩니다.
애니메이션은 아래와 같이 브라우저별 CSS로 사용자 지정 스타일을 사용하여 중지할 수 있습니다.
/* Microsoft IE */
progress {
color: #036;
}
/* Apple Safari and Google Chrome */
progress::-webkit-progress-bar {
background-color: #036;
}
/* Mozilla Firefox */
progress::-moz-progress-bar {
background-color: #036;
}
접근 방식 4 : 단계별 표시기 사용
폼에 완료해야 할 단계 수가 알려진 경우 '단계별 표시기'를 사용하면 사용자가 방향을 잡는 데 도움이 될 수 있습니다. 아래 예에서는 모든 단계에 대해 목록 항목이 있는 정렬된 목록을 사용합니다. 시각적으로 숨겨진 텍스트를 사용하여 현재 단계와 완료된 단계를 표시합니다. 가능하면 이미 완료된 단계에 대한 링크를 제공하여 사용자가 검토할 수 있도록 하세요. 이 경우 현재 단계에 이미 입력된 데이터는 모두 저장되어야 합니다.
<div class="tlwrapper">
<ol class="timeline">
<li class="timeline-past">
<span class="visuallyhidden">Completed: </span>
<a href="billing.html">Billing Address</a>
</li>
<li class="timeline-current">
<span class="visuallyhidden">Current: </span>
<span>Shipping Address</span>
</li>
<li><span>Review Order</span></li>
<li><span>Payment</span></li>
<li><span>Finish Purchase</span></li>
</ol>
</div>
사용자 지정 컨트롤
폼이 있는 페이지를 디자인하다보면 표준 폼 컨트롤로 달성할 수 있는 것 이상의 무언가가 필요한 경우가 있습니다. 이러한 경우 표준 컨트롤에 추가 기능 및 스타일을 구축할 수 있습니다. 견고성을 보장하려면 최종 컨트롤이 처리하려는 기능의 하위 집합을 수행하는 HTML 요소를 재사용하고 거기서부터 빌드하세요. 다음 예제에서는 추가 기능의 접근성을 보장하기 위해 고려해야 할 사항에 대한 몇 가지 일반적인 지침을 제공합니다.
빌드할 적절한 HTML 요소가 없는 컴포넌트의 경우 보조 기술을 사용하는 사람들을 위해 기능을 전달하는 데 WAI-ARIA 속성이 유용할 수 있습니다.
예제 1 : 공유 버튼
아래 예는 두 가지 기능이 있는 소셜 미디어 '공유 버튼'을 보여줍니다. 이미 얼마나 많은 사람들이 버튼을 활성화했는지('공유') 표시하고 사용자가 버튼을 눌러 공유 기능을 활성화할 수 있습니다.
사용자 지정 버튼은 CSS를 사용하여 일반 <button> 요소의 스타일을 지정하므로 CSS 없이 렌더링할 때 기본 기능이 그대로 유지됩니다. 예를 들어, 대부분의 화면 리더는 버튼과 해당 내용을 알립니다.
또한 <form> 요소의 action 속성은 자바스크립트가 지원되지 않는 경우 동일한 기능을 수행하는 서버 측 스크립트를 참조합니다.
<form action="path/to/submit">
<button type="submit" id="share-btn" class="btn-primary">
<span class="count">3</span>
<span class="text">Shares</span>
</button>
</form>
document.getElementById('share-btn').addEventListener('click', function(event){
event.preventDefault();
event.stopImmediatePropagation();
var count = this.querySelector('.count');
var text = this.querySelector('.text');
count.textContent = parseInt(count.textContent) + 1;
text.textContent = "Shared ✓";
this.setAttribute("disabled", "true");
});
예제 2 : 별점
일반적으로 별점은 특정 항목을 평가하는 데 사용할 수 있는 별 5개 이미지로 구성됩니다. 마우스로 별을 가리키고 별을 클릭하면 별을 선택할 수 있습니다. 예를 들어 사용자가 왼쪽에서 세 번째 별을 클릭하면 해당 항목의 평점은 별 5개 중 3개가 됩니다.
가능한 한 쉽게 액세스할 수 있도록 하기 위해 폼은 시각적으로 숨겨지는 필드를 사용합니다.
이 폼에는 각 별에 대해 각 하나의 라디오 버튼, 그리고 기본적으로 선택되어 있는 별 0개에 해당하는 라디오 버튼, 총 6개의 라디오 버튼이 포함되어 있습니다. 라디오 버튼의 레이블에는 실제 텍스트("별 1개", "별 2개", ...)가 포함되며 시각적으로도 숨겨져 있습니다. 또한 폼에는 시각적으로 숨겨진 submit 버튼이 포함되어 있어 키보드 사용자가 라디오 버튼을 탐색할 때 폼이 자동으로 제출되지 않습니다.
별의 이미지는 SVG를 사용하여 생성됩니다. 이 별의 색상을 반영한 애니메이션은 마우스, 키보드 및 기타 입력 방법을 사용하여 활성화할 수 있도록 CSS :focus 및 :hover 가상 클래스를 통해 조정합니다. :checked 가상 클래스 및 일반 형제 선택자는 선택된 별, 활성화된 별, 비활성화된 별을 나타내는 데 사용됩니다.
<form action="#" id="star_rating">
<input value="0" id="star0" checked
type="radio" name="rating" class="visuallyhidden">
<label for="star0">
<span class="visuallyhidden">0 Stars</span>
<svg viewBox="0 0 512 512">
<g stroke-width="70" stroke-linecap="square">
<path d="M91.5,442.5 L409.366489,124.633512"></path>
<path d="M90.9861965,124.986197 L409.184248,443.184248"></path>
</g>
</svg>
</label>
<input value="1" id="star1"
type="radio" name="rating" class="visuallyhidden">
<label for="star1">
<span class="visuallyhidden">1 Star</span>
<svg viewBox="0 0 512 512"><path d="M512 198.525l-176.89-25.704-79.11-160.291-79.108 160.291-176.892 25.704 128 124.769-30.216 176.176 158.216-83.179 158.216 83.179-30.217-176.176 128.001-124.769z"></path></svg>
</label>
<input value="2" id="star2"
type="radio" name="rating" class="visuallyhidden">
<label for="star2">
<span class="visuallyhidden">2 Stars</span>
<svg viewBox="0 0 512 512">…</svg>
</label>
<input value="3" id="star3"
type="radio" name="rating" class="visuallyhidden">
<label for="star3">
<span class="visuallyhidden">3 Stars</span>
<svg viewBox="0 0 512 512">…</svg>
</label>
<input value="4" id="star4"
type="radio" name="rating" class="visuallyhidden">
<label for="star4">
<span class="visuallyhidden">4 Stars</span>
<svg viewBox="0 0 512 512">…</svg>
</label>
<input value="5" id="star5"
type="radio" name="rating" class="visuallyhidden">
<label for="star5">
<span class="visuallyhidden">5 Stars</span>
<svg viewBox="0 0 512 512">…</svg>
</label>
<button type="submit" class="btn-small visuallyhidden focusable">Submit rating</button>
<output></output>
</form>
#star_rating svg {
width: 1em;
height: 1em;
fill: currentColor;
stroke: currentColor;
}
#star_rating label, #star_rating output {
display: block;
float: left;
font-size: 2em;
height: 1.2em;
color: #036;
cursor: pointer;
/* Transparent border-bottom avoids jumping
when a colored border is applied
on :hover/:focus */
border-bottom: 2px solid transparent;
}
#star_rating output {
font-size: 1.5em;
padding: 0 1em;
}
#star_rating input:checked ~ label {
color: #999;
}
#star_rating input:checked + label {
color: #036;
border-bottom-color: #036;
}
#star_rating input:focus + label {
border-bottom-style: dotted;
}
#star_rating:hover input + label {
color: #036;
}
#star_rating input:hover ~ label,
#star_rating input:focus ~ label,
#star_rating input[id="star0"] + label {
color: #999;
}
#star_rating input:hover + label,
#star_rating input:focus + label {
color: #036;
}
#star_rating input[id="star0"]:checked + label {
color: #ff2d21;
}
#star_rating [type="submit"] {
float: none;
}
var radios = document.querySelectorAll('#star_rating input[type=radio]');
var output = document.querySelector('#star_rating output');
var do_something = function(stars) {
// An AJAX request could send the data to the server
output.textContent = stars;
};
// Iterate through all radio buttons and add a click
// event listener to the labels
Array.prototype.forEach.call(radios, function(el, i){
var label = el.nextSibling.nextSibling;
label.addEventListener("click", function(event){
do_something(label.querySelector('span').textContent);
});
});
// If the form gets submitted, do_something
document.querySelector('#star_rating').addEventListener('submit', function(event){
do_something(document.querySelector('#star_rating :checked ~ label span').textContent);
event.preventDefault();
event.stopImmediatePropagation();
});
묶음 글 목록
[A11Y] - 웹접근성 튜토리얼 - 2. 페이지 구조
[A11Y] - 웹접근성 튜토리얼 - 3. 메뉴(네비게이션)
[A11Y] - 웹접근성 튜토리얼 - 6. 폼
이 글은 W3C의 웹접근성 튜토리얼을 번역한 글입니다.
'A11Y' 카테고리의 다른 글
[번역] ARIA aria-label 속성 (0) | 2023.08.07 |
---|---|
[번역] 웹접근성 튜토리얼 - 7. 캐러셀 (0) | 2023.07.06 |
[번역] 웹접근성 튜토리얼 - 5. 테이블(표) (0) | 2023.07.06 |
[번역] 웹접근성 튜토리얼 - 4. 이미지 (0) | 2023.07.06 |
[번역] 웹접근성 튜토리얼 - 3. 메뉴(네비게이션) (0) | 2023.07.06 |