Menu
ESC

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

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

Đang tải...

Bài 23 — Form Design: Input, Label, Error

UI Design and Design Handoff Bài 23/60

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

Form là nơi giao dịch thật sự xảy ra. Bạn có thể thiết kế một landing page đẹp long lanh, nhưng nếu form đăng ký, form thanh toán, hay form nhập địa chỉ giao hàng khiến người dùng bối rối, bực bội hay nhập sai — thì mọi công sức phía trước đổ sông đổ biển. Trong e-commerce, người ta hay nói vui rằng "checkout form là cái máy in tiền", còn form thiết kế tệ thì là cái máy đốt tiền.

Có một con số đáng để bạn ghim vào đầu: theo nhiều nghiên cứu về trải nghiệm thanh toán (điển hình là các báo cáo của Baymard Institute), khoảng 17–18% người dùng bỏ giỏ hàng chỉ vì quá trình checkout quá dài hoặc quá phức tạp. Ở Việt Nam, khi tôi làm việc với một sàn thương mại điện tử vừa, chúng tôi phát hiện gần 30% người dùng bỏ dở ở bước điền địa chỉ — không phải vì họ đổi ý mua hàng, mà vì cái form địa chỉ bắt họ chọn Tỉnh/Huyện/Xã qua ba dropdown lồng nhau chậm và rối.

Điều đáng nói: form là thứ có tỷ lệ hoàn vốn (ROI) cao nhất khi bạn cải thiện. Một thay đổi nhỏ về vị trí label, một thông báo lỗi rõ ràng hơn, một helper text đúng lúc — có thể nâng conversion vài phần trăm, tương đương hàng trăm triệu doanh thu. Bài này chúng ta sẽ mổ xẻ ba thành phần cốt lõi của form ở cấp độ một field đơn lẻ: Input, Label, và Error — cùng cách sắp xếp chúng thành một anatomy chuẩn mực, có thể tái sử dụng trong design system.

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

Input anatomy — giải phẫu một trường nhập liệu

Trước khi thiết kế cả một form dài, bạn phải làm chủ đơn vị nhỏ nhất: một field. Một field hoàn chỉnh không chỉ là cái ô để gõ chữ. Nó là một cụm gồm nhiều phần, xếp theo trình tự đọc từ trên xuống:

[Label]                          *required
[Helper text]
[================ Input field ================]
[Error: Email không hợp lệ]

Cụ thể từng lớp:

  • Label: nhãn mô tả field này hỏi cái gì ("Email", "Số điện thoại", "Mật khẩu"). Đây là phần bắt buộc phải có.
  • Required indicator: dấu hiệu cho biết field bắt buộc hay không, thường là dấu sao đỏ () hoặc chữ "(bắt buộc)". Ngược lại, một số hệ thống lại đánh dấu field không* bắt buộc bằng chữ "(tùy chọn)".
  • Helper text: dòng gợi ý ngắn giúp người dùng biết cần nhập gì và nhập như thế nào ("Dùng ít nhất 8 ký tự"). Xuất hiện trước khi lỗi xảy ra.
  • Input field: chính cái ô nhập liệu, gồm border, background, padding, và placeholder.
  • Error message / Success message: phản hồi sau khi người dùng nhập, cho biết nhập đúng hay sai và sai chỗ nào.
Chiều cao ô nhập nên đủ lớn để bấm được thoải mái trên mobile — tối thiểu 44px (theo khuyến nghị của Apple Human Interface Guidelines) hoặc 48px (theo Material Design). Padding ngang thường 12–16px để chữ không dính sát viền.

Label — đặt ở đâu và viết thế nào

Vị trí label ảnh hưởng trực tiếp đến tốc độ điền form. Có ba kiểu chính:

  • Top-aligned (label nằm trên input): đây là lựa chọn mặc định tốt nhất cho hầu hết trường hợp. Mắt chỉ cần quét theo một trục dọc, dễ scan, thân thiện với mobile, và không bị vỡ layout khi dịch sang tiếng khác (tiếng Việt thường dài hơn tiếng Anh 20–30%).
  • Left-aligned (label bên trái input): tiết kiệm chiều cao dọc, hợp cho form dày đặc trên desktop như trang admin. Nhược điểm: tốn không gian ngang, khó responsive.
  • Placeholder-as-label (dùng placeholder thay label): TRÁNH. Khi người dùng bắt đầu gõ, placeholder biến mất, họ quên field này hỏi gì. Đây là một trong những lỗi accessibility phổ biến nhất.
Về floating label (label nằm trong ô, "trôi" lên nhỏ lại khi focus) — trông sang nhưng có bẫy: label ở trạng thái nhỏ đôi khi contrast kém, và animation gây phân tâm. Dùng có chừng mực.

Về cách viết: label nên ngắn, dùng danh từ, viết đúng ngôn ngữ người dùng. "Số điện thoại" tốt hơn "SĐT liên hệ khách hàng". Tránh dấu hai chấm thừa nếu design system không thống nhất dùng.

Helper text vs Error — hai vai trò khác nhau

Nhiều bạn junior nhầm lẫn hai thứ này. Helper text là hướng dẫn chủ động, xuất hiện ngay từ đầu để phòng ngừa lỗi ("Mật khẩu cần ít nhất 8 ký tự"). Error message là phản hồi bị động, chỉ hiện khi người dùng đã nhập sai.

Nguyên tắc vàng cho error message: phải nói rõ sai cái gìcách sửa. So sánh:

  • Tệ: "Lỗi." / "Không hợp lệ." / "Invalid input."
  • Tốt: "Email chưa đúng định dạng — cần có dấu @ (ví dụ: ten@gmail.com)."
Error không được chỉ dựa vào màu đỏ để truyền tải, vì khoảng 8% nam giới bị mù màu đỏ-lục. Luôn đi kèm icon (dấu chấm than) và chữ mô tả — đây là nguyên tắc "đừng dùng riêng màu để truyền tải thông tin" mà bạn sẽ gặp lại trong bài Accessibility.

States — các trạng thái của một input

Một input không chỉ có một hình dạng. Design system phải định nghĩa đủ bộ trạng thái:

  • Default (rest): trạng thái nghỉ, chưa tương tác.
  • Hover: con trỏ chuột rê qua (chỉ có trên desktop).
  • Focus: đang được chọn, thường có viền đậm/đổi màu + focus ring rõ ràng để hỗ trợ điều hướng bằng bàn phím.
  • Filled: đã có dữ liệu.
  • Error: viền đỏ + error message.
  • Success: viền xanh + dấu tích (dùng tiết chế, không phải field nào cũng cần).
  • Disabled: bị khóa, làm mờ, con trỏ "not-allowed".
  • Read-only: hiển thị dữ liệu nhưng không cho sửa (khác disabled ở chỗ vẫn có thể chọn/copy).
Thiếu định nghĩa focus state là lỗi accessibility nghiêm trọng: người dùng bàn phím sẽ không biết mình đang ở field nào.

Validation timing — nên báo lỗi khi nào

Đây là quyết định UX quan trọng bậc nhất của form:

  • On submit: chỉ báo lỗi khi bấm nút gửi. An toàn, không làm phiền, nhưng người dùng phải cuộn lại sửa nhiều field cùng lúc.
  • On blur: báo lỗi khi người dùng rời khỏi field (đã điền xong). Đây là lựa chọn cân bằng nhất và được khuyến nghị.
  • On change / real-time: báo lỗi ngay khi đang gõ. Chỉ nên dùng cho phản hồi tích cực (ví dụ thanh sức mạnh mật khẩu), tránh dùng để la mắng người dùng ngay khi họ vừa gõ ký tự đầu tiên của email — cực kỳ khó chịu.
Nguyên tắc thực dụng: "Reward early, punish late" — khen sớm, phạt muộn. Chỉ hiện lỗi sau khi người dùng rời field, nhưng một khi đã hiện lỗi thì cập nhật real-time để họ thấy mình đã sửa đúng.

Tình huống thực tế

Ví dụ 1: Form địa chỉ giao hàng của một sàn TMĐT Việt Nam

Bối cảnh: Một sàn thương mại điện tử tầm trung ở TP.HCM có bước checkout với form địa chỉ dùng ba dropdown lồng nhau: Tỉnh/Thành → Quận/Huyện → Phường/Xã, cộng thêm ô "Địa chỉ chi tiết". Analytics cho thấy 28% người dùng rơi rớt ngay tại bước này.

Diễn giải: Vấn đề nằm ở chỗ label không rõ, ba dropdown tải dữ liệu chậm (mỗi lần chọn phải chờ gọi API), và error message chỉ hiện chữ "Vui lòng điền đầy đủ" ở cuối form khi bấm submit — người dùng không biết field nào thiếu. Đội thiết kế đã: (1) chuyển sang top-aligned label rõ ràng, (2) thêm helper text "Chọn từ trên xuống dưới", (3) cho phép gõ để tìm nhanh trong dropdown thay vì cuộn 63 tỉnh, (4) chuyển validation sang on-blur với error inline ngay dưới từng field.

Bài học: Với dữ liệu địa chỉ hành chính Việt Nam, cấu trúc phân cấp là bắt buộc, nhưng trải nghiệm nhập phải mượt. Error phải gắn tại chỗ với field lỗi, không dồn ra cuối form. Sau chỉnh sửa, tỷ lệ rớt tại bước địa chỉ giảm từ 28% xuống còn khoảng 16%.

Ví dụ 2: Form đăng ký tài khoản với password requirements

Bối cảnh: Một ứng dụng fintech (giả định tên "MoMoney") có form đăng ký với ô mật khẩu yêu cầu: ít nhất 8 ký tự, có chữ hoa, có số, có ký tự đặc biệt. Ban đầu họ giấu các yêu cầu này, chỉ hiện error đỏ "Mật khẩu không đủ mạnh" sau khi người dùng bấm submit.

Diễn giải: Người dùng gõ mật khẩu, bấm submit, bị báo lỗi, sửa, lại bị báo lỗi vì thiếu ký tự đặc biệt — vòng lặp bực bội. Đội thiết kế thay bằng một checklist real-time hiển thị ngay dưới ô mật khẩu: mỗi yêu cầu là một dòng, chuyển từ xám sang xanh có dấu tích khi được thỏa mãn. Đây là ứng dụng đúng đắn của validation on-change cho phản hồi tích cực.

Bài học: Với các field có ràng buộc phức tạp, đừng bắt người dùng đoán. Hiển thị yêu cầu ngay từ đầu (helper text/checklist), và cho phản hồi tiến độ theo thời gian thực. "Khen sớm" ở đây nghĩa là để người dùng thấy từng điều kiện được tick xanh dần.

Ví dụ 3: Optional vs Required gây mất niềm tin

Bối cảnh: Một startup SaaS ở Singapore có form đăng ký dùng thử. Họ đánh dấu tất cả field bắt buộc bằng dấu sao đỏ, nhưng lại có tới 11 field, trong đó nhiều field như "Tên công ty", "Quy mô nhân sự", "Số điện thoại" đều bắt buộc. Tỷ lệ hoàn thành form chỉ 41%.

Diễn giải: Vấn đề không nằm ở anatomy của từng field, mà ở việc quá nhiều field bắt buộc. Đội đã audit lại: field nào thật sự cần cho việc tạo tài khoản? Kết quả là chỉ giữ Email và Mật khẩu bắt buộc, các field còn lại chuyển thành tùy chọn và gắn nhãn "(tùy chọn)" rõ ràng, hoặc dời sang bước onboarding sau. Vì phần lớn field còn lại là tùy chọn, họ đổi quy ước: chỉ đánh dấu field bắt buộc, không đánh dấu field tùy chọn nữa cho đỡ rối.

Bài học: Anatomy đẹp không cứu được một form hỏi quá nhiều. Required indicator không chỉ là chuyện trang trí — nó là tuyên bố về mức độ bạn tôn trọng thời gian người dùng. Sau khi cắt gọn, tỷ lệ hoàn thành tăng lên 68%.

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

Đây là quy trình dựng một input component chuẩn để đưa vào design system:

  • Xác định cấu trúc anatomy: Vẽ khung dọc gồm 5 lớp — Label + Required, Helper text, Input, và vùng dành cho Error/Success. Đặt sẵn khoảng trống cho error để layout không "nhảy" khi lỗi xuất hiện (reserve space).
  • Chốt spacing dọc: Label cách input khoảng 4–8px, error cách input 4–6px. Dùng hệ spacing bội số của 8 để nhất quán với phần còn lại của hệ thống.
  • Thiết kế input container: Định nghĩa height (44–48px), padding ngang (12–16px), border (1–1.5px), border-radius, background, và màu chữ. Đảm bảo contrast chữ nhập với nền đạt tối thiểu 4.5:1.
  • Dựng đủ bộ states: Tạo variant cho Default, Hover, Focus, Filled, Error, Success, Disabled, Read-only. Focus state phải có ring rõ ràng, không được chỉ đổi màu border rất nhẹ.
  • Viết microcopy mẫu: Soạn sẵn helper text và error message mẫu cho các loại field phổ biến (email, số điện thoại, mật khẩu). Error luôn theo công thức: [Field] + [sai gì] + [cách sửa/ví dụ].
  • Chọn validation timing: Mặc định on-blur cho lỗi, real-time cho phản hồi tích cực. Ghi rõ quy tắc này trong tài liệu component.
  • Kiểm thử accessibility: Đảm bảo label liên kết đúng với input (để screen reader đọc được), error được thông báo cho công nghệ hỗ trợ, và mọi thông tin không chỉ dựa vào màu.
  • Đóng gói thành component có API rõ ràng: Các thuộc tính như label, helperText, errorMessage, state, required nên là các property có thể bật/tắt, để lập trình viên dùng lại dễ dàng.

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

  • Dùng placeholder thay label: Placeholder biến mất khi gõ. Luôn có label riêng, placeholder chỉ để gợi ý định dạng ("VD: 0912 345 678").
  • Error message vô nghĩa: "Lỗi" hay "Invalid" không giúp ai. Luôn nói rõ sai gì và sửa thế nào.
  • Chỉ dùng màu đỏ để báo lỗi: Thêm icon và chữ. Người mù màu và người dùng screen reader cần điều đó.
  • Layout nhảy khi lỗi xuất hiện: Chừa sẵn khoảng trống cho error message ngay từ đầu, đừng để form giật lên giật xuống.
  • Validation real-time quá hung hăng: Đừng báo "email sai" khi người dùng mới gõ được chữ "n". Chờ họ rời field (on-blur).
  • Bỏ quên focus state: Đây vừa là lỗi UX vừa là lỗi accessibility. Focus ring phải rõ ràng.
  • Quá nhiều field bắt buộc: Mỗi field bắt buộc là một rào cản. Hỏi ít nhất có thể, phần còn lại để sau.
  • Mẹo autofill: Đặt đúng thuộc tính autocomplete (như email, tel, name) để trình duyệt và trình quản lý mật khẩu điền hộ — giảm ma sát rất nhiều, đặc biệt trên mobile.
  • Mẹo bàn phím mobile: Field số điện thoại nên bật bàn phím số, field email nên có phím @. Chi tiết nhỏ nhưng tăng tốc nhập đáng kể.

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

  • Dựng input component đủ states: Trong Figma, tạo một input field với đầy đủ 8 trạng thái (Default, Hover, Focus, Filled, Error, Success, Disabled, Read-only). Dùng biến/variant để quản lý. Đảm bảo focus state có ring rõ ràng và error có cả icon lẫn chữ.
  • Viết lại 5 error message tệ: Lấy 5 câu error "vô hồn" (ví dụ: "Lỗi", "Không hợp lệ", "Field required", "Sai định dạng", "Có gì đó không ổn") và viết lại theo công thức [Field] + [sai gì] + [cách sửa]. So sánh trước–sau.
  • Redesign form địa chỉ Việt Nam: Thiết kế lại form nhập địa chỉ giao hàng gồm Họ tên, SĐT, Tỉnh/Huyện/Xã, Địa chỉ chi tiết. Áp dụng top-aligned label, helper text hợp lý, validation on-blur, và error inline. Chú thích rõ quyết định về required/optional.
  • Audit một form thật: Chọn một form đăng ký hoặc checkout của một ứng dụng bạn hay dùng. Liệt kê ít nhất 5 vấn đề về Input/Label/Error và đề xuất cách sửa cụ thể.

Tóm tắt

Form là nơi conversion sống hay chết, và mọi form đều bắt đầu từ đơn vị nhỏ nhất: một field gồm Label, Input và Error. Hãy nhớ những điểm cốt lõi:

  • Anatomy: Label + Required indicator → Helper text → Input → Error/Success, xếp theo trục dọc, chừa sẵn chỗ cho error để layout không nhảy.
  • Label: ưu tiên top-aligned, không bao giờ dùng placeholder thay label, viết ngắn gọn đúng ngôn ngữ người dùng.
  • Helper text vs Error: helper là hướng dẫn phòng ngừa (xuất hiện trước), error là phản hồi khắc phục (xuất hiện sau, nói rõ sai gì + cách sửa, có icon lẫn chữ).
  • States: định nghĩa đủ bộ, đặc biệt không quên focus state.
  • Validation timing: "khen sớm, phạt muộn" — lỗi báo on-blur, phản hồi tích cực có thể real-time.
  • Tôn trọng người dùng: hỏi ít field nhất có thể, đánh dấu required nhất quán, tận dụng autofill và bàn phím phù hợp.
Làm chủ được ba thành phần này ở cấp độ một field, bạn đã có nền tảng vững để dựng những form phức tạp hơn — điều mà các bài tiếp theo về navigation, data display và prototyping sẽ tiếp nối. Một field tốt là viên gạch đầu tiên của một trải nghiệm nhập liệu khiến người dùng muốn hoàn thành, chứ không muốn bỏ chạy.