Créer un formulaire de connexion centré en React avec Framer Motion
Un formulaire de connexion React centré regroupe email, mot de passe et boutons sociaux optionnels dans une carte étroite, centrée verticalement et horizontalement par Flexbox. Une animation Framer Motion en fade-in (opacity 0→1, y 20→0) sur 600ms avec un ease spring personnalisé donne à la carte une entrée soignée sans saccade au chargement.
- Stack : React + Framer Motion 11 + Tailwind v4, ~194 lignes, aucune librairie d'icônes à l'exécution.
- Animation d'entrée : motion.div avec initial { opacity: 0, y: 20 } animé vers { opacity: 1, y: 0 }, durée 0,6s, cubic-bezier personnalisé [0.16, 1, 0.3, 1].
- Les boutons sociaux (Google + GitHub) et la case "se souvenir" sont pilotés par des props booléens, aucun balisage superflu quand ils sont désactivés.
- Entièrement thémable : toutes les couleurs lisent les CSS custom properties (--color-background, --color-accent, --color-border), compatible avec les 7 presets du registry.
- Accessible : les labels sont associés à leurs champs, le formulaire utilise un élément <form> natif, le bouton de soumission est de type='submit'.
Auth Login Centered est une page d'authentification React standalone qui centre une carte de connexion compacte dans le viewport. Elle combine un formulaire email/mot de passe avec des raccourcis OAuth optionnels (Google, GitHub), une case se-souvenir et un lien mot de passe oublié, tous les éléments qu'un produit SaaS requiert, sans dépendance figée à une librairie d'auth particulière.
Anatomie
La section extérieure s'étend sur min-h-screen et utilise Flexbox (items-center justify-center) pour centrer la carte intérieure. La carte est une motion.div limitée à max-w-sm. À l'intérieur : un bloc marque (initiale dans un carré accent + h1 + sous-titre optionnel), une rangée sociale optionnelle (deux boutons côte à côte séparés du formulaire par un séparateur horizontal avec label 'ou'), puis le formulaire avec champ email, champ mot de passe, une rangée se-souvenir/mot de passe oublié, et le bouton soumettre. Un lien d'inscription se place sous le formulaire.
Comment ça marche
L'entrée de la carte est une seule transition Framer Motion sur la motion.div enveloppante. Le tableau d'ease [0.16, 1, 0.3, 1] est une courbe expo-out : elle accélère fortement au départ puis décélère vers la position de repos, ce qui donne l'impression que la carte se cale en place plutôt que de dériver. Le décalage y de 20px garde le trajet court pour que l'animation signifie 'arrivée' plutôt qu'un scroll reveal. Pas de déclencheur scroll, pas d'IntersectionObserver, l'animation se lance immédiatement au mount.
Comment le coder en React
Centrer la mise en page
Enveloppe tout dans une section avec min-h-screen et le centrage Flexbox. Cela gère le centrage vertical et horizontal sans hauteur fixe ni positionnement absolu, la carte reste centrée même si le contenu déborde sur les petits écrans.
<section className="min-h-screen flex items-center justify-center py-16 px-6" style={{ background: "var(--color-background)" }}>Animer la carte au montage
Remplace la div intérieure par une motion.div et définis initial/animate. La courbe d'ease personnalisée donne le ressenti de pop-in. Garde le décalage y petit (20px) pour que ça signifie un atterrissage, pas une chute.
const ease: [number, number, number, number] = [0.16, 1, 0.3, 1]; <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, ease }} className="w-full max-w-sm" >Conditionner le bloc social avec une prop
Enveloppe les boutons sociaux et le séparateur dans un unique test showSocial. La carte s'affiche proprement comme formulaire email seul quand OAuth n'est pas disponible, sans espace vide à compenser.
{showSocial && ( <> <div className="flex gap-3 mb-6"> <button style={{ border: "1px solid var(--color-border)" }}>Google</button> <button style={{ border: "1px solid var(--color-border)" }}>GitHub</button> </div> {/* divider */} </> )}Connecter les champs de formulaire avec des tokens CSS
Utilise les CSS custom properties pour toutes les couleurs afin que le formulaire s'adapte à chaque preset de thème sans aucune classe conditionnelle. Le token background-alt donne le fond légèrement distinct qui différencie les champs de la surface de la carte.
<input type="email" style={{ background: "var(--color-background-alt)", color: "var(--color-foreground)", border: "1px solid var(--color-border)", }} />
Quand l'utiliser
Utilise ce layout chaque fois que l'écran de connexion est la destination principale : dashboards SaaS, outils internes, apps B2B. Il fonctionne bien quand l'authentification est un point de friction attendu et que l'utilisateur arrive avec l'intention de se connecter. À éviter pour les produits où la connexion est enfouie dans un tunnel (comme le checkout), une modale ou un formulaire inline convient mieux. La prop showSocial permet de retirer facilement les boutons OAuth quand seul email/mot de passe est supporté.
Utilisé par
- Linear, Connexion centrée minimaliste avec email et Google OAuth, séparation nette entre les flux social et email.
- Vercel, Carte d'auth centrée avec boutons sociaux GitHub, GitLab et Bitbucket au-dessus d'un formulaire email, correspondant exactement au pattern de layout de ce composant.
- Notion, Connexion centrée en colonne unique avec OAuth Google et Apple, puis une option email sous un séparateur, la structure empilée classique que ce composant suit.
- Loom, Carte d'auth centrée avec logo de marque en haut, boutons de connexion sociale et formulaire email en dessous, la même anatomie que ce composant.
FAQ
Comment connecter ce formulaire à une vraie librairie d'auth comme NextAuth ou Supabase ?
Remplace le handler onSubmit (actuellement e.preventDefault()) par l'appel de connexion de ta librairie. Pour NextAuth : signIn('credentials', { email, password }). Pour Supabase : supabase.auth.signInWithPassword({ email, password }). Les boutons sociaux suivent le même schéma : signIn('google') ou supabase.auth.signInWithOAuth({ provider: 'google' }).
Peut-on désactiver l'animation d'entrée pour les utilisateurs qui préfèrent moins de mouvement ?
Oui. Conditionne la config de transition au hook useReducedMotion de Framer Motion. Quand il retourne true, mets duration à 0 ou supprime entièrement les props initial/animate : initial={reducedMotion ? false : { opacity: 0, y: 20 }} avec transition={{ duration: reducedMotion ? 0 : 0.6, ease }}.
Comment ajouter de la validation de formulaire ?
Le formulaire est non contrôlé par défaut. Remplace les inputs par des versions contrôlées avec useState, puis valide à la soumission ou au blur. React Hook Form s'intègre proprement : enregistre chaque input avec register et appelle handleSubmit, qui remplace le onSubmit actuel.
Le composant est-il accessible à la navigation clavier ?
Les éléments de formulaire natifs (input, button, a, label) gèrent l'ordre de tabulation et le focus automatiquement. La case à cocher 'se souvenir' utilise un input natif type='checkbox', elle reçoit donc le focus et répond à Espace sans ARIA supplémentaire. Le bouton de soumission est type='submit', appuyer sur Entrée dans n'importe quel champ soumet donc le formulaire.