Yêu cầu với lập trình viên
Chuẩn UI trên mobile
Phân biệt chuẩn với pattern, đặt ngưỡng đo được cho touch target, contrast, font scale và scroll, rồi gắn chúng vào acceptance criteria, PR và DoD.
Nội dung bài
Thuật ngữ trong bài (24)
- accessibility · khả năng tiếp cận
- bottom sheet · tấm trượt từ đáy
- chrome · khung app
- contrast ratio · tỷ lệ tương phản
- dark mode · chế độ tối
- dialog · hộp thoại
- font scale · mức phóng chữ
- HIG · hướng dẫn giao diện của Apple
- Material Design · hệ thiết kế của Google
- safe area · vùng chừa
- tab bar · thanh điều hướng đáy
- top bar · thanh tiêu đề
- touch target · vùng chạm
- WCAG · bộ hướng dẫn nội dung tiếp cận được
- acceptance criteria · tiêu chí nghiệm thu
- definition of done · định nghĩa hoàn thành
- quality gate · cổng chất lượng
- pull request · yêu cầu gộp mã
- bug · lỗi
- device matrix · ma trận thiết bị
- regression · lỗi tái phát
- release · bản phát hành
- low-end · máy cấu hình thấp
- OTP · mã dùng một lần
48dp
touch target nhỏ nhất
4.5:1
contrast chữ thường
200%
mức phóng chữ phải chịu được
60fps
ngân sách 16,6ms mỗi frame
Ba thứ hay bị gộp làm một
Phần lớn tranh cãi về UI kéo dài vì hai người đang nói về hai loại phát biểu khác nhau mà tưởng là một. Tách ra thì hết cãi:
Chuẩn
- Đo được bằng một con số hoặc một phép thử
- Fail được — có trạng thái "không đạt" rõ ràng
- Không phụ thuộc ai đang review
- Ví dụ: target ≥ 48dp, contrast ≥ 4.5:1
Pattern
- Cách làm có sẵn, nền tảng đã đặt tên
- Là một đường đạt chuẩn, không phải đường duy nhất
- Chọn hay không là quyết định thiết kế
- Ví dụ: top bar co lại khi vuốt, bottom sheet
Sở thích
- Không có phép thử nào bác bỏ được
- Đổi theo người, theo tuần, theo tâm trạng
- Không được vào acceptance criteria
- Ví dụ: "trông hiện đại hơn", "giống app X"
Câu hỏi mẫu: header co lại khi vuốt có phải yêu cầu không
Đây là câu hỏi thật, và cách trả lời nó là khuôn để trả lời mọi câu hỏi cùng loại.
- "Header co lại khi vuốt"
một phát biểu về UI
- Nó là chuẩn, pattern hay sở thích?
pattern — Apple và Google đều có sẵn
- Vậy chuẩn đằng sau nó là gì?
chrome không được nuốt nội dung
- Viết cái đó vào requirement
pattern để dev chọn
Pattern này là hàng chính chủ, không phải phát minh của ai:
iOS
Large title thu vào navigation bar khi scroll, có trong HIG từ iOS 11. Bản mới đặt title nằm trong scroll view và trôi dưới bar.
Android
Material 3 có medium và large top app bar kèm scroll behavior — pinned, enter-always-collapsed, exit-until-collapsed.
Nhưng không chuẩn nào bắt buộc nó. Không có điều khoản nào trong HIG, Material hay WCAG nói header phải co. Dùng thì đúng quy ước nền tảng; không dùng mà chrome đã đủ nhỏ thì cũng không sai.
Cái đáng viết vào requirement là kết quả:
Khi user đang đọc, chrome chiếm không quá ~10% chiều cao màn
Tỷ lệ nội dung trên chrome đo được bằng ảnh chụp màn hình, không cần bàn cãi.
Không thao tác nào chỉ tồn tại ở trạng thái mở rộng
Nút nằm trong phần bị co mất là bug, không phải thiết kế.
Không giật qua lại khi vuốt nhẹ
Phải có ngưỡng trễ. Header nhấp nháy theo từng cử động ngón tay gây khó chịu, và gây chóng mặt thật ở người nhạy tiền đình.
Tôn trọng tuỳ chọn giảm chuyển động của hệ điều hành
Bật thì đổi trạng thái tức thì, không animate.
Ô đang focus không bị chrome che
Trượt tới một control gần đỉnh mà nó nằm khuất dưới header là lỗi accessibility, không phải tiểu tiết.
Trạng thái co khôi phục đúng sau khi xoay màn hoặc quay lại từ màn khác
Ghép với nhóm lifecycle ở Kiểm thử đặc thù mobile.
Và một cảnh báo ngược chiều — không phải thứ gì ẩn đi cũng tốt:
Top bar co lại — nên
- Phần bị giấu chỉ là tiêu đề, thứ user đã biết
- Thao tác chính vẫn còn ở dạng icon
- Đổi lại được nhiều dòng nội dung ở màn nhỏ
- Nền tảng đã có sẵn, không phải tự chế
Bottom nav tự ẩn — thường không nên
- Vùng dưới màn tap chính xác ~96%, vùng trên ~61%
- Nav luôn hiện giúp điều hướng nhanh hơn ~22%
- Ẩn nav là mất khả năng khám phá, đổi lại vài chục pixel
- Vuốt lên để gọi nav về là một bước thừa mỗi lần chuyển mục
Tầng 1 — Sáu nguyên tắc chung
Ít, và khó cãi. Nguyên tắc nào cãi được thì nó là sở thích, bỏ ra.
1.Chrome phục vụ nội dung, không ngược lại
Mỗi pixel dành cho header, tab bar, banner là một pixel lấy khỏi thứ user mở app để xem. Không cấm chrome — bắt chrome phải trả lời được nó đổi lấy gì.
2.Thao tác chính nằm trong tầm ngón cái
Màn hình lớn dần, phần trên cùng ngày càng khó với tới bằng một tay.
3.Nền tảng nào theo quy ước nền tảng đó
Nút back, cử chỉ vuốt, vị trí nút xác nhận trong dialog, kiểu date picker — bê nguyên iOS sang Android là tiết kiệm công cho đội và tính phí lên user. Alliance App chạy trên cả hai, nên đây là quyết định phải nói rõ ở từng màn, không để mặc định.
4.Mỗi màn có đủ năm trạng thái
Loading, rỗng, lỗi, mất mạng, đủ dữ liệu. Thiếu một cái là một màn trắng ngoài production.
5.Thao tác phá huỷ phải rút lại được
Hoàn tác tốt hơn hộp xác nhận. Không làm được hoàn tác thì mới hỏi.
6.Màu không phải kênh thông tin duy nhất
Trạng thái phân biệt bằng màu thì phải kèm chữ hoặc icon.
Tầng 2 — Ngưỡng cụ thể
Đây là phần fail được. Con số lấy từ chuẩn công khai; chỗ nào Alliance App đặt cao hơn thì ghi rõ lý do.
| Hạng mục | Ngưỡng | Nguồn | Kiểm bằng |
|---|---|---|---|
| Touch target | ≥ 48dp (Android), ≥ 44pt (iOS) | Material, HIG | Layout inspector. WCAG chỉ đòi 24×24px — quá thấp cho app dùng cả ngày |
| Khoảng cách giữa hai target | ≥ 8dp | Material | Cùng lúc đo target |
| Contrast chữ thường | ≥ 4.5:1 | WCAG AA | Contrast checker, kiểm cả light và dark mode |
| Contrast chữ lớn, icon mang nghĩa | ≥ 3:1 | WCAG AA | Như trên |
| Phóng chữ | Tới 200% không vỡ layout, không mất chữ | WCAG, Dynamic Type | Bật cỡ lớn nhất trong Settings rồi đi hết luồng |
| Safe area | Không nội dung hay thao tác nào nằm dưới notch, thanh cử chỉ, viền bo | HIG, Material | Máy có notch trong device matrix |
| Dark mode | Mọi màn có bản tối, contrast vẫn đạt ngưỡng trên | HIG, Material | Đổi theme giữa lúc app đang mở, không chỉ lúc khởi động |
| Scroll | 60fps, không jank frame ở danh sách dài | Ngân sách nội bộ | Profiler, danh sách ≥ 500 phần tử |
| Chrome khi đọc | ≤ ~10% chiều cao màn | Ngân sách nội bộ | Ảnh chụp màn hình trên máy nhỏ nhất |
| Phản hồi thao tác | Có dấu hiệu trong 100ms; có tiến trình nếu quá 1s | Performance budget | Đo trên máy cấu hình thấp, không phải máy dev |
| Screen reader | Mọi control có nhãn đọc được, thứ tự đọc đúng | WCAG | TalkBack và VoiceOver, luồng chính |
| Vùng chạm không có nhãn nhìn thấy | Phải có nhãn riêng cho screen reader | WCAG | Nút chỉ có icon là chỗ hay quên nhất |
Tầng 3 — Ai đánh giá, đánh giá lúc nào
Bốn chốt, đặt càng sớm càng rẻ, đúng tinh thần shift left. Không dựng gate mới — gắn vào bốn chỗ đã có:
Lúc viết acceptance criteria
Người viết yêu cầu ghi kết quả cần đạt, không ghi pattern. Chốt rẻ nhất: sửa một dòng chữ thay vì sửa một màn hình đã code xong.
Lúc mở PR
Ảnh chụp bắt buộc gồm ba bản — cỡ chữ mặc định, cỡ chữ lớn nhất, dark mode. Xem Chuẩn pull request.
Lúc tick DoD
Thử tay trên máy cấu hình thấp, đi hết năm trạng thái màn hình. Xem Definition of Done.
Regression trước release
Chạy đủ bảng ngưỡng ở tầng 2 cho các màn mức risk cao, không chạy cho mọi pull request.
Không áp cả bảng cho mọi màn — throughput sẽ sụp. Phân theo mức risk, giống cách Kiểm thử đặc thù mobile phân:
Mức cao
Thanh toán, tạo đơn, nhập OTP, đăng nhập. Màn nào chạm tới tiền hoặc chặn đường vào app.
Mức trung bình
Chat, danh sách Task, tạo và sửa nội dung. Dùng nhiều lần mỗi ngày.
Mức thấp
Cài đặt, trang tĩnh, màn chỉ đọc ít người mở.
Khi review một màn hình, hỏi đúng năm câu này theo thứ tự — dừng ở câu đầu tiên trả lời "không":
Nó hỏng ở đâu trước?
Máy nhỏ nhất, chữ to nhất, dark mode, không mạng. Bốn điều kiện này lộ gần hết lỗi layout.
Một tay dùng được không?
Thao tác chính có nằm trong tầm ngón cái không.
Chrome đổi lấy được gì?
Mỗi thanh cố định phải trả lời được câu này.
Năm trạng thái có đủ hình không?
Loading, rỗng, lỗi, mất mạng, đủ dữ liệu.
Người không nhìn rõ dùng được không?
Screen reader đọc đúng, contrast đạt, màu không phải kênh duy nhất.
Giới hạn
Bài này nói về chuẩn, không nói về thẩm mỹ
Chọn màu thương hiệu, chọn phông, chọn phong cách minh hoạ nằm ngoài phạm vi. Chúng là quyết định thiết kế, không phải quality gate.
Phần lớn bảng ngưỡng chưa tự động hoá được
Contrast và target size có công cụ quét; font scale, dark mode và screen reader hiện vẫn phải thử tay. Đừng ép automation vào chỗ nó chưa hiệu quả.
Chưa tuyên bố tuân thủ WCAG ở mức nào
Đây là ngưỡng nội bộ mượn con số từ WCAG AA, không phải cam kết tuân thủ. Muốn tuyên bố thì cần audit riêng, và khoảng cách ghi vào sổ nợ kỹ thuật.

