用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/skeletorflet/opencode-kit --skill form-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | form-patterns |
| description | Form design and validation patterns. UX, accessibility, React Hook Form, Zod, error handling. |
Forms are where users do work. Remove every obstacle.
Label (above input, never placeholder-only)
Input / Select / Checkbox / Radio
Helper text (optional guidance below)
Error message (replaces helper text on error)
| Data | Input Type |
|---|---|
| Short text | <input type="text"> |
<input type="email"> | |
| Password | <input type="password"> + show toggle |
| Number | <input type="number"> or text + mask |
| Long text | <textarea> |
| Date | Native date or custom picker |
| One of few options | Radio group (≤5) or Select (>5) |
| Many options | Multi-select or Combobox |
| Boolean | Checkbox or Toggle |
| File | <input type="file"> with drag zone |
When to validate:
├── On blur (field loses focus) → best UX
├── On submit → acceptable
├── On change → only after first error shown (revalidate)
└── NEVER on keypress for first validation
Server-side:
├── Always re-validate server-side regardless of client
├── Return field-level errors { field: "email", message: "..." }
└── Show server errors inline next to fields
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "Min 8 characters"),
});
type FormData = z.infer<typeof schema>;
export function LoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = async (data: FormData) => {
await loginUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<div>
<label htmlFor=>Email
{errors.email && (
{errors.email.message}
)}
{isSubmitting ? "Logging in..." : "Log in"}
);
}
| ❌ Bad | ✅ Good |
|---|---|
| "Invalid input" | "Email must include @" |
| "Error 422" | "This email is already registered" |
| "Required" | "Please enter your name" |
| "Password wrong" | "Incorrect password. Forgot it?" |
Rules:
role="alert" for screen readersProgress indicator:
Step 1: Account Step 2: Profile Step 3: Review
Rules:
├── Show step X of Y
├── Allow back navigation without data loss
├── Persist state (localStorage / URL params)
├── Validate step before proceeding
└── Show summary on final step before submit
<label> (not just placeholder)aria-describedbyaria-invalid="true"required attr + visual indicatorautocomplete="email" etc.)| Pattern | Description |
|---|---|
| Inline validation | Validate on blur, not keypress |
| Password strength | Visual meter + requirements list |
| Input masks | Phone, credit card, date formatting |
| Autofocus | First field on page load (not modals unless modal-only) |
| Smart defaults | Pre-fill from account, geolocation |
| Disable submit | Only while submitting (not on invalid) |
| Success feedback | Clear confirmation, not just form reset |
| ❌ Don't | ✅ Do |
|---|---|
| Placeholder as label | Visible label always |
| Validate on every keypress | Validate on blur |
| Generic "form error" at top only | Field-level inline errors |
| Password confirm field | Show/hide toggle instead |
| Disable browser autocomplete | Allow it |
| Asterisk without explanation | "(required)" in legend or label |
| """ |