Contrast accessibility cơ bản | Chữ xám, placeholder và checklist UI hàng ngày【2026】
Contrast accessibility cơ bản = đo chữ/nền bằng số, không bằng mắt trên Figma sáng. Mục tiêu hàng ngày là WCAG AA 4.5:1 cho chữ thường. Chữ xám “sang” (gray-400), placeholder thay label, và link pastel là ba lỗi junior hay merge. Đo nhanh bằng công cụ kiểm tra độ tương phản trước khi mở PR.
Bài WCAG sâu về chữ trên ảnh hero dành cho landing và brand. Bài này ngược lại: contrast a11y cơ bản — những cặp màu bạn đụng mỗi ngày trong form, bảng, card, dashboard outsourcing.
Junior Việt thường nhận ticket “làm UI giống Figma”. Figma trên Mac Retina trong văn phòng điều hòa nhìn ổn; khách mở trên điện thoại ngoài đường Nguyễn Huệ thì helper text biến thành sương. Reviewer bảo “chữ mờ” — bạn không biết bắt đầu từ đâu vì không có số.
Ai nên đọc
- Fresher / intern lần đầu nghe “WCAG” trong PR comment
- Freelancer bàn giao admin CRUD, form đăng ký, bảng dữ liệu
- Designer–dev handoff khi token chỉ có
text-mutedmà không ghi contrast
Contrast accessibility là gì (phiên bản 5 phút)
Độ tương phản (contrast ratio) so sánh độ sáng tương đối của hai màu. Công thức WCAG rút gọn: (L1 + 0.05) / (L2 + 0.05). Trắng/đen = 21:1; hai màu giống nhau = 1:1.
Bạn không cần tự tính tay. Cần nhớ ngưỡng quyết định:
| Loại | AA (mục tiêu) | Ghi chú thực tế |
|---|---|---|
| Chữ thường (body, label, error) | ≥ 4.5:1 | Hầu hết product đặt đây làm cổng QA |
| Chữ lớn (≥ ~24px hoặc ~18.66px bold) | ≥ 3:1 | Heading lớn; đừng dùng cho caption 12px |
| UI không phải chữ (viền, icon nghĩa) | ≥ 3:1 | Viền input, focus ring có nghĩa |
AAA 7:1 hữu ích cho nội dung pháp lý / giáo dục, nhưng không phải mục tiêu mặc định của mọi secondary text. Ép AAA trên mọi text-muted thường khiến UI thành đen–trắng cứng.
Ba lỗi contrast junior hay merge
1) Secondary text = gray-400
Tailwind text-gray-400 (#9CA3AF) trên bg-white trông “hierarchy đẹp” trên mock sáng. Số liệu thực tế:
#9CA3AF trên #FFFFFF → ~2.54:1 → Fail AA (chữ thường)
#6B7280 trên #FFFFFF → ~4.83:1 → Pass AA (gray-500)
#4B5563 trên #FFFFFF → ~7.0:1 → Pass AA/AAA gần biên
Quy tắc tay: chữ phụ vẫn phải đọc được như câu đầy đủ — không chỉ “trang trí hierarchy”. Nếu nội dung quan trọng (ngày hết hạn, số dư, trạng thái đơn), dùng shade đạt ≥ 4.5:1.
2) Placeholder thay cho label
<!-- Fail UX + rủi ro a11y -->
<input type="email" placeholder="Email của bạn" />
<!-- Đúng hướng -->
<label for="email">Email</label>
<input id="email" type="email" placeholder="vd: [email protected]" />
Placeholder biến mất khi gõ; màu thường cố ý nhạt. Đừng lấy màu placeholder làm chuẩn cho label. Label Pass AA; placeholder chỉ gợi ý format.
3) Link / CTA pastel theo brand
Brand #60A5FA (blue-400) trên trắng có thể Fail hoặc sát biên tùy ngữ cảnh. Text link dài trong đoạn văn cần đo như chữ thường. Nút filled: đo chữ trên nền nút, không đo logo trên slide marketing.
Case study: form đăng ký 1 buổi chiều
Bối cảnh: Intern làm form đăng ký khóa học. Figma dùng text-slate-400 cho mọi hint.
| Phần tử | Cặp màu ban đầu | Kết quả đo | Sửa |
|---|---|---|---|
| Label “Họ tên” | #94A3B8 / #FFF | ~2.8:1 Fail | #334155 (slate-700) |
| Hint “Như trên CCCD” | #CBD5E1 / #FFF | ~1.6:1 Fail | Gộp vào helper dưới field, #475569 |
| Link “Điều khoản” | #93C5FD / #FFF | ~2.2:1 Fail | #1D4ED8 hoặc underline + màu Pass |
| Error “Bắt buộc” | #FCA5A5 / #FFF | ~2.0:1 Fail | #B91C1C trên nền trắng |
Sau khi đổi token, lead không còn comment “chữ mờ”; QA chỉ còn check logic validate.
Checklist đo trong 10 phút
- Mở trang trên Chrome, DevTools → chọn chữ → xem
colorvàbackground-colorcomputed (cẩn thận nested opacity). - Mở Color Contrast Checker — chọn màu chữ (foreground) và màu nền (background).
- Đọc tỷ lệ và trạng thái AA chữ thường / AA chữ lớn / AAA.
- Với nút: đo riêng default và
:hovernếu đổi màu. - Với dark mode: đo lại trên nền tối — Pass light không tự Pass dark.
Công cụ chạy trên trình duyệt, không cần tài khoản — phù hợp log màu nội bộ và token chưa public.
Ví dụ nhanh trên tool:
Màu chữ: #374151
Màu nền: #FFFFFF
→ Tỷ lệ cao, Pass AA chữ thường
Đổi chữ sang #9CA3AF → Fail ngay — đúng “bẫy gray-400”
Phân biệt nhanh với bài WCAG sâu
| Bài này (cơ bản) | Bài WCAG landing |
|---|---|
| Form, bảng, admin, text UI hàng ngày | Chữ trên ảnh hero / campaign |
| Token gray, placeholder, link, error | Overlay ảnh, brand pastel, AA vs AAA chiến lược |
| Checklist PR 10 phút | Checklist trước launch landing SME |
Đọc thêm khi làm hero: Kiểm tra độ tương phản WCAG AA/AAA.
Lỗi thường gặp sau khi “đã Pass tool”
opacitytrên cả khối: Tool Pass HEX gốc, nhưngopacity: 0.5trên wrapper làm chữ lẫn nền — đo màu sau khi flatten hoặc tránh opacity trên text.rgbaoverlay: Nền hiệu dụng ≠ HEX ảnh gốc. Lấy màu vùng chữ thật.- Anti-alias / font mỏng: 4.51:1 trên Inter 300 có thể vẫn khó đọc — tăng weight hoặc shade đậm hơn một bậc.
- Chỉ test trên màn Mac: Thêm một lần nhìn trên điện thoại ngoài trời trước demo khách.
Checklist dán vào PR (copy được)
- Body / label / error ≥ 4.5:1
- Secondary text quan trọng ≥ 4.5:1 (không dùng gray-400 mặc định)
- Mọi input có
<label>visible; placeholder không thay label - Link trong đoạn văn Pass AA
- Viền input / focus đủ ~3:1 nếu là chỉ báo duy nhất
- Đã đo bằng công cụ contrast và ghi cặp HEX trong PR nếu đổi token
Câu hỏi thường gặp
Chỉ dùng màu hệ thống OS có cần đo không?
Vẫn nên đo token trong design system của bạn. Màu hệ thống không bảo vệ cặp text-muted tự chế.
Disabled button Fail AA có sao không?
Trạng thái disabled thường được chấp nhận mờ hơn; đừng dùng cùng xám đó cho thông báo lỗi hoặc giá tiền.
Có cần axe/lighthouse mỗi PR không?
Nên có trong CI dần. Contrast tay bằng tool vẫn bắt được sớm hơn khi mới chọn màu ở Figma/CSS.