Créer une page de connexion split-screen en React
Une page de connexion split-screen en React utilise CSS Grid avec deux colonnes égales : un panneau gauche pour l'identité de marque et un panneau droit pour le formulaire. Le panneau du formulaire entre en fondu + glissement via un spring Framer Motion au montage, ce qui garde l'entrée fluide sans transition pleine page.
- Stack : React + Framer Motion + Tailwind v4, ~130 lignes, zéro dépendance supplémentaire.
- Layout : lg:grid-cols-2, panneau gauche masqué sur mobile, formulaire pleine largeur sous le breakpoint lg.
- Animation d'entrée : opacity 0→1 + y 20→0 en 600ms avec un cubic-bezier spring (0.16, 1, 0.3, 1).
- Props : showSocial et showRemember activent/désactivent les blocs UI optionnels sans bidouillage CSS conditionnel.
- Thème via CSS custom properties uniquement, zéro couleur en dur, compatible avec tous les presets.
Auth Login Split est une page de connexion plein écran à deux colonnes construite en CSS Grid. La colonne gauche est une surface branding, fond accent, nom du produit, tagline, et la colonne droite contient le formulaire. Sur mobile le panneau gauche disparaît complètement, laissant un formulaire centré et épuré. Le formulaire entre avec un léger fondu + glissement Framer Motion, juste assez de mouvement pour paraître soigné sans distraire un utilisateur qui veut simplement se connecter.
Anatomie
L'élément outer est un grid min-h-screen avec lg:grid-cols-2. Le panneau gauche (masqué sous lg) est une flex column en justify-between pour pousser le logo en haut, le tagline au milieu et le copyright en bas ; il utilise var(--color-accent) comme fond. Le panneau droit centre une div max-w-sm qui contient le titre, le bouton Google optionnel, un séparateur, les inputs email et mot de passe, une ligne optionnelle se souvenir/oublier, le bouton de soumission et un lien d'inscription.
Comment ça marche
L'animation est minimale mais précise. Le wrapper max-w-sm du formulaire est une motion.div avec `initial={{ opacity: 0, y: 20 }}` et `animate={{ opacity: 1, y: 0 }}`. La transition utilise un cubic-bezier `[0.16, 1, 0.3, 1]`, une courbe ease-out agressive qui démarre vite et s'établit doucement, donnant au formulaire un effet d'atterrissage naturel. Durée : 0.6s. Comme l'animation cible le wrapper plutôt que les champs individuels, tout le formulaire entre comme une unité, ce qui est plus lisible que des animations décalées dans un contexte d'authentification.
Comment le coder en React
Mettre en place le grid deux colonnes
Fais de la section outer un grid plein viewport avec deux colonnes égales au-dessus du breakpoint lg. En dessous, CSS Grid revient automatiquement à une seule colonne empilée, donc le formulaire prend toute la largeur sur mobile sans media query supplémentaire.
<section className="min-h-screen grid lg:grid-cols-2"> {/* left panel */} <div className="hidden lg:flex flex-col justify-between p-12" style={{ background: "var(--color-accent)" }}> ... </div> {/* right panel */} <div className="flex items-center justify-center p-8"> ... </div> </section>Animer le wrapper du formulaire au montage
Enveloppe le contenu du formulaire dans une motion.div et déclare les états initial et animate. Le cubic-bezier `[0.16, 1, 0.3, 1]` est un ease-out rapide qui donne de l'énergie à l'animation sans rebond. Anime le wrapper, pas les inputs individuels, pour que le formulaire entre comme un bloc cohérent.
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" >Activer les blocs optionnels via des props
Conditionne le bloc social login à un booléen showSocial et la ligne se souvenir/oublier à showRemember. Les deux sont à true par défaut. Cela rend le composant utile dans différents contextes, un outil interne minimaliste supprime le social login, un SaaS grand public garde tout, sans toucher le markup.
{showSocial && ( <> <button ...>Continuer avec Google</button> <div className="flex items-center gap-3 my-6">...</div> </> )}Utiliser les CSS tokens pour le thème
Chaque couleur référence une CSS custom property : var(--color-accent) pour le fond du panneau gauche et le bouton de soumission, var(--color-foreground) pour le texte, var(--color-border) pour les bordures des inputs et les séparateurs. Le composant change de thème automatiquement quand l'attribut data-theme change sur un élément parent.
<input type="email" style={{ background: "var(--color-background-alt)", color: "var(--color-foreground)", border: "1px solid var(--color-border)", }} />
Quand l'utiliser
Le layout split convient aux produits SaaS et agences où l'écran de connexion est un point de contact de marque. Le panneau gauche est l'endroit idéal pour renforcer la proposition de valeur juste avant que l'utilisateur se connecte. À éviter pour les outils internes ou les dashboards où personne ne se préoccupe du branding à la connexion, un formulaire centré plus simple est plus rapide à parcourir. Le layout demande aussi suffisamment de contenu pour que les deux colonnes semblent équilibrées ; un panneau gauche avec juste un logo et une tagline vide est moins bon qu'un design mono-colonne.
Utilisé par
- Vercel, Layout d'authentification deux colonnes avec message de marque à gauche et formulaire de connexion à droite.
- Linear, Page de connexion split avec panneau d'identité de marque et formulaire email/social, cohérent avec leur esthétique produit épurée.
- Notion, Écran d'authentification deux colonnes qui maintient le branding produit visible tout au long du flux de connexion.
FAQ
Que devient le panneau gauche sur mobile ?
Il disparaît complètement. La div porte hidden lg:flex, donc sous le breakpoint lg (1024px) elle est en display:none et le panneau formulaire occupe toute la largeur. Pas de layout shift, pas d'élément branding replié qui prend de la place.
Comment connecter le formulaire à un vrai provider d'authentification ?
Remplace le handler onSubmit={(e) => e.preventDefault()} par ta logique d'auth, NextAuth signIn(), Supabase auth.signInWithPassword(), ou un appel API custom. Le formulaire est intentionnellement non-contrôlé dans la démo, tu peux donc ajouter ta propre gestion d'état sans lutter contre des inputs contrôlés existants.
Peut-on mettre une image ou illustration en fond du panneau gauche ?
Oui. Remplace le style background de var(--color-accent) par un background-image CSS ou ajoute un img/Image en position absolute dans la div gauche. Garde le texte lisible en ajoutant un overlay semi-transparent si l'image est chargée.
Le composant est-il accessible ?
Le formulaire utilise des éléments input natifs avec type='email' et type='password', ce qui donne aux navigateurs une validation intégrée et un support pour les technologies d'assistance. Les labels sont absents des inputs de démo, ajoute des éléments label explicites ou des attributs aria-label avant de mettre en production.