Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 45 — Accessibility in Design Systems

Đã đọc 0/14 mục · còn khoảng 12 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn là designer trong một công ty fintech ở TP.HCM. Đội ngũ có 8 designer, 30 kỹ sư frontend, và một design system dùng chung cho 5 sản phẩm khác nhau. Một ngày nọ, bộ phận pháp chế báo về: ứng dụng bị một tổ chức bảo vệ người khuyết tật khiếu nại vì màn hình đăng nhập không dùng được với screen reader. Bạn sửa nút "Đăng nhập" trong một sản phẩm. Nhưng cái nút đó xuất hiện ở 47 màn hình, trong 5 sản phẩm, được code lại theo 12 cách khác nhau. Bạn sẽ sửa từng cái một trong bao lâu?

Đây chính là lý do bài học hôm nay quan trọng. Ở những bài trước, chúng ta đã học cách làm cho từng thành phần riêng lẻ trở nên accessible: nút bấm, form, modal, tooltip. Nhưng trong thực tế của một tổ chức lớn, accessibility không thể là công việc "sửa từng chỗ" (fix-by-fix). Nó phải được nhúng vào hạ tầng — tức là design system.

Design system là nơi accessibility có đòn bẩy lớn nhất. Khi bạn làm đúng một component một lần trong design system, hàng trăm màn hình dùng lại nó đều được hưởng lợi. Ngược lại, khi bạn để một lỗi accessibility lọt vào component nền tảng, lỗi đó nhân bản ra khắp sản phẩm. Design system vừa là điểm đòn bẩy mạnh nhất, vừa là điểm rủi ro lớn nhất. Bài này dạy bạn cách biến nó thành đòn bẩy.

Khái niệm cốt lõi

Accessibility là ưu tiên ở cấp hệ thống, không phải "add-on"

Nguyên tắc trung tâm của bài này: accessibility phải là thuộc tính của hệ thống, không phải tính năng gắn thêm về sau. Có nghĩa là mỗi component trong design system phải "sinh ra đã accessible" — người dùng lại (designer, developer) không cần phải nhớ thêm gì để dùng đúng.

Điều này khác biệt căn bản với cách làm phổ biến, nơi accessibility là một checklist chạy vào cuối dự án. Khi accessibility ở cấp hệ thống, nó trở thành default an toàn: dùng đúng thì tự động accessible, còn muốn làm sai phải cố tình vượt rào.

Mỗi component đều có tài liệu yêu cầu accessibility

Trong một design system trưởng thành, mỗi component không chỉ có mô tả về màu sắc, kích thước, khoảng cách. Nó còn phải có một mục tài liệu accessibility rõ ràng, trả lời các câu hỏi:

  • Keyboard: Người dùng thao tác component này bằng bàn phím thế nào? Phím nào làm gì? (Ví dụ: với Tabs, mũi tên trái/phải chuyển tab, Home/End nhảy về đầu/cuối.)
  • Screen reader: Component này thông báo gì cho screen reader? Vai trò (role) là gì? Trạng thái (state) nào cần công bố?
  • Focus: Thứ tự focus ra sao? Khi mở/đóng thì focus đi đâu?
  • Contrast: Các trạng thái (mặc định, hover, disabled, error) có đạt tỷ lệ tương phản tối thiểu không?
  • Khi nào KHÔNG dùng: Ví dụ, "Không dùng Tooltip để chứa thông tin thiết yếu vì người dùng touch và keyboard có thể không tiếp cận được."
Tài liệu này không phải để trang trí. Nó là hợp đồng (contract). Khi một developer dùng component Button của bạn, họ được đảm bảo rằng nếu dùng đúng API, nút đó đã accessible. Trách nhiệm accessibility được chuyển từ "mỗi người tự nhớ" sang "hệ thống đã lo".

Design tokens mã hóa accessibility sẵn

Đây là ý tưởng mạnh mẽ nhất của bài. Design tokens là các biến thiết kế nguyên tử — màu sắc, kích thước chữ, khoảng cách — được đặt tên và dùng lại khắp hệ thống. Điểm hay là: bạn có thể mã hóa (encode) các ràng buộc accessibility ngay vào token.

Ví dụ cụ thể về màu. Thay vì có một token blue-500 mơ hồ, bạn thiết kế token theo cặp nền + chữ đã được kiểm tra tương phản trước:

  • text-on-primary + bg-primary → đã đảm bảo tỷ lệ tương phản ≥ 4.5:1 (đạt WCAG AA cho văn bản thường).
  • text-disabled + bg-surface → được thiết kế để vẫn phân biệt được nhưng có ngữ cảnh trạng thái.
Khi designer chọn cặp token này, họ không thể tạo ra tổ hợp màu thất bại về tương phản, vì hệ thống chỉ cho phép các cặp đã validate. Đây gọi là "pit of success" — thiết kế sao cho con đường dễ đi nhất cũng là con đường đúng.

Token còn có thể mã hóa các ràng buộc khác:

  • Kích thước touch target: token size-touch-min = 44px, ép mọi thành phần tương tác đủ lớn cho ngón tay.
  • Focus ring: token focus-ring-width và focus-ring-color được thiết kế đủ tương phản với nền, dùng nhất quán.
  • Motion: token duration-instant để dùng khi người dùng bật prefers-reduced-motion.

Governance: ai giữ cửa cho accessibility

Design system không tự bảo trì. Cần có quy trình quản trị (governance): ai được thêm component mới, tiêu chí nào để một component được "chấp nhận" vào hệ thống. Một tiêu chí bắt buộc phải là: component chưa pass kiểm tra accessibility thì chưa được publish. Accessibility trở thành definition of done của mỗi component.

Tình huống thực tế

Tình huống 1: Ngân hàng số Việt Nam thống nhất design system

Một ngân hàng số tại Hà Nội (giả định, gọi là "NeoBank") có 4 nhóm sản phẩm phát triển độc lập: app khách hàng cá nhân, app doanh nghiệp, portal web, và dashboard nội bộ. Mỗi nhóm tự code component riêng. Khi audit accessibility, họ phát hiện nút "Xác nhận chuyển tiền" có 9 phiên bản code khác nhau — 3 phiên bản dùng thẻ <div> thay vì <button> nên không bấm được bằng bàn phím.

Đội design system quyết định xây một component Button duy nhất, accessible chuẩn: dùng semantic <button>, có focus ring từ token, có trạng thái disabled thông báo đúng cho screen reader, và tài liệu ghi rõ "không bao giờ tạo nút từ <div>". Sau 6 tháng migrate 4 nhóm về dùng component chung, số bug accessibility liên quan đến nút giảm từ 34 xuống còn 2.

Bài học: Sự phân mảnh (fragmentation) là kẻ thù của accessibility. Mỗi lần một component được code lại là một cơ hội để lỗi lọt vào. Design system tập trung hóa chất lượng — sửa một lần, hưởng lợi mọi nơi.

Tình huống 2: Token màu cứu một startup e-commerce khỏi hàng loạt lỗi tương phản

Một startup thương mại điện tử ở Singapore rebrand, đổi màu chủ đạo từ xanh đậm sang một tông cam sáng hơn cho hợp xu hướng. Marketing rất thích màu mới. Nhưng khi designer áp màu cam đó làm nền nút với chữ trắng, tỷ lệ tương phản chỉ đạt 2.1:1 — trượt xa mốc WCAG AA 4.5:1. Vấn đề là màu này đã kịp xuất hiện ở hàng trăm chỗ.

May mắn, họ đã dùng design token. Thay vì đổi màu ở từng màn hình, đội design system chỉ cần điều chỉnh giá trị token bg-primary sang một sắc cam tối hơn một chút (vẫn giữ được tinh thần thương hiệu) và cập nhật text-on-primary. Một thay đổi ở lớp token, toàn bộ sản phẩm cập nhật theo. Họ còn cài thêm bước kiểm tra tự động: mọi cặp token nền–chữ phải pass ngưỡng tương phản trước khi được merge.

Bài học: Token biến accessibility từ việc "sửa hàng trăm chỗ" thành "sửa một chỗ". Và khi bạn validate contrast ở cấp token, bạn ngăn lỗi từ gốc thay vì đuổi bắt nó khắp sản phẩm.

Tình huống 3: Component "chuẩn Figma nhưng sai code" ở một công ty SaaS

Một công ty SaaS Đông Nam Á có design system rất đẹp trong Figma, nhưng khi audit, họ phát hiện component Dropdown trong Figma trông hoàn hảo, còn bản code lại không điều hướng được bằng bàn phím. Nguyên nhân: tài liệu accessibility chỉ tồn tại trong đầu người designer, không được viết ra, nên developer implement theo cảm tính.

Họ khắc phục bằng cách thêm mục "Accessibility spec" vào mỗi trang component trong Figma — ghi rõ role ARIA, hành vi bàn phím, thứ tự focus — và bắt buộc mỗi Pull Request implement component phải đối chiếu với spec đó. Từ đó, khoảng cách giữa "thiết kế trông accessible" và "code thực sự accessible" được thu hẹp.

Bài học: Design system không chỉ là thư viện hình ảnh. Nếu tài liệu accessibility không được viết ra thành spec rõ ràng, nó sẽ thất lạc ở khâu bàn giao giữa design và development.

Hướng dẫn từng bước

Nếu bạn được giao nhiệm vụ nhúng accessibility vào design system, đây là lộ trình thực tế:

Bước 1 — Kiểm kê (audit) các component hiện có. Liệt kê mọi component nền tảng: Button, Input, Select, Modal, Tabs, Tooltip... Với mỗi cái, đánh giá nhanh: keyboard có dùng được không, screen reader thông báo đúng không, contrast có đạt không. Ưu tiên component dùng nhiều nhất (Button, Input, Link thường xuất hiện ở khắp nơi).

Bước 2 — Chuẩn hóa design tokens với ràng buộc accessibility. Thiết lập các cặp token màu nền–chữ đã validate contrast. Định nghĩa token cho touch target tối thiểu, focus ring, và trạng thái reduced-motion. Loại bỏ các token màu "trần" cho phép tạo tổ hợp sai.

Bước 3 — Viết accessibility spec cho từng component. Với mỗi component, tạo một mục tài liệu chuẩn gồm: role/ARIA cần thiết, bản đồ phím bàn phím, hành vi focus, các trạng thái và cách công bố, ví dụ dùng đúng và dùng sai. Đặt tài liệu này ngay cạnh component (trong Storybook, trong Figma) để không ai bỏ lỡ.

Bước 4 — Xây component với accessibility làm mặc định. Code component sao cho dùng đúng API là tự động accessible. Ẩn đi những cách dùng nguy hiểm. Ví dụ, không expose prop cho phép bỏ label; nếu bắt buộc phải có label thì API bắt developer truyền vào.

Bước 5 — Tự động hóa kiểm tra. Cài linter accessibility (như eslint-plugin-jsx-a11y), chạy kiểm tra tự động (axe-core) trong Storybook và CI. Đặt ngưỡng contrast cho token vào bước build. Mục tiêu: lỗi bị chặn trước khi merge, không phải phát hiện sau khi ship.

Bước 6 — Đưa accessibility vào governance. Định nghĩa "definition of done" cho component mới: chưa pass accessibility thì chưa vào hệ thống. Chỉ định người review accessibility. Ghi lại quyết định để đội sau hiểu vì sao component được thiết kế như vậy.

Bước 7 — Đào tạo và lan tỏa. Design system chỉ hiệu quả khi mọi người dùng nó. Tổ chức buổi hướng dẫn cho designer và developer về cách dùng token, cách đọc accessibility spec. Biến việc dùng component chuẩn thành thói quen mặc định.

Lỗi thường gặp & mẹo

Lỗi 1 — Nghĩ rằng "component đẹp trong Figma" là đã accessible. Hình ảnh không phản ánh hành vi. Một dropdown đẹp có thể hoàn toàn không dùng được bằng bàn phím. Mẹo: luôn viết accessibility spec bằng chữ, đừng để nó chỉ tồn tại trong hình.

Lỗi 2 — Token màu quá "trần", cho phép tạo tổ hợp sai. Nếu bạn chỉ có blue-500, gray-100 mà không có cặp nền–chữ đã validate, designer sẽ vô tình ghép màu trượt contrast. Mẹo: thiết kế token theo cặp có ngữ nghĩa (semantic pairs) như text-on-primary, và validate contrast trong build.

Lỗi 3 — Cho phép "override" dễ dàng phá vỡ accessibility. Nếu component cho phép truyền className tùy ý đè lên focus ring hay ẩn label, sớm muộn ai đó sẽ làm hỏng. Mẹo: khóa những phần cốt lõi về accessibility, chỉ mở override ở nơi an toàn.

Lỗi 4 — Accessibility là việc của "một người anh hùng". Khi chỉ một người trong đội lo accessibility, hệ thống sụp đổ khi người đó nghỉ. Mẹo: nhúng accessibility vào quy trình (linter, CI, definition of done) để nó không phụ thuộc vào trí nhớ cá nhân.

Lỗi 5 — Xây component accessible nhưng không migrate sản phẩm cũ. Có component chuẩn mà sản phẩm vẫn dùng code cũ thì vô nghĩa. Mẹo: lập kế hoạch migrate, đo lường tỷ lệ adoption (bao nhiêu % màn hình đã dùng component chuẩn).

Mẹo vàng: Hãy nghĩ về accessibility trong design system như một khoản đầu tư hạ tầng. Chi phí ban đầu cao (viết spec, chuẩn hóa token, tự động hóa), nhưng lợi ích cộng dồn theo mỗi lần component được tái sử dụng.

Bài tập thực hành

Bài tập 1 — Audit một component. Chọn component Button (hoặc Input) trong một design system bạn tiếp cận được (có thể là Material UI, Ant Design, hoặc hệ thống công ty bạn). Trả lời: nó có dùng semantic HTML không? Trạng thái disabled thông báo thế nào cho screen reader? Focus ring có đủ tương phản không? Viết ra 3 điểm mạnh và 3 điểm yếu.

Bài tập 2 — Thiết kế cặp token màu. Cho màu thương hiệu chủ đạo của một dự án bất kỳ, hãy thiết kế 3 cặp token nền–chữ (ví dụ: primary, success, danger). Dùng công cụ kiểm tra tương phản để đảm bảo mỗi cặp đạt ≥ 4.5:1 cho văn bản thường. Ghi lại giá trị hex và tỷ lệ tương phản.

Bài tập 3 — Viết accessibility spec. Chọn một component phức tạp hơn (Tabs hoặc Accordion). Viết một accessibility spec đầy đủ gồm: role ARIA, bản đồ phím bàn phím, hành vi focus khi mở/đóng, các trạng thái cần công bố, và một mục "khi nào không nên dùng". Trình bày như thể developer sẽ đọc nó để implement.

Bài tập 4 — Đề xuất governance. Viết một trang ngắn định nghĩa "definition of done" về accessibility cho component mới trong design system giả định của bạn: cần pass những kiểm tra nào, ai review, và điều gì chặn merge.

Tóm tắt

Bài này chuyển tư duy accessibility từ cấp "từng thành phần" lên cấp "hệ thống". Những điểm cốt lõi cần nhớ:

  • Design system là điểm đòn bẩy lớn nhất của accessibility. Sửa đúng một lần trong component nền tảng, hàng trăm màn hình được hưởng lợi. Ngược lại, một lỗi ở đây nhân bản khắp sản phẩm.
  • Mỗi component phải có tài liệu yêu cầu accessibility — role, keyboard, focus, contrast, khi nào không dùng — như một hợp đồng đảm bảo dùng đúng thì tự động accessible.
  • Design token mã hóa accessibility sẵn. Cặp màu nền–chữ được validate contrast trước, token cho touch target và focus ring, giúp con đường dễ đi nhất cũng là con đường đúng.
  • Accessibility phải là default và là definition of done, được bảo vệ bởi tự động hóa (linter, CI) và governance, không phụ thuộc vào trí nhớ một cá nhân.
  • Phân mảnh là kẻ thù; tập trung hóa qua design system là giải pháp. Nhưng nhớ migrate sản phẩm cũ và đo lường adoption, nếu không component chuẩn chỉ nằm im.
Khi bạn nhúng được accessibility vào chính hạ tầng thiết kế, bạn không còn phải "chiến đấu" với từng lỗi nữa — hệ thống tự làm điều đúng cho bạn. Đó là dấu hiệu của một tổ chức thiết kế trưởng thành.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi