AllianceProject Handbook
← Knowledge

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.

Cập nhật 14/09/2026devmobilechất lượngbắt buộc

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
  • 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.

  1. "Header co lại khi vuốt"

    một phát biểu về UI

  2. Nó là chuẩn, pattern hay sở thích?

    pattern — Apple và Google đều có sẵn

  3. Vậy chuẩn đằng sau nó là gì?

    chrome không được nuốt nội dung

  4. 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ả:

  1. 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.

  2. 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ế.

  3. 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.

  4. 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.

  5. Ô đ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.

  6. 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ụcNgưỡngNguồnKiểm bằng
Touch target≥ 48dp (Android), ≥ 44pt (iOS)Material, HIGLayout inspector. WCAG chỉ đòi 24×24px — quá thấp cho app dùng cả ngày
Khoảng cách giữa hai target≥ 8dpMaterialCùng lúc đo target
Contrast chữ thường≥ 4.5:1WCAG AAContrast checker, kiểm cả light và dark mode
Contrast chữ lớn, icon mang nghĩa≥ 3:1WCAG AANhư trên
Phóng chữTới 200% không vỡ layout, không mất chữWCAG, Dynamic TypeBật cỡ lớn nhất trong Settings rồi đi hết luồng
Safe areaKhông nội dung hay thao tác nào nằm dưới notch, thanh cử chỉ, viền boHIG, MaterialMáy có notch trong device matrix
Dark modeMọi màn có bản tối, contrast vẫn đạt ngưỡng trênHIG, MaterialĐổi theme giữa lúc app đang mở, không chỉ lúc khởi động
Scroll60fps, không jank frame ở danh sách dàiNgân sách nội bộProfiler, danh sách ≥ 500 phần tử
Chrome khi đọc≤ ~10% chiều cao mànNgâ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ácCó dấu hiệu trong 100ms; có tiến trình nếu quá 1sPerformance budgetĐo trên máy cấu hình thấp, không phải máy dev
Screen readerMọi control có nhãn đọc được, thứ tự đọc đúngWCAGTalkBack và VoiceOver, luồng chính
Vùng chạm không có nhãn nhìn thấyPhải có nhãn riêng cho screen readerWCAGNú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ó:

  1. 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.

  2. 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.

  3. 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.

  4. 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:

Target và contrast
Font scale 200%
Dark mode
Screen reader
Cao
đầy đủ
đầy đủ
đầy đủ
đầy đủ
Trung bình
đầy đủ
đầy đủ
đầy đủ
luồng chính
Thấp
đầy đủ
quét mắt
đầy đủ

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":

  1. 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.

  2. 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.

  3. Chrome đổi lấy được gì?

    Mỗi thanh cố định phải trả lời được câu này.

  4. Năm trạng thái có đủ hình không?

    Loading, rỗng, lỗi, mất mạng, đủ dữ liệu.

  5. 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.