Aller au contenu principal
Guillaume Duverger Code & graphisme icodink.com Création de sites web modernes et graphisme

Form validation avec pseudo-classe :has()

Accueil blog Démonstrations CSS MAJ : 07 août 2025

Votre navigateur ne supporte pas cette démonstration

Cette démonstration utilise la pseudo-classe fonctionnelle :has() qui va nous permettre de gérer les états de validité ou d'erreur des champs renseignés et d'adapter les actions à effectuer en conséquence.

Veuillez entrer une adresse valide
Votre mot de passe doit contenir au moins 8 caractères


<form>
	
<div class=cadre-input>
<label for="email">Email :</label>
<div class=input-form>
<svg></svg>
<input required type="email" id="email" title="Veuillez entrer une adresse valide" placeholder="Veuillez entrer une adresse valide">
</div>
<div class="erreur"><span>Veuillez entrer une adresse valide</span></div>
</div>
	
<div class=cadre-input>

<label for="password">Mot de passe :</label>
<div class=input-form>
<svg>...</svg>
<input required type="password" id="password"  placeholder="Votre mot de passe" title="Votre mot de passe doit contenir au moins 8 caractères" pattern=".{8,}">
</div>	
<div class="erreur"><span>Votre mot de passe doit contenir au moins 8 caractères</span></div>
</div>
	
<button type="submit">Se connecter</button>
	
</form>





*{box-sizing: border-box}

:root {
interpolate-size: allow-keywords;	
--texte: oklch(52% 0 0);
--rouge: oklch(48% .2 30);
--vert: oklch(58% 0.18 145);
--jaune: color-mix(in oklch,var(--vert),var(--blanc));
--gris: oklch(80% 0 0);
--blanc:oklch(1 0 0);
 	
}	
	


	
form:is(:focus,:focus-within){border-color: oklch(55% 0 0);}
	

	
form{
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04),
0 4px 3px rgba(0, 0, 0, 0.03),
0 8px 6px rgba(0, 0, 0, 0.03),
0 14px 11px rgba(0, 0, 0, 0.02),
0 22px 17px rgba(0, 0, 0, 0.01),
0 32px 24px rgba(0, 0, 0, 0.01);
border-radius:.5lh;
border:1px solid var(--gris);
display: grid;
grid-template-columns:var(--grid);
max-width: 75ch;
margin:auto;
row-gap: 2rem;
column-gap: 1rem;
padding:1rem;

}


.cadre-input {

grid-column: span 2;
display: grid;
gap: 1rem;
grid-template-columns: var(--sub);
grid-template-rows: auto minmax(3rem,1fr);
align-items: center;

	
}
	

form label {
display: flex;
font-weight: bold;
color:var(--couleur-form);
transition: color var(--transition);
}
	
form input {
padding:0;
background:none;
font-family: inherit;
width:100%;
transition: border-color var(--transition);
border: 2px solid var(--couleur-form);
border-radius: 4px;
height: 3rem;
padding-left:4cap;
font-weight: 400;
font-size:1rem;
}


form input::placeholder {color:transparent;}
	
[type="submit"] {
font-family: inherit;
border:none;	
place-self:center;
grid-column:span 2;
padding: 1lh;
border-radius: .25lh;
pointer-events: none;
background:linear-gradient(var(--vert) 0 0) no-repeat 0 0/var(--progression,0) 2px,#ececec;
transition:background-size var(--transition) var(--transition);
font-weight: bold;
font-size: 1rem;
color: var(--couleur-form);
}
	
form input:is(:focus-within,:focus-visible){outline:2px solid var(--couleur-form);outline-offset: 2px}
	
.input-form{display: grid;}
	
.input-form>*{grid-area:1/1}
	
.input-form svg{

border-radius: 4px;
margin-left:.5rem;
width:2.5cap;
height:2.5cap;
fill:var(--couleur-form);
align-self:center
	
	}
	
		
.cadre-input:has(:user-invalid) {
	
  --couleur-form: var(--rouge);
	
}

.cadre-input:has(:focus) {
	
  --couleur-form: var(--jaune);
	
}

.cadre-input:has(:valid) {
	
  --couleur-form: var(--vert);
}

.cadre-input:has(:placeholder-shown) {
	
  --couleur-form: var(--texte);
}
		
.erreur {
	
display: none;
translate:0 10px;
opacity:0;
white-space: nowrap;
font-size: .875rem;
grid-column:var(--gc);

}
	
.erreur span {
 
position: relative;
background-color: var(--rouge);
border-radius: 5px;
padding: 1rlh;
display:block;
white-space: pre-wrap;
width:fit-content;
color:var(--blanc);
text-align: center;
}
	
.erreur span::after {
	 
content: "";
position: absolute;
top: -5px;
left:10px;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid var(--rouge);

}
	

	
.cadre-input:first-of-type:has(:valid) ~ [type="submit"]{ --progression:50%;}

.cadre-input:has(:valid) ~ .cadre-input:last-of-type:has(:valid) ~ [type="submit"] {

--progression:100%;
background-color:var(--vert);
--color:var(--blanc);
transition:background-size var(--transition), background-color var(--transition) calc(var(--transition)*2),color var(--transition) calc(var(--transition)*2);
	}

.cadre-input:has(:invalid:not(:focus):not(:placeholder-shown)) .erreur {
	
display: block;
translate:0 0;  
opacity:1;
	
	}	
	
.mail:not(:has(:valid)) + .mdp {
  height: 0;
  opacity: 0;
  overflow: clip;
}
	
@media (min-width:40rem){:root{--grid: auto minmax(30ch,1fr);--gc:2}
.cadre-input{--sub:subgrid}}	
	
@media (prefers-reduced-motion: no-preference) {
:root{--transition: .25s;}


.cadre-input:has(:invalid:not(:focus):not(:placeholder-shown)) .erreur {

transition:all var(--transition), 
translate var(--transition), 
display var(--transition);

}
	
.mdp {
  
transition: var(--transition),opacity var(--transition);
}
@starting-style {.cadre-input:has(:invalid:not(:focus):not(:placeholder-shown)) .erreur{
	
	opacity:0;
	translate:0 10px;
	}
	
	}	
	}