Button States: idle → loading → success

Một submit button không chỉ có một trạng thái. Từ lúc người dùng nhìn thấy form đến lúc hoàn thành action, button đi qua ít nhất ba phase: chờ, xử lý, và xác nhận. Nếu ba phase này không được thiết kế rõ ràng, người dùng sẽ click lại, refresh, hoặc đơn giản là không biết chuyện gì đang xảy ra.

Click để xem sequence: idle → loading → success → idle

Ba state và nhiệm vụ của từng state

Idle — button đang chờ. Người dùng chưa làm gì. Label mô tả action sắp xảy ra (“Pay $128”, “Submit”, “Send”). State này không cần nhiều — chỉ cần rõ label và đủ contrast để nhận ra đây là element có thể nhấn.

Loading — action đã được gửi đi, hệ thống đang xử lý. Đây là state quan trọng nhất và thường bị bỏ qua nhất. Người dùng cần được biết ngay lập tức rằng click của họ đã được nhận — nếu không, họ sẽ click lại.

Spinner hoặc dots animation không phải để “đẹp” — chúng là bằng chứng rằng hệ thống đang làm việc. Thiếu feedback này, 2 giây im lặng cảm giác như 10 giây.

Success — xử lý xong, kết quả tốt. Label đổi thành xác nhận (“Payment successful”, “Sent”, “Saved”). Đây là moment người dùng cảm thấy an tâm — đừng để nó quá ngắn.

Tại sao transition direction quan trọng

Khi label cũ biến mất và label mới xuất hiện, direction của animation tạo ra narrative:

  • Label cũ trượt lên và biến mất → cảm giác “hoàn thành, đi tiếp”
  • Label mới trượt vào từ dưới → cảm giác “cái mới đang đến”

Direction này map với mental model tuyến tính: idle → loading → success là tiến về phía trước. Nếu đảo ngược (label mới từ trên xuống), sẽ cảm giác đang đi lùi — không khớp với flow.

Loader trong button: dots, không phải spinner

Spinner tròn hoạt động tốt trên không gian rộng. Trong button nhỏ (height 40–48px), spinner thường bị clip bởi border-radius hoặc lệch khỏi trung tâm khi button có label dài.

Dots loader (ba chấm sóng) compact hơn và scale tốt hơn trong button. Ba chấm với delay lần lượt tạo cảm giác “hệ thống đang hoạt động” mà không cần nhiều không gian.

Button phải bị vô hiệu hoá trong khi loading

Đây là rule không có ngoại lệ: khi button đang ở state loading, người dùng không được phép click lại. Không phải vì kỹ thuật — mà vì UX.

Nếu button vẫn clickable trong khi loading, người dùng sẽ click lại khi thấy không có phản hồi rõ ràng. Double submission là lỗi UX, không phải lỗi kỹ thuật.

Visual để reinforce: button loading nên trông khác với button idle — thường là giảm opacity nhẹ hoặc cursor đổi thành not-allowed / default.

Timing

PhaseThời gian hiển thịLý do
Idle → LoadingTức thì (< 100ms)Người dùng cần biết click được nhận ngay
Loading → SuccessPhụ thuộc serverKhông fake — chờ response thật
Success hiển thị2–3 giâyĐủ để đọc và cảm thấy an tâm trước khi reset

Đừng fake loading animation với setTimeout cố định. Nếu server trả về trong 300ms, loading state nên kết thúc sau 300ms — không phải 1.8 giây cho có vẻ “đang xử lý”.

Error state — state thứ tư thường bị quên

Flow thực tế không phải lúc nào cũng idle → loading → success. Khi có lỗi (network fail, validation fail), button cần trở về idle với error message — không phải im lặng.

Button error state thường đơn giản: reset về idle, hiển thị error message bên ngoài button (dưới form hoặc toast). Không nên hiển thị error bên trong button vì message thường dài hơn button width.

Tham khảo: Hover Micromotion, Gesture Feedback Motion.