Hướng dẫn cho lập trình viên về nhãn ARIA thực sự hữu ích
Nhãn ARIA không phải là một lớp hỗ trợ tiếp cận thần kỳ. Dùng đúng, chúng giúp các điều khiển dễ hiểu hơn. Dùng tùy tiện, chúng che khuất văn bản hữu ích và tạo ra giao diện gây bối rối.
Mục lục
- Nhãn ARIA dùng để đặt tên, không phải để chữa cháy
- Tên hỗ trợ tiếp cận, nói một cách dễ hiểu
- Quy tắc đầu tiên: ưu tiên HTML gốc và nhãn hiển thị
- Khi `aria-label` là công cụ phù hợp
- Khi `aria-label` là công cụ sai
- Hãy dùng `aria-labelledby` khi văn bản hiển thị đã tồn tại
- Dùng `aria-describedby` cho văn bản trợ giúp, không phải cho tên
- Điều khiển lặp lại cần tên riêng biệt
- Đừng gắn nhãn cho mọi thứ
- Kiểm tra tên được tính toán, không chỉ kiểm tra mã
- Checklist review thực tế
- Kỷ luật thầm lặng của ARIA tốt
Nhãn ARIA dùng để đặt tên, không phải để chữa cháy
ARIA hữu ích, nhưng thường bị dùng như một miếng vá cho HTML chưa rõ ràng. Đó là lúc các đội ngũ dễ gặp rắc rối.
Ví dụ phổ biến nhất là aria-label. Nó trông vô hại: thêm một chuỗi, làm hài lòng linter, rồi tiếp tục. Nhưng tên hỗ trợ tiếp cận không phải là phần trang trí. Đó là tên mà nhiều công nghệ hỗ trợ dùng để trình bày cho người dùng khi họ điều hướng qua nút, liên kết, trường biểu mẫu, tiêu đề, landmark và điều khiển.
Nếu tên đó mơ hồ, trùng lặp, lỗi thời hoặc khác với nhãn hiển thị, giao diện sẽ khó dùng hơn. Đôi khi còn tệ hơn: aria-label có thể ghi đè văn bản tốt hơn vốn đã có trong DOM.
Mục tiêu không phải là thêm nhiều ARIA hơn. Mục tiêu là làm cho tên, vai trò, trạng thái và mục đích của từng phần tử giao diện trở nên rõ ràng.
Tên hỗ trợ tiếp cận, nói một cách dễ hiểu
Hầu hết các phần tử tương tác đều có tên hỗ trợ tiếp cận. Trình đọc màn hình dùng tên đó để thông báo phần tử là gì.
Ví dụ:
<button>Save changes</button>
Trình đọc màn hình có thể thông báo đại loại: “Save changes, button.” Vai trò đến từ phần tử button gốc. Tên đến từ văn bản bên trong nó.
Đó là trường hợp lý tưởng: văn bản hiển thị và tên hỗ trợ tiếp cận khớp nhau.
Các thuộc tính gắn nhãn ARIA trở nên hữu ích khi giao diện hiển thị không cung cấp một tên đầy đủ, hoặc khi tên phải lấy từ một phần tử khác. Các thuộc tính chính là:
aria-label: cung cấp một chuỗi trực tiếp trên phần tử.aria-labelledby: trỏ đến một hoặc nhiều phần tử có văn bản trở thành tên.aria-describedby: trỏ đến văn bản mô tả bổ trợ, không phải tên chính.
Ba thuộc tính này có liên quan, nhưng không thể thay thế cho nhau.
Quy tắc đầu tiên: ưu tiên HTML gốc và nhãn hiển thị
Nếu bạn có thể đặt văn bản hiển thị trên điều khiển, hãy làm việc đó trước.
Cách này tốt hơn:
<button>Delete invoice</button>
So với cách này:
<button aria-label="Delete invoice">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
Mẫu thứ hai hợp lệ cho một nút chỉ có biểu tượng. Nhưng nếu thiết kế có thể chấp nhận văn bản hiển thị, văn bản hiển thị sẽ giúp tất cả mọi người: người dùng trình đọc màn hình, người dùng nhận dạng giọng nói, người đang chịu tải nhận thức, người quét nhanh nội dung và người dùng công cụ dịch.
Đây là một chủ đề lặp lại trong công việc hỗ trợ tiếp cận. HTML gốc và các dấu hiệu hiển thị giải quyết được nhiều vấn đề hơn siêu dữ liệu ẩn. Nguyên tắc tương tự cũng áp dụng rộng hơn cho ngữ nghĩa của nút; nếu đội ngũ của bạn đang rà soát các điều khiển UI, checklist cho nút web hỗ trợ tiếp cận của chúng tôi là tài liệu đi kèm phù hợp với hướng dẫn này.
Khi aria-label là công cụ phù hợp
Dùng aria-label khi một phần tử cần tên hỗ trợ tiếp cận và không có văn bản hiển thị phù hợp để tham chiếu.
Trường hợp kinh điển là nút chỉ có biểu tượng:
<button aria-label="Search">
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<!-- icon -->
</svg>
</button>
Cách này hợp lý. Biểu tượng hiển thị gợi ý chức năng tìm kiếm, nhưng đường dẫn SVG tự nó không cung cấp một tên đáng tin cậy. aria-label bổ sung tên đó.
Các trường hợp tốt khác gồm:
- Nút đóng chỉ được biểu diễn bằng dấu “X”.
- Landmark điều hướng cần một tên cụ thể hơn, chẳng hạn
aria-label="Product". - Một điều khiển lặp lại mà ngữ cảnh hiển thị không nằm trong văn bản của nút.
Ví dụ:
<nav aria-label="Primary">
...
</nav>
<nav aria-label="Footer">
...
</nav>
Cả hai đều là landmark điều hướng, nhưng nhãn của chúng giúp người dùng phân biệt khi di chuyển theo landmark.
Khi aria-label là công cụ sai
Đừng thêm aria-label chỉ vì một bài kiểm tra nói rằng phần tử cần nhãn. Hãy sửa markup trước.
Không tốt:
<div role="button" tabindex="0" aria-label="Submit">Submit</div>
Tốt hơn:
<button>Submit</button>
Ví dụ đầu tiên tạo thêm công việc không cần thiết. Bây giờ bạn phải tái tạo hành vi bàn phím, trạng thái vô hiệu hóa, hành vi biểu mẫu và những kỳ vọng mà nút gốc đã cung cấp sẵn.
Cũng nên tránh dùng aria-label để đổi tên văn bản hiển thị theo cách làm thay đổi ý nghĩa.
<button aria-label="Delete invoice">Remove</button>
Điều này trông nhỏ, nhưng có thể gây bối rối cho người dùng dựa vào nhập liệu bằng giọng nói. Nếu một nút hiển thị ghi “Remove,” nhưng tên hỗ trợ tiếp cận là “Delete invoice,” người dùng cố nói “click Remove” có thể không nhận được kết quả mong đợi. Yêu cầu “label in name” của WCAG tồn tại chính vì lý do này: văn bản hiển thị nói chung nên được chứa trong tên hỗ trợ tiếp cận.
Phiên bản tốt hơn:
<button aria-label="Remove invoice">Remove</button>
Thường còn tốt hơn nữa:
<button>Remove invoice</button>
Hãy dùng aria-labelledby khi văn bản hiển thị đã tồn tại
Nếu văn bản nhãn đã có trên trang, aria-labelledby thường tốt hơn aria-label.
Ví dụ:
<h2 id="billing-title">Billing address</h2>
<section aria-labelledby="billing-title">
...
</section>
Tên hỗ trợ tiếp cận của section lúc này đến từ tiêu đề hiển thị. Bạn tránh lặp lại chuỗi, nhờ đó giảm lỗi dịch thuật và nhãn lỗi thời.
Điều này đặc biệt hữu ích cho các nhóm biểu mẫu:
<fieldset aria-labelledby="shipping-speed-title">
<legend id="shipping-speed-title">Shipping speed</legend>
<label>
<input type="radio" name="shipping" value="standard">
Standard
</label>
<label>
<input type="radio" name="shipping" value="express">
Express
</label>
</fieldset>
Trong nhiều trường hợp, legend gốc là đủ mà không cần ARIA. Ý chính là nhãn hiển thị nên dẫn dắt. ARIA nên kết nối ý nghĩa đã có, không tạo ra một phiên bản riêng tư thứ hai của ý nghĩa đó.
Dùng aria-describedby cho văn bản trợ giúp, không phải cho tên
Mô tả không phải là nhãn.
Xem trường này:
<label for="password">Password</label>
<input id="password" type="password" aria-describedby="password-help">
<p id="password-help">Use at least 12 characters.</p>
Tên hỗ trợ tiếp cận là “Password.” Mô tả là “Use at least 12 characters.” Trình đọc màn hình có thể thông báo cả hai, nhưng chúng phục vụ những mục đích khác nhau.
Đừng làm thế này:
<input type="password" aria-label="Use at least 12 characters">
Cách đó đặt tên trường theo hướng dẫn, không phải theo khái niệm. Người dùng điều hướng một biểu mẫu muốn biết trường đó là gì trước, rồi mới đến các ràng buộc áp dụng.
Sự phân biệt này cũng quan trọng trong trạng thái lỗi:
<label for="email">Email</label>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
>
<p id="email-error">Enter an email address in the format [email protected].</p>
Nhãn vẫn ổn định. Thông báo lỗi trở thành ngữ cảnh bổ trợ.
Điều khiển lặp lại cần tên riêng biệt
Danh sách và thẻ là nơi nhãn ARIA thường trở nên cần thiết.
Không tốt:
<button>Delete</button>
<button>Delete</button>
<button>Delete</button>
Người dùng trình đọc màn hình điều hướng theo nút có thể nghe “Delete, button” ba lần mà không có ngữ cảnh.
Tốt:
<button aria-label="Delete report: Q4 revenue">Delete</button>
<button aria-label="Delete report: Hiring plan">Delete</button>
<button aria-label="Delete report: Vendor list">Delete</button>
Đây là cách dùng aria-label hợp lệ: văn bản hiển thị vẫn ngắn gọn, còn tên hỗ trợ tiếp cận bao gồm đối tượng.
Nhưng hãy dùng mẫu này cẩn thận. Nếu tên đối tượng hiển thị ở gần đó, aria-labelledby có thể dễ bảo trì hơn:
<article>
<h3 id="report-q4">Q4 revenue</h3>
<button aria-labelledby="delete-q4 report-q4" id="delete-q4">Delete</button>
</article>
Tên hỗ trợ tiếp cận trở thành “Delete Q4 revenue.” Cách này tránh lặp lại tiêu đề báo cáo trong một thuộc tính.
Đừng gắn nhãn cho mọi thứ
Không phải phần tử nào cũng cần nhãn ARIA.
Văn bản tĩnh thường không cần. Biểu tượng trang trí không cần. Container không cần, trừ khi chúng có landmark hoặc vai trò widget có ý nghĩa. Gắn nhãn quá mức có thể làm trang trở nên ồn ào và khó điều hướng hơn.
Với hình ảnh, hãy dùng mô hình dành riêng cho hình ảnh: ảnh có ý nghĩa cần alt hữu ích; ảnh trang trí cần alt="" rỗng. Đừng dùng nhãn ARIA để thay thế văn bản ảnh tốt. Nếu đội ngũ của bạn đang trộn lẫn các khái niệm đó, hãy xem lại văn bản alt thực dụng cho hình ảnh và tách phần thay thế hình ảnh khỏi tên điều khiển.
Một lỗi phổ biến là gắn aria-label cho mọi SVG. Nếu SVG nằm bên trong một nút và nút đã có tên, biểu tượng thường nên được ẩn khỏi công nghệ hỗ trợ:
<button aria-label="Open menu">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
Nếu không, người dùng có thể nghe các thông báo dư thừa hoặc kỳ lạ tùy theo tổ hợp trình duyệt và công nghệ hỗ trợ.
Kiểm tra tên được tính toán, không chỉ kiểm tra mã
Lỗi hỗ trợ tiếp cận thường vượt qua code review vì markup trông có vẻ hợp lý.
Các công cụ dành cho nhà phát triển trong trình duyệt hiện đại có thể hiển thị cây hỗ trợ tiếp cận được tính toán. Trong Chrome, Edge, Firefox và Safari, hãy inspect phần tử và tìm thông tin hỗ trợ tiếp cận như vai trò, tên và mô tả. Bạn đang kiểm tra ba điều:
- Vai trò có đúng như bạn mong đợi không?
- Tên hỗ trợ tiếp cận có rõ ràng và cụ thể không?
- Mô tả có hữu ích mà không thay thế tên không?
Sau đó kiểm thử một vài luồng bằng trình đọc màn hình thật. Bạn không cần trở thành chuyên gia công nghệ hỗ trợ toàn thời gian để bắt được những vấn đề cơ bản. Trên macOS, VoiceOver được tích hợp sẵn. Trên Windows, NVDA được dùng rộng rãi và miễn phí. Trên thiết bị di động, hãy kiểm thử với VoiceOver trên iOS và TalkBack trên Android khi phù hợp.
Công cụ tự động rất hữu ích, nhưng chúng không thể đáng tin cậy cho biết liệu “Open,” “Read more,” hay “Delete” đã đủ ngữ cảnh hay chưa. Hãy xem tự động hóa như một tấm lưới, không phải một vị thẩm phán. Điều này tương tự kiểm tra hiệu năng: một báo cáo có thể chỉ ra các khu vực đáng nghi, nhưng bạn vẫn cần diễn giải tác động. Cách tiếp cận bình tĩnh mà chúng tôi khuyến nghị khi đọc báo cáo Lighthouse mà không hoảng cũng áp dụng ở đây.
Checklist review thực tế
Trước khi phát hành nhãn ARIA, hãy hỏi:
- Có thể dùng HTML gốc thay cho việc này không?
- Có văn bản hiển thị nào nên được dùng làm nhãn không?
- Nếu có văn bản hiển thị, tên hỗ trợ tiếp cận có bao gồm văn bản đó không?
- Các điều khiển lặp lại có đủ riêng biệt khi được điều hướng ngoài ngữ cảnh trực quan không?
- Văn bản trợ giúp có được kết nối bằng
aria-describedby, thay vì bị nhồi vào nhãn không? - Biểu tượng trang trí có được ẩn khỏi công nghệ hỗ trợ không?
- Đã có ai kiểm tra tên hỗ trợ tiếp cận được tính toán trong công cụ dev của trình duyệt chưa?
- Đã có ít nhất một lượt kiểm thử bằng trình đọc màn hình thật cho luồng quan trọng chưa?
Checklist này bắt được hầu hết vấn đề về nhãn trước khi chúng trở thành vấn đề của người dùng.
Kỷ luật thầm lặng của ARIA tốt
Làm ARIA tốt hiếm khi kịch tính. Phần lớn là sự tiết chế.
Dùng nút thật. Dùng nhãn thật. Giữ tên hiển thị và tên hỗ trợ tiếp cận thống nhất. Chỉ thêm aria-label khi không có nguồn hiển thị nào tốt hơn. Dùng aria-labelledby khi trang đã có đúng văn bản. Dùng aria-describedby cho hướng dẫn bổ trợ và lỗi.
Nền tảng web cho lập trình viên rất nhiều thứ miễn phí khi chúng ta dùng nó trực tiếp. ARIA tồn tại để lấp các khoảng trống. Kỹ năng nằm ở việc biết khi nào thật sự có khoảng trống.