--- name: resilient-forms-validation description: >- Build high-resilience, accessible web forms with unified schema validation (Zod / Valibot), React 19 Actions / React Hook Form, optimistic UX, draft auto-save persistence, and strict WCAG error announcements. --- # Resilient Forms & Accessible Schema Validation Ce skill encadre la création de formulaires web de niveau entreprise. Il élimine les formulaires artisanaux fragiles (multiples `useState` non validés, perte de données lors du rechargement, spinners bloquants) au profit d'un contrat de validation de schéma unifié, d'une résilience hors-ligne et d'une accessibilité irréprochable. --- ## 🎯 Déclencheurs d'Activation Activez ce skill dès que : 1. Vous créez ou refactorisez un formulaire (inscription, authentification, paiement, paramètres utilisateur, formulaires multi-étapes). 2. Vous validez des entrées utilisateur côté client et côté serveur avec **Zod** ou **Valibot**. 3. Vous implémentez des mutations asynchrones avec **React 19 Actions** (`useActionState`) ou **React Hook Form**. 4. Vous devez garantir la persistance des brouillons (*draft auto-save*) face aux rafraîchissements intempestifs. 5. Vous auditez l'accessibilité des erreurs de saisie selon le critère WCAG 3.3 (Identification des erreurs et suggestions). --- ## 📐 1. Les 4 Piliers d'un Formulaire Résilient 1. **Schéma Unique Partagé (Zod)** : Le même schéma TypeScript valide la saisie en temps réel côté client et protège l'endpoint API côté serveur. 2. **Accessibilité des Erreurs (WCAG 3.3)** : - Chaque champ en erreur porte `aria-invalid="true"`. - Les messages d'erreur sont reliés au champ via `aria-describedby="field-error-id"`. - Un conteneur avec `role="alert"` ou `aria-live="polite"` annonce les erreurs aux lecteurs d'écran sans déplacer brutalement le curseur. 3. **Persistance Locale Automatique des Brouillons** : Pour les formulaires longs (> 3 champs), les valeurs sont sauvegardées dans `sessionStorage` ou `localStorage` pour éviter la perte de données en cas de crash d'onglet. 4. **Mutations Non Bloquantes** : Ne jamais afficher d'overlay gris qui fige l'écran entier. Seul le bouton d'action affiche un état en cours, tout en restant désactivé pour empêcher la double soumission. --- ## 💻 2. Exemple d'Implémentation de Référence (Zod + React 19 Action) ```tsx 'use client'; import React, { useActionState } from 'react'; import { z } from 'zod'; // 1. Schéma Zod Partagé export const ContactSchema = z.object({ name: z.string().min(2, 'Le nom doit comporter au moins 2 caractères'), email: z.string().email('Adresse email invalide'), message: z.string().min(10, 'Votre message doit comporter au moins 10 caractères'), }); type FormState = { success?: boolean; errors?: Record; values?: { name: string; email: string; message: string }; }; // 2. Action Serveur / Handler Typé async function submitContactAction(prevState: FormState, formData: FormData): Promise { const rawData = { name: formData.get('name') as string, email: formData.get('email') as string, message: formData.get('message') as string, }; const validated = ContactSchema.safeParse(rawData); if (!validated.success) { return { success: false, errors: validated.error.flatten().fieldErrors, values: rawData, }; } // Traitement backend sécurisé... return { success: true }; } // 3. Composant Formulaire Accessible export const ResilientContactForm = () => { const [state, formAction, isPending] = useActionState(submitContactAction, {}); return (
{state.success && (
Votre message a été envoyé avec succès !
)} {/* Champ Nom */}
{state.errors?.name && ( )}
{/* Champ Email */}
{state.errors?.email && ( )}
{/* Bouton de soumission avec état de chargement intégré */}
); }; ``` --- ## 📋 Checklist de Conformité Formulaires - [ ] Un schéma Zod ou Valibot est défini et partagé entre validation UI et API. - [ ] L'attribut `noValidate` est présent sur `
` pour éviter les infobulles natives inconsistantes des navigateurs. - [ ] Tout champ en erreur est balisé par `aria-invalid="true"` et relié à son message par `aria-describedby`. - [ ] Le bouton d'envoi est désactivé pendant la soumission pour éviter les doublons. - [ ] En cas de rejet serveur, les valeurs déjà saisies sont conservées pour éviter à l'utilisateur de tout retaper.