Chào mừng đến với Cafedev – nơi chúng tôi không chỉ chia sẻ kiến thức về công nghệ mà còn đặc biệt quan tâm đến việc tạo ra trải nghiệm truy cập dễ dàng cho mọi người. Trong bài viết này, chúng tôi sẽ khám phá cách tích hợp Vue.js với tính truy cập cao. Vue.js không chỉ là một framework phổ biến cho việc phát triển ứng dụng web hiện đại mà còn có thể được tối ưu hóa để đáp ứng các tiêu chuẩn về tiếp cận. Hãy cùng khám phá cách Vue.js có thể làm cho ứng dụng của bạn trở nên truy cập hơn!

Khả năng tiếp cận web đề cập đến việc tạo ra các trang web có thể được sử dụng bởi bất kỳ ai – có thể là người khuyết tật, web kết nối chậm, phần cứng lỗi thời hoặc hỏng hoặc đơn giản là ai đó trong một môi trường không thuận lợi. Ví dụ, việc thêm phụ đề vào một video sẽ giúp cả người dùng khiếm thính và người dùng có thị lực yếu của bạn cũng như những người dùng đang ở trong một môi trường ồn ào và không thể nghe được điện thoại của họ. Tương tự, đảm bảo văn bản của bạn không quá mất cân bằng sẽ giúp cả người dùng có thị lực yếu và người dùng của bạn đang cố gắng sử dụng điện thoại của họ dưới ánh nắng mặt trời sáng.

Tham khảo thêm hướng dẫn Lập kế hoạch và quản lý về tiếp cận web được cung cấp bởi Tổ chức World Wide Web Consortium (W3C)

1. Bỏ qua các Liên kết

Bạn nên thêm một liên kết ở đầu mỗi trang để trực tiếp đến khu vực nội dung chính để người dùng có thể bỏ qua nội dung được lặp lại trên nhiều trang web.
Thông thường điều này được thực hiện ở đầu của App.vue vì nó sẽ là phần tử có thể tập trung đầu tiên trên tất cả các trang của bạn:

<ul class="skip-links">
  <li>
    <a href="#main" ref="skipLink" class="skip-link">Skip to main content</a>
  </li>
</ul>

Để ẩn liên kết trừ khi nó được tập trung, bạn có thể thêm kiểu sau:

.skip-link {
  white-space: nowrap;
  margin: 1em auto;
  top: 0;
  position: fixed;
  left: 50%;
  margin-left: -72px;
  opacity: 0;
}
.skip-link:focus {
  opacity: 1;
  background-color: white;
  padding: 0.5em;
  border: 1px solid black;
}


Khi người dùng thay đổi route, đưa tập trung trở lại liên kết bỏ qua. Điều này có thể được thực hiện bằng cách gọi tập trung vào tham chiếu mẫu của liên kết bỏ qua (giả sử sử dụng vue-router):

<script>
export default {
  watch: {
    $route() {
      this.$refs.skipLink.focus()
    }
  }
}
</script>
<script setup>
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const skipLink = ref()

watch(
  () => route.path,
  () => {
    skipLink.value.focus()
  }
)
</script>

Tham khảo thêm tài liệu về liên kết bỏ qua tới nội dung chính

2. Cấu trúc Nội dung

Một trong những phần quan trọng nhất của khả năng tiếp cận là đảm bảo rằng thiết kế có thể hỗ trợ việc triển khai tiếp cận. Thiết kế nên xem xét không chỉ độ tương phản màu sắc, lựa chọn font chữ, cỡ chữ và ngôn ngữ, mà còn cách nội dung được cấu trúc trong ứng dụng.

2.1 Tiêu đề

Người dùng có thể điều hướng ứng dụng thông qua các tiêu đề. Có các tiêu đề mô tả cho mỗi phần của ứng dụng của bạn giúp cho người dùng dễ dàng dự đoán nội dung của mỗi phần. Khi đến với tiêu đề, có một số thực hành tiếp cận khuyến nghị:
– Nhúng tiêu đề theo thứ tự của chúng: h1->h6
– Không bỏ qua tiêu đề trong một phần nào đó
– Sử dụng thẻ tiêu đề thực sự thay vì thiết kế văn bản để tạo ra diện mạo của tiêu đề
Đọc thêm về các tiêu đề

<main role="main" aria-labelledby="main-title">
  <h1 id="main-title">Main title</h1>
  <section aria-labelledby="section-title-1">
    <h2 id="section-title-1"> Section Title </h2>
    <h3>Section Subtitle</h3>
    <!-- Content -->
  </section>
  <section aria-labelledby="section-title-2">
    <h2 id="section-title-2"> Section Title </h2>
    <h3>Section Subtitle</h3>
    <!-- Content -->
    <h3>Section Subtitle</h3>
    <!-- Content -->
  </section>
</main>

2.1 Landmarks

Landmarks cung cấp quyền truy cập chương trình vào các phần trong ứng dụng. Người dùng dựa vào công nghệ hỗ trợ có thể điều hướng đến từng phần của ứng dụng và bỏ qua nội dung. Bạn có thể sử dụng Vai trò ARIA để giúp bạn thực hiện điều này.

HTMLVai trò ARIAMục đích của Landmarks
headerrole=”banner”Tiêu đề chính: tiêu đề của trang
navrole=”navigation”Tập hợp các liên kết phù hợp để điều hướng tài liệu hoặc tài liệu liên quan
mainrole=”main”Nội dung chính của tài liệu.
footerrole=”contentinfo”Thông tin về tài liệu: chú thích/bản quyền/liên kết đến tuyên bố quyền riêng tư
asiderole=”complementary” Hỗ trợ nội dung chính, nhưng được tách rời và có ý nghĩa riêng của nó
searchrole=”search”Phần này chứa chức năng tìm kiếm cho ứng dụng
formrole=”form”Tập hợp các phần tử liên quan đến biểu mẫu
sectionrole=”region”Nội dung có liên quan và mà người dùng có lẽ muốn điều hướng đến. Phải cung cấp nhãn cho phần tử này

Tip: Đề xuất sử dụng các phần tử HTML Landmarks với các thuộc tính vai trò dư thừa để tối đa hóa sự tương thích với các trình duyệt cũ không hỗ trợ các phần tử HTML5.
Đọc thêm về các Landmarks

3. Biểu mẫu ngữ nghĩa(form)

Nhãn thường được đặt ở phía trên hoặc bên trái của các trường biểu mẫu(form):

<form action="/dataCollectionLocation" method="post" autocomplete="on">
  <div v-for="item in formItems" :key="item.id" class="form-item">
    <label :for="item.id">{{ item.label }}: </label>
    <input
      :type="item.type"
      :id="item.id"
      :name="item.id"
      v-model="item.value"
    />
  </div>
  <button type="submit">Submit</button>
</form>

Chú ý rằng bạn có thể bao gồm autocomplete='on' trên phần tử biểu mẫu và nó sẽ được áp dụng cho tất cả các trường nhập trong biểu mẫu của bạn. Bạn cũng có thể đặt các giá trị khác nhau cho thuộc tính autocomplete cho mỗi trường nhập.

3.1 Nhãn

Cung cấp nhãn để mô tả mục đích của tất cả các control của biểu mẫu; liên kết forid:

<label for="name">Name: </label>
<input type="text" name="name" id="name" v-model="name" />

Nếu bạn kiểm tra phần tử này trong công cụ phát triển của Chrome và mở tab Tiếp cận bên trong tab Phần tử, bạn sẽ thấy cách đầu vào nhận tên của mình từ nhãn:


Cảnh báo: Mặc dù bạn có thể đã thấy nhãn bao quanh các trường nhập như thế này:

<label>
  Name:
  <input type="text" name="name" id="name" v-model="name" />
</label>

Việc đặt nhãn một cách rõ ràng với một id tương ứng được hỗ trợ tốt hơn bởi công nghệ sau.

aria-label

Bạn cũng có thể đặt tên có thể truy cập cho đầu vào với aria-label.

<label for="name">Name: </label>
<input
  type="text"
  name="name"
  id="name"
  v-model="name"
  :aria-label="nameLabel"
/>

Hãy kiểm tra phần tử này trong Chrome DevTools để xem tên có thể truy cập đã thay đổi như thế nào:

aria-labelledby

Sử dụng aria-labelledby tương tự như aria-label ngoại trừ việc nó được sử dụng nếu văn bản nhãn hiển thị trên màn hình. Nó được kết hợp với các phần tử khác thông qua id của chúng và bạn có thể liên kết nhiều id:

<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
  <h1 id="billing">Billing</h1>
  <div class="form-item">
    <label for="name">Name: </label>
    <input
      type="text"
      name="name"
      id="name"
      v-model="name"
      aria-labelledby="billing name"
    />
  </div>
  <button type="submit">Submit</button>
</form>

aria-describedby

aria-describedby được sử dụng cách tương tự như aria-labelledby ngoại trừ việc cung cấp mô tả với thông tin bổ sung mà người dùng có thể cần. Điều này có thể được sử dụng để mô tả tiêu chí cho bất kỳ đầu vào nào:

<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
  <h1 id="billing">Billing</h1>
  <div class="form-item">
    <label for="name">Full Name: </label>
    <input
      type="text"
      name="name"
      id="name"
      v-model="name"
      aria-labelledby="billing name"
      aria-describedby="nameDescription"
    />
    <p id="nameDescription">Please provide first and last name.</p>
  </div>
  <button type="submit">Submit</button>
</form>

Bạn có thể xem mô tả bằng cách kiểm tra Chrome DevTools:

3.2 Đánh dấu vị trí (Placeholder)

Tránh sử dụng đánh dấu vị trí vì chúng có thể làm rối người dùng.
Một trong những vấn đề với vị trí trống là chúng không đáp ứng được các tiêu chí tương phản màu sắc theo mặc định; sửa đổi tương phản màu sắc làm cho vị trí trống trông giống như dữ liệu được điền sẵn trong các trường nhập. Nhìn vào ví dụ sau, bạn có thể thấy rằng vị trí trống Last Name đáp ứng được tiêu chí tương phản màu sắc trống giống như dữ liệu được điền sẵn:

<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
  <div v-for="item in formItems" :key="item.id" class="form-item">
    <label :for="item.id">{{ item.label }}: </label>
    <input
      type="text"
      :id="item.id"
      :name="item.id"
      v-model="item.value"
      :placeholder="item.placeholder"
    />
  </div>
  <button type="submit">Submit</button>
</form>
/* https://www.w3schools.com/howto/howto_css_placeholder.asp */

#lastName::placeholder {
  /* Chrome, Firefox, Opera, Safari 10.1+ */
  color: black;
  opacity: 1; /* Firefox */
}

#lastName:-ms-input-placeholder {
  /* Internet Explorer 10-11 */
  color: black;
}

#lastName::-ms-input-placeholder {
  /* Microsoft Edge */
  color: black;
}

Tốt nhất là cung cấp tất cả thông tin mà người dùng cần để điền vào biểu mẫu.

3.3 Hướng dẫn

Khi thêm hướng dẫn cho các trường nhập của bạn, hãy đảm bảo liên kết nó đúng với trường nhập. Bạn có thể cung cấp hướng dẫn bổ sung và gắn nhiều ids bên trong một aria-labelledby. Điều này cho phép thiết kế linh hoạt hơn.

<fieldset>
  <legend>Using aria-labelledby</legend>
  <label id="date-label" for="date">Current Date: </label>
  <input
    type="date"
    name="date"
    id="date"
    aria-labelledby="date-label date-instructions"
  />
  <p id="date-instructions">MM/DD/YYYY</p>
</fieldset>

Hoặc, bạn có thể gắn hướng dẫn vào trường nhập với aria-describedby:

<fieldset>
  <legend>Using aria-describedby</legend>
  <label id="dob" for="dob">Date of Birth: </label>
  <input type="date" name="dob" id="dob" aria-describedby="dob-instructions" />
  <p id="dob-instructions">MM/DD/YYYY</p>
</fieldset>

3.4 Ẩn Nội dung

Thông thường, không khuyến khích ẩn nhãn trực quan, ngay cả khi trường nhập có tên có thể truy cập được. Tuy nhiên, nếu chức năng của trường nhập có thể hiểu được với nội dung xung quanh, chúng ta có thể ẩn nhãn trực quan.
Hãy xem xét trường tìm kiếm này:

<form role="search">
  <label for="search" class="hidden-visually">Search: </label>
  <input type="text" name="search" id="search" v-model="search" />
  <button type="submit">Search</button>
</form>

Chúng ta có thể làm điều này vì nút tìm kiếm sẽ giúp người dùng trực quan xác định mục đích của trường nhập.
Chúng ta có thể sử dụng CSS để ẩn trực quan các phần tử nhưng vẫn giữ chúng sẵn có cho công nghệ hỗ trợ:

.hidden-visually {
  position: absolute;
  overflow: hidden;
  white-space: nowrap;
  margin: 0;
  padding: 0;
  height: 1px;
  width: 1px;
  clip: rect(0 0 0 0);
  clip-path: inset(100%);
}

aria-hidden="true"

Thêm aria-hidden="true" sẽ ẩn phần tử khỏi công nghệ hỗ trợ nhưng vẫn giữ nó hiển thị cho người dùng khác. Đừng sử dụng nó trên các phần tử có thể chú ý, chỉ trên nội dung trang trí, trùng lặp hoặc nằm ngoài màn hình.

<p>This is not hidden from screen readers.</p>
<p aria-hidden="true">This is hidden from screen readers.</p>

3.5 Nút

Khi sử dụng nút trong một biểu mẫu, bạn phải đặt loại để ngăn việc gửi biểu mẫu. Bạn cũng có thể sử dụng một trường nhập để tạo nút.

<form action="/dataCollectionLocation" method="post" autocomplete="on">
  <!-- Buttons -->
  <button type="button">Cancel</button>
  <button type="submit">Submit</button>

  <!-- Input buttons -->
  <input type="button" value="Cancel" />
  <input type="submit" value="Submit" />
</form>

3.6 Chức năng với Hình ảnh

Bạn có thể sử dụng kỹ thuật này để tạo ra các chức năng với hình ảnh.
– Trường nhập(input)

  • Những hình ảnh này sẽ hoạt động như một nút gửi trên biểu mẫu
<form role="search">
  <label for="search" class="hidden-visually">Search: </label>
  <input type="text" name="search" id="search" v-model="search" />
  <input
    type="image"
    class="btnImg"
    src="https://img.icons8.com/search"
    alt="Search"
  />
</form>
  • Biểu tượng (icon)
<form role="search">
  <label for="searchIcon" class="hidden-visually">Search: </label>
  <input type="text" name="searchIcon" id="searchIcon" v-model="searchIcon" />
  <button type="submit">
    <i class="fas fa-search" aria-hidden="true"></i>
    <span class="hidden-visually">Search</span>
  </button>
</form>

Chúng tôi hy vọng rằng qua bài viết này, bạn đã có cái nhìn tổng quan về cách tích hợp Vue.js với tính truy cập cao, giúp tạo ra những ứng dụng web mang tính phản ứng và dễ tiếp cận hơn. Tại Cafedev, chúng tôi luôn tận tâm chia sẻ những kiến thức mới nhất và đảm bảo rằng mọi người đều có cơ hội học hỏi và phát triển. Hãy tiếp tục theo dõi chúng tôi để không bỏ lỡ những bài viết hữu ích khác về công nghệ và tiếp cận. Cảm ơn bạn đã đọc và ủng hộ Cafedev!

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!