用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/InugamiDev/ultrathink-oss --skill forms命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Unified design foundations — design system architecture, tokens, component specs, visual principles, creative vision, figma integration, plus brand design system loader (66 real brands via DESIGN.md). Absorbs design, design-system, design-systems, design-principles, design-router, creative-vision, figma, design-md.
Render, summarize, and present markdown documents and structured content in multiple output modes
Ultra UI skill - combines Google's DESIGN.md spec (machine-readable design tokens) with the ui-ux-pro-max knowledge base (91 styles, 161 palettes, 73 font pairings, 161 products, 104 UX guidelines, 25 chart types). Generates lint-clean DESIGN.md files, validates token references and WCAG contrast, exports Tailwind/DTCG tokens, and diffs design systems version-over-version.
正在显示 SKILL.md
基于 SOC 职业分类
| name | forms |
| description | Form validation, multi-step forms, React Hook Form with Zod, server actions, and accessible form patterns |
| layer | domain |
| category | frontend |
| triggers | ["form","form validation","multi-step form","react hook form","zod schema","form builder","input validation"] |
| inputs | [{"requirements":"Form fields, validation rules, submission behavior"},{"framework":"React Hook Form | Formik | native (optional, defaults to RHF)"},{"validation":"Zod | Yup | native (optional, defaults to Zod)"},{"features":"Multi-step, file upload, conditional fields, server validation"}] |
| outputs | [{"form_component":"Complete form implementation with validation"},{"schema":"Zod/Yup validation schema"},{"types":"TypeScript types inferred from schema"},{"server_action":"Server-side validation and submission handler"}] |
| linksTo | ["react","nextjs","tailwindcss","ui-ux-pro","api-designer"] |
| linkedFrom | ["cook","react","nextjs"] |
| preferredNextSkills | ["react","api-designer"] |
| fallbackSkills | ["ui-ux-pro"] |
| riskLevel | low |
| memoryReadPolicy | selective |
| memoryWritePolicy | none |
| sideEffects | [] |
Build accessible, validated, performant forms using React Hook Form and Zod. This skill covers single-page and multi-step forms, client-side and server-side validation, file uploads, dynamic/conditional fields, and optimistic form submissions. Forms are the primary way users input data -- they must be correct, accessible, and pleasant to use.
CLIENT VALIDATION (fast feedback):
Zod schema validates on change/blur
React Hook Form manages state and re-renders
Errors displayed inline next to fields
SERVER VALIDATION (source of truth):
Server action or API route validates again
Business logic validation (uniqueness, permissions)
Returns field-level errors for display
PRINCIPLE: Validate on client for UX, validate on server for security.
Never trust client-side validation alone.
import { z } from 'zod';
// User registration schema
export const registerSchema = z.object({
email: z
.string()
.min(1, 'Email is required')
.email('Please enter a valid email address'),
password: z
.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Password must contain an uppercase letter')
.regex(/[0-9]/, 'Password must contain a number'),
confirmPassword: z.string(),
name: z
.string()
.min(2, 'Name must be at least 2 characters')
.(, ),
: z.(, {
: ({ : }),
}),
}).( data. === data., {
: ,
: [],
});
= z.< registerSchema>;
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { registerSchema, type RegisterInput } from '@/lib/schemas';
export function RegisterForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<RegisterInput>({
resolver: zodResolver(registerSchema),
defaultValues: {
email: '',
password: '',
confirmPassword: '',
name: '',
},
});
async function onSubmit(data: RegisterInput) {
const result = await registerUser(data);
if (!result.ok) {
// Handle server-side errors
}
}
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium">
Email
</label>
<input
id="email"
type="email"
autoComplete="email"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? 'email-error' : undefined}
className="mt-1 w-full px-4 py-3 rounded-lg border transition-all duration-200
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
{...register('email')}
/>
{errors.email && (
<p id="email-error" role="alert" className="mt-1 text-sm text-red-600">
{errors.email.message}
</p>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full px-6 py-4 min-h-[2.625rem] text-base font-medium rounded-lg
bg-blue-600 text-white hover:bg-blue-700
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed
transition-all duration-200 motion-reduce:transition-none"
>
{isSubmitting ? 'Creating account...' : 'Create account'}
</button>
</form>
);
}
'use client';
import { useState } from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// Step schemas
const step1Schema = z.object({
email: z.string().email(),
name: z.string().min(2),
});
const step2Schema = z.object({
company: z.string().min(1),
role: z.string().min(1),
});
const step3Schema = z.object({
plan: z.enum(['free', 'pro', 'enterprise']),
});
const fullSchema = step1Schema.merge(step2Schema).merge(step3Schema);
type FormData = z.infer<typeof fullSchema>;
const STEPS = [
{ schema: step1Schema, title: 'Account' },
{ schema: step2Schema, title: 'Company' },
{ schema: step3Schema, title: 'Plan' },
];
export function OnboardingForm() {
const [currentStep, setCurrentStep] = useState(0);
const methods = useForm<FormData>({
resolver: zodResolver(STEPS[currentStep].schema),
mode: 'onBlur',
});
async function handleNext() {
const isValid = await methods.trigger();
if (isValid && currentStep < STEPS.length - 1) {
setCurrentStep((s) => s + 1);
}
}
function handleBack() {
setCurrentStep((s) => Math.max(0, s - 1));
}
async function onSubmit(data: FormData) {
await submitOnboarding(data);
}
return (
<FormProvider {...methods}>
{/* Step indicator */}
<nav aria-label="Progress">
<ol className="flex gap-4">
{STEPS.map((step, i) => (
<li key={step.title} className={i <= currentStep ? 'text-blue-600' : 'text-gray-400'}>
{step.title}
</li>
))}
</ol>
</nav>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{/* Render current step fields */}
{currentStep === 0 && <Step1Fields />}
{currentStep === 1 && <Step2Fields />}
{currentStep === 2 && <Step3Fields />}
<div className="flex gap-4 mt-8">
{currentStep > 0 && (
<button type="button" onClick={handleBack}>Back</button>
)}
{currentStep < STEPS.length - 1 ? (
<button type="button" onClick={handleNext}>Next
) : (
Submit
)}
);
}
// app/actions/register.ts
'use server';
import { registerSchema } from '@/lib/schemas';
type ActionState = {
success: boolean;
errors?: Record<string, string[]>;
message?: string;
};
export async function registerAction(
prevState: ActionState,
formData: FormData,
): Promise<ActionState> {
const raw = Object.fromEntries(formData);
const parsed = registerSchema.safeParse(raw);
if (!parsed.success) {
return {
success: false,
errors: parsed.error.flatten().fieldErrors,
};
}
// Server-side business validation
const existingUser = await db.user.findUnique({
where: { email: parsed.data.email },
});
if (existingUser) {
return {
success: false,
errors: { email: ['This email is already registered'] },
};
}
await db.user.create({ data: parsed.data });
return { success: true, message: 'Account created' };
}
REQUIRED:
- [ ] Every input has a visible <label> (not just placeholder)
- [ ] Errors use role="alert" for screen reader announcement
- [ ] aria-invalid={true} on fields with errors
- [ ] aria-describedby links input to its error message
- [ ] Form can be submitted with Enter key
- [ ] Focus moves to first error field on submission failure
- [ ] Required fields marked with aria-required="true"
- [ ] Error messages are descriptive ("Email is required" not "Required")
RECOMMENDED:
- [ ] Autocomplete attributes on common fields (email, name, password)
- [ ] Input type matches content (email, tel, url, number)
- [ ] Tab order is logical (top to bottom, left to right)
- [ ] Loading state announced to screen readers
autoComplete attributes -- browsers and password managers need these| Pitfall | Impact | Fix |
|---|---|---|
| Client-only validation | Security bypass | Always validate on server |
| Missing labels | Inaccessible to screen readers | Use <label htmlFor> |
| Clearing form on error | User loses all input | Preserve state on failure |
| No loading state | User clicks submit multiple times | Disable button + show spinner |
| Placeholder as label | Disappears on input, fails accessibility | Use visible labels |
Missing noValidate | Browser and RHF validation conflict | Add noValidate to form tag |