?? Introduction
Les formulaires sont le point de contact principal entre vos utilisateurs et votre site. Un formulaire bien conçu peut augmenter significativement votre taux de conversion, tandis qu'un formulaire mal pensé peut frustrer et faire fuir vos visiteurs.
Dans ce tutoriel complet, vous allez apprendre à créer des formulaires modernes et élégants avec CSS pur. Nous couvrirons les labels flottants, la validation en temps réel, les animations d'inputs et toutes les bonnes pratiques UX.
??? Les pseudo-classes de formulaire
CSS offre de nombreuses pseudo-classes spécialement conçues pour les formulaires. Ces sélecteurs puissants permettent de styliser les inputs selon leur état.
Les pseudo-classes essentielles
| Pseudo-classe | Description | Exemple d'utilisation |
|---|---|---|
:focus |
Élément actuellement sélectionné | Bordure bleue quand on clique |
:focus-within |
Parent contenant un élément focus | Styliser le conteneur du form |
:valid |
Input avec valeur valide | Bordure verte de succès |
:invalid |
Input avec valeur invalide | Bordure rouge d'erreur |
:placeholder-shown |
Input affichant son placeholder | Labels flottants |
:required |
Champ obligatoire | Ajouter un astérisque |
:disabled |
Champ désactivé | Opacité réduite |
:read-only |
Champ en lecture seule | Style grisé non-éditable |
:checked |
Checkbox/radio coché | Changer la couleur |
L'astuce du :placeholder-shown
La pseudo-classe :placeholder-shown est la clé des labels flottants. Elle détecte si le placeholder est visible (donc si l'input est vide).
/* Le placeholder est visible = input vide */
input:placeholder-shown + label {
/* Label en position normale */
top: 50%;
font-size: 16px;
}
/* Le placeholder est caché = input rempli */
input:not(:placeholder-shown) + label {
/* Label flotte vers le haut */
top: 0;
font-size: 12px;
color: #0066ff;
}
:placeholder-shown fonctionne, votre input DOIT avoir un attribut placeholder, même s'il est vide : placeholder=" "
? Styliser les inputs
Reset des styles navigateur
Avant de styliser, il faut réinitialiser les styles par défaut des navigateurs qui varient beaucoup.
/* Reset complet des inputs */
input, textarea, select {
appearance: none; /* Supprime les styles natifs */
-webkit-appearance: none;
border: none;
outline: none;
background: none;
font-family: inherit;
font-size: inherit;
color: inherit;
}
/* Supprimer le outline bleu par défaut */
input:focus {
outline: none;
}
/* Cacher le X de suppression sur IE/Edge */
input::-ms-clear {
display: none;
}
/* Styliser le placeholder */
input::placeholder {
color: #888;
opacity: 1;
}
Structure HTML recommandée
Une bonne structure HTML est essentielle pour les labels flottants :
<div class="input-group">
<input
type="email"
id="email"
placeholder=" "
required
>
<label for="email">Adresse email</label>
</div>
<!-- Note : le placeholder=" " (espace) est obligatoire
pour que :placeholder-shown fonctionne -->
✅ Validation CSS en temps réel
La validation HTML5 combinée au CSS permet de créer des feedbacks visuels instantanés sans JavaScript.
Attributs de validation HTML5
| Attribut | Validation | Exemple |
|---|---|---|
required |
Champ obligatoire | <input required> |
type="email" |
Format email valide | Vérifie @domaine.com |
minlength |
Longueur minimale | minlength="8" |
maxlength |
Longueur maximale | maxlength="100" |
pattern |
Expression régulière | pattern="[A-Za-z]+" |
min / max |
Valeur min/max (nombres) | min="0" max="100" |
CSS de validation complet
/* État par défaut */
.input-group input {
border: 2px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
/* Focus - bordure bleue */
.input-group input:focus {
border-color: #0066ff;
box-shadow: 0 0 0 4px rgba(0, 102, 255, 0.1);
}
/* Valide ET rempli - bordure verte */
.input-group input:valid:not(:placeholder-shown) {
border-color: #00c853;
}
/* Invalide ET rempli ET pas focus - bordure rouge */
.input-group input:invalid:not(:placeholder-shown):not(:focus) {
border-color: #ff3d57;
}
/* Icône de validation */
.input-group input:valid:not(:placeholder-shown) ~ .icon-valid {
opacity: 1;
color: #00c853;
}
.input-group input:invalid:not(:placeholder-shown):not(:focus) ~ .icon-invalid {
opacity: 1;
color: #ff3d57;
}
:not(:focus)). Afficher une erreur pendant qu'il tape est frustrant !
?? 9 Exemples pratiques
Le label se déplace vers le haut quand l'input reçoit le focus ou contient du texte. C'est le pattern le plus utilisé en 2025.
.input-group {
position: relative;
margin-bottom: 25px;
}
.input-group input {
width: 100%;
padding: 16px 14px;
font-size: 16px;
color: #e0e0e0;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
outline: none;
transition: all 0.3s ease;
}
.input-group label {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
font-size: 16px;
color: #888;
pointer-events: none;
transition: all 0.3s ease;
background: #000; /* Couleur du fond */
padding: 0 5px;
}
/* Focus : bordure bleue + label monte */
.input-group input:focus {
border-color: #0066ff;
}
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
top: 0;
font-size: 12px;
color: #0066ff;
}
/* Validation */
.input-group input:valid:not(:placeholder-shown) {
border-color: #00c853;
}
.input-group input:valid:not(:placeholder-shown) + label {
color: #00c853;
}
Une ligne bleue qui s'étend du centre vers les extrémités au focus. Style minimaliste très élégant inspiré de Material Design.
.input-underline {
position: relative;
margin-bottom: 30px;
}
.input-underline input {
width: 100%;
padding: 12px 0;
font-size: 16px;
color: #e0e0e0;
background: transparent;
border: none;
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
}
/* La ligne animée */
.input-underline .underline {
position: absolute;
bottom: 0;
left: 50%; /* Commence au centre */
width: 0;
height: 2px;
background: #0066ff;
transition: all 0.3s ease;
}
/* Au focus : la ligne s'étend */
.input-underline input:focus ~ .underline {
left: 0;
width: 100%;
}
/* Label flottant */
.input-underline label {
position: absolute;
left: 0;
top: 12px;
color: #888;
pointer-events: none;
transition: all 0.3s ease;
}
.input-underline input:focus + label,
.input-underline input:not(:placeholder-shown) + label {
top: -20px;
font-size: 12px;
color: #0066ff;
}
Des icônes qui changent de couleur au focus pour guider l'utilisateur.
.input-icon {
position: relative;
}
/* Padding gauche pour l'icône */
.input-icon input {
padding-left: 45px;
}
/* Label aussi décalé pour éviter le chevauchement */
.input-icon label {
left: 45px;
}
/* Icône positionnée à gauche */
.input-icon .icon {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
color: #888;
transition: color 0.3s ease;
}
/* L'icône devient bleue au focus */
.input-icon input:focus ~ .icon {
color: #0066ff;
}
/* L'icône devient verte si valide */
.input-icon input:valid:not(:placeholder-shown) ~ .icon {
color: #00c853;
}
/* L'icône devient rouge si invalide */
.input-icon input:invalid:not(:placeholder-shown):not(:focus) ~ .icon {
color: #ff3d57;
}
Un effet de lueur subtil au focus pour un look futuriste. Inspiré de la collection FVOweb.
.input-glow input {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
transition: all 0.3s ease;
}
.input-glow input:focus {
border-color: #0066ff;
box-shadow:
0 0 10px rgba(0, 102, 255, 0.3),
0 0 20px rgba(0, 102, 255, 0.2),
0 0 30px rgba(0, 102, 255, 0.1);
}
Le conteneur entier s'illumine quand un de ses champs est actif. Parfait pour les formulaires de connexion.
.form-focus-within {
padding: 30px;
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
transition: all 0.3s ease;
}
/* Quand N'IMPORTE QUEL enfant a le focus */
.form-focus-within:focus-within {
border-color: #0066ff;
box-shadow: 0 0 30px rgba(0, 102, 255, 0.1);
}
/* Bonus: changer le titre quand le form est actif */
.form-focus-within:focus-within h2 {
color: #0066ff;
}
:focus-within est très puissant ! Il permet de styliser n'importe quel parent quand un de ses descendants a le focus.
Validation native HTML5 stylisée avec CSS pur. Aucun JavaScript nécessaire ! Les pseudo-classes :valid et :invalid détectent automatiquement l'état.
/* Style de base de l'input */
.input-group input {
width: 100%;
padding: 14px 40px 14px 16px; /* Espace pour l'icône */
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
background: transparent;
color: #fff;
transition: all 0.3s ease;
}
/* Icônes de validation (cachées par défaut) */
.validation-icon {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
font-size: 18px;
opacity: 0;
transition: opacity 0.3s ease;
}
.valid-icon { color: #00c853; }
.invalid-icon { color: #ff3d57; }
/* Message d'erreur (caché par défaut) */
.validation-msg {
position: absolute;
bottom: -22px;
left: 0;
font-size: 12px;
color: #ff3d57;
opacity: 0;
transition: opacity 0.3s ease;
}
/* ✔️ État VALIDE - Bordure verte + icône check */
input:valid:not(:placeholder-shown) {
border-color: #00c853;
}
input:valid:not(:placeholder-shown) ~ .valid-icon {
opacity: 1;
}
/* ❌ État INVALIDE - Bordure rouge + icône X + message */
input:invalid:not(:placeholder-shown):not(:focus) {
border-color: #ff3d57;
}
input:invalid:not(:placeholder-shown):not(:focus) ~ .invalid-icon {
opacity: 1;
}
input:invalid:not(:placeholder-shown):not(:focus) ~ .validation-msg {
opacity: 1;
}
💡 Important : Le sélecteur :not(:placeholder-shown) empêche la validation de s'afficher sur un champ vide. Et :not(:focus) attend que l'utilisateur quitte le champ avant d'afficher l'erreur.
required - Champ obligatoiretype="email" - Vérifie le format emailminlength="3" - Longueur minimumpattern="[a-z]+" - Expression régulière personnalisée
La technique du input:checked permet de styliser totalement les checkboxes et radios natifs sans JavaScript.
/* Container cliquable */
.custom-checkbox {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
}
/* Cacher l'input natif */
.custom-checkbox input {
display: none;
}
/* Notre checkbox personnalisée */
.checkbox-box {
width: 22px;
height: 22px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 6px;
position: relative;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
/* Icône check (cachée par défaut) */
.checkbox-box::after {
content: '?';
font-size: 14px;
color: white;
opacity: 0;
transform: scale(0);
transition: all 0.2s ease;
}
/* État coché : fond bleu + check visible */
.custom-checkbox input:checked + .checkbox-box {
background: #0066ff;
border-color: #0066ff;
}
.custom-checkbox input:checked + .checkbox-box::after {
opacity: 1;
transform: scale(1);
}
.custom-radio {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
}
.custom-radio input {
display: none;
}
/* Cercle extérieur */
.radio-circle {
width: 22px;
height: 22px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
position: relative;
transition: all 0.3s ease;
}
/* Point intérieur (caché par défaut) */
.radio-circle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
width: 10px;
height: 10px;
background: #0066ff;
border-radius: 50%;
transition: transform 0.2s ease;
}
/* État coché : bordure bleue + point visible */
.custom-radio input:checked + .radio-circle {
border-color: #0066ff;
}
.custom-radio input:checked + .radio-circle::after {
transform: translate(-50%, -50%) scale(1);
}
Les états :disabled et :read-only permettent de styliser les champs non-éditables différemment.
/* Champ désactivé - non cliquable */
input:disabled {
opacity: 0.5;
cursor: not-allowed;
background: rgba(255, 255, 255, 0.02);
}
input:disabled + label {
color: rgba(255, 255, 255, 0.3);
top: 0; /* Label en position haute */
font-size: 12px;
}
/* Champ read-only - visible mais non éditable */
input:read-only {
opacity: 0.7;
cursor: default;
background: rgba(255, 255, 255, 0.02);
border-style: dashed; /* Indicateur visuel */
}
input:read-only + label {
color: rgba(255, 255, 255, 0.5);
top: 0;
font-size: 12px;
}
/* Note: read-only peut recevoir le focus contrairement à disabled */
input:read-only:focus {
border-color: rgba(255, 255, 255, 0.3);
box-shadow: none;
}
disabled : Ne peut pas recevoir le focus, valeur non envoyée au serveurreadonly : Peut recevoir le focus, valeur envoyée au serveur
Ce formulaire combine toutes les techniques : labels flottants, icônes, validation, checkbox custom et :focus-within sur le conteneur.
<form class="login-form">
<h3>Connexion</h3>
<div class="input-group input-icon">
<input type="email" id="email" placeholder=" " required>
<label for="email">Email</label>
<i class="fas fa-envelope icon"></i>
</div>
<div class="input-group input-icon">
<input type="password" id="pass" placeholder=" " required minlength="8">
<label for="pass">Mot de passe</label>
<i class="fas fa-lock icon"></i>
</div>
<label class="custom-checkbox">
<input type="checkbox" name="remember">
<span class="checkbox-box"></span>
<span>Se souvenir de moi</span>
</label>
<button type="submit" class="submit-btn">Se connecter</button>
</form>
.login-form {
background: linear-gradient(145deg, #111111 0%, #0a0a0a 100%);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 20px;
padding: 40px;
max-width: 400px;
transition: all 0.3s ease;
}
/* Tout le formulaire s'illumine quand actif */
.login-form:focus-within {
border-color: #0066ff;
box-shadow: 0 0 40px rgba(0, 102, 255, 0.1);
}
/* Bouton avec gradient */
.submit-btn {
width: 100%;
padding: 16px;
font-weight: 600;
color: white;
background: linear-gradient(135deg, #0066ff 0%, #683FEA 100%);
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(0, 102, 255, 0.3);
}
Retrouvez plus de formulaires dans la collection FVOweb
Tous ces exemples et bien d'autres sont disponibles gratuitement dans notre collection, prêts à copier-coller !
Voir tous les formulaires? Accessibilité des formulaires
Règles essentielles
- Labels associés : Toujours utiliser
for="id"pour lier label et input - Focus visible : Ne jamais supprimer
outlinesans alternative visible - Contraste suffisant : Ratio minimum 4.5:1 pour le texte
- Messages d'erreur clairs : Pas seulement une couleur rouge
- Navigation clavier : Tab doit parcourir tous les champs
/* ? MAUVAIS - Supprime l'indication de focus */
input:focus {
outline: none;
}
/* ? BON - Remplace par un style visible */
input:focus {
outline: none;
border-color: #0066ff;
box-shadow: 0 0 0 4px rgba(0, 102, 255, 0.2);
}
/* ? Pour les utilisateurs clavier uniquement */
input:focus-visible {
outline: 2px solid #0066ff;
outline-offset: 2px;
}
:focus-visible pour afficher un outline uniquement lors de la navigation clavier, pas au clic souris. C'est le meilleur des deux mondes !
?? Conclusion
Vous avez maintenant tous les outils pour créer des formulaires modernes et accessibles avec CSS pur. Voici les points clés à retenir :
- ? Utilisez
:placeholder-shownpour les labels flottants - ? Combinez
:validet:invalidpour la validation visuelle - ? N'affichez les erreurs qu'après le blur (
:not(:focus)) - ? Utilisez
:focus-withinpour styliser les conteneurs - ? Toujours garder un focus visible pour l'accessibilité
- ? Testez la navigation clavier
Pour aller plus loin
- Explorez notre collection de formulaires
- Découvrez les inputs personnalisés
- Consultez l'article sur les boutons animés CSS
N'hésitez pas à expérimenter et combiner ces techniques pour créer vos propres designs uniques !