Bỏ qua tới nội dung
Component / 036 / NEXOFRAME

PasswordField

Mẫu chuẩn

Ô mật khẩu với nút hiện/ẩn nằm trong ô và autoComplete bắt buộc — quên nó là lỗi trình quản lý mật khẩu phổ biến nhất trên web, và ở đây trình biên dịch không cho quên.

Composite tầng 2"use client"autoComplete bắt buộcDựng từ Label + Input

Playground

Bấm nút hiện/ẩn thật, đổi autoComplete và ghi chú để xem cùng một PasswordField thích ứng.

LIVE PREVIEW

autoComplete hiện tại: current-password

import { PasswordField } from "@nexobox/ui";

function Example() {
  const [value, setValue] = useState("");
  return (
    <PasswordField
      label="Mật khẩu"
      value={value}
      onChange={setValue}
      autoComplete="current-password"
    />
  );
}

Bài toán

Một ô mật khẩu che chính thứ người dùng vừa gõ, nên tỉ lệ gõ sai cao và người dùng không có cách nào tự kiểm. Cách chữa đã thành chuẩn là một nút hiện/ẩn, nhưng nút đó cần một <button> thật để đến được bằng bàn phím, cần định vị tuyệt đối trong lòng ô, cần một aria-label đổi theo trạng thái, và cần lật type giữa password và text — tức nó phải sở hữu một mảnh state. Input là một primitive không state và không nên trở thành có state chỉ vì một ca dùng. Chỗ đúng cho nút đó là tầng trên cả Input lẫn Label, và đó cũng là lý do nó không thể là một variant của Field: Field không có state, composite này có.

Khi nào sử dụng

Nên dùng

Mọi ô nhập mật khẩu: đăng nhập, đăng ký, đặt lại mật khẩu, đổi mật khẩu trong trang bảo mật. Khi người dùng cần tự kiểm điều mình vừa gõ trước khi nộp.

Khi nào không dùng

Tránh dùng

Không dùng choDùng thay
Ô nhập văn bản thường có gợi ý và lỗiField
Mã OTP hoặc mã xác thực sáu chữ sốOtpInput — mã OTP không cần che, che một chuỗi dùng một lần chỉ gây gõ sai
Khoá API, token, secret trong trang cấu hìnhInput cộng một nút sao chép — ngữ nghĩa ở đó là tiết lộ một giá trị đã lưu, không phải nhập một mật khẩu
Hai ô "mật khẩu" và "nhắc lại mật khẩu" cần so khớpHai PasswordField cộng luật so khớp ở host. Composite này không so hai giá trị

Dựng từ

Primitive tầng 1Vai trong composite
LabelNhãn, htmlFor bằng id đã giải quyết.
InputÔ nhập. type do composite lật, className bị composite đặt để chừa chỗ cho nút.
Icon (wrapper, không phải dòng catalogue)Vẽ EyeIcon hoặc ViewOffIcon.
PasswordField thêm gì mà không primitive nào có
Một <button type="button"> định vị tuyệt đối trong lòng ô, vuông theo component.field.height
useState giữ trạng thái hiện/ẩn, và phép lật type giữa password và text
aria-label đổi theo trạng thái: "Hiện mật khẩu" khi đang che, "Ẩn mật khẩu" khi đang hiện
Một autoComplete bắt buộc, kiểu union hai giá trị

Anatomy

#PhầnGhi chú
1LabelhtmlFor bằng id.
2Inputtype lật theo state hiện/ẩn, padding-right bằng chiều cao ô.
3Nút hiện/ẩnHình vuông cạnh bằng chiều cao ô, dính mép phải, vành tiêu điểm vẽ vào trong.
4Ghi chú (p)Chỉ render khi có error hoặc hint.

Cạnh nút bằng chiều cao ô là một token dùng hai lần, không phải hai con số tình cờ bằng nhau — nên chữ trong ô không bao giờ chạy xuống dưới nút.

States

CHE (MẶC ĐỊNH)

type="password", nhãn nút "Hiện mật khẩu".

LỖI

Mật khẩu không đúng.

aria-invalid="true", gợi ý bị che.

CÓ GỢI Ý

Tối thiểu 8 ký tự.

autoComplete="new-password" cho ô đặt mật khẩu mới.

Nút được focus: viền tiêu điểm vẽ vào trong (outline-offset âm) vì nút dính mép ô. Trạng thái hiện/ẩn không đặt lại khi value đổi. Hover, disabled của ô: kế thừa từ Input.

Bàn phím & trợ năng

Tab vào ô, Tab ra -- đến từ Input.
Bấm nhãn để tiêu điểm vào ô -- Label cộng cặp htmlFor/id composite nối.
Tab lần nữa vào nút hiện/ẩn, Enter hoặc Space để lật -- PasswordField thêm. Nút là <button> thật nên nằm trong tab order và nhận cả hai phím kích hoạt mà không cần xử lý phím riêng.
Đây là điểm duy nhất trong bảy composite mà một phần tử tương tác mới được thêm vào tab order -- nghĩa vụ trợ năng thật sự phát sinh mới, không kế thừa.

Seam — host sở hữu gì

PasswordField không đo độ mạnh mật khẩu, không áp luật độ dài, không so khớp hai ô, không nộp gì. Theo ADR-UI-003 clause 2 nó không fetch, không đọc router, không giữ session, không import gì từ next. Clause 6 cấm cụ thể hơn: dấu vân mật khẩu mô phỏng của bộ kit không được port, nên bất kỳ chỉ báo độ mạnh nào cũng phải là quyết định mới, không phải một thứ lặng lẽ xuất hiện ở đây.

Host sở hữuCấp qua
Giá trịvalue — controlled, bắt buộc, khác Field
Mỗi lần gõonChange(value: string) — nhận chuỗi, không nhận event
Luật độ mạnh, luật so khớp, và câu chữ của chúngerror / hint
Ngữ cảnh của ô: đang đăng nhập hay đang đặt mật khẩu mớiautoComplete, bắt buộc

Trạng thái duy nhất composite tự giữ là hiện/ẩn — trạng thái trình bày, cố ý không lộ ra API: không có prop defaultVisible.

API & triển khai

PropMặc địnhQuy ước
label—Bắt buộc.
value—Bắt buộc. Composite này luôn controlled.
onChange—Bắt buộc. Nhận chuỗi đã bóc khỏi event.
autoComplete—"current-password" | "new-password". Bắt buộc, không default.
hintundefinedBị error che.
errorundefinedCó giá trị ⇒ aria-invalid="true".
iduseId()—
…InputHTMLAttributes—Trừ type, value, onChange -- ba prop này bị Omit vì composite sở hữu chúng.

Design tokens

PasswordField đọc --nf-component-auth-field-gap cho khoảng cách dọc, --nf-component-field-height cho cạnh nút hiện/ẩn và padding-right của ô, --nf-component-focus-width / -focus-color cho viền tiêu điểm của nút, --nf-semantic-text-muted cho màu icon và gợi ý, --nf-semantic-status-danger-foreground cho màu lỗi. Cùng lỗ hổng đặt tên component.auth.fieldGap đã ghi ở Field, theo dõi bằng dòng ledger W10.19, áp cho cả trang này.

Do / Don't

Nên dùng

Truyền autoComplete="new-password" ở mọi ô đặt mật khẩu mới. Để composite sở hữu trạng thái hiện/ẩn. Đặt luật độ dài vào hint. Dùng hai composite cho "mật khẩu" và "nhắc lại".

Tránh dùng

Truyền "current-password" cho ô đăng ký — trình quản lý mật khẩu sẽ đề nghị mật khẩu cũ thay vì sinh một mật khẩu mới. Thêm một prop để mở sẵn. Trông đợi một thanh đo độ mạnh — nó không có. Trông đợi composite tự so khớp hai ô. Dùng PasswordField cho khoá API.