Aller au contenu
Bibliotheque UIJABS

Retours utilisateur 16 variantes

Tout retour repond a deux questions : que s'est-il passe, et que puis-je faire maintenant. Un message qui ne dit que la premiere laisse l'utilisateur bloque. Regle tenue partout : l'erreur nomme la cause en langage humain, jamais un code technique seul, et propose toujours une suite.

A. Messages en ligne

Bandeaux poses dans le flux de la page, avec ou sans actions, et messages attaches a un champ.

Nouvelle facturation electronique

A partir du 1er septembre 2027, toutes vos factures devront passer par une plateforme agreee.

Devis envoye

Le client a recu le devis LTP-2026-103 par courriel.

Attestation manquante

Le Cerfa de TVA reduite doit etre signe avant le debut des travaux.

Envoi impossible

L'adresse du client est incomplete. Completer la fiche client

Bandeau d'information

Message pose dans le flux de la page. L'icone double le sens porte par la couleur : lu en noir et blanc, le bandeau reste comprehensible.

Voir le codeMasquer le code
<div class="demo-col demo-col--large">
  <div class="alerte alerte--info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg><div><strong>Nouvelle facturation electronique</strong><p>A partir du 1er septembre 2027, toutes vos factures devront passer par une plateforme agreee.</p></div></div>
  <div class="alerte alerte--ok"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg><div><strong>Devis envoye</strong><p>Le client a recu le devis LTP-2026-103 par courriel.</p></div></div>
  <div class="alerte alerte--attention"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg><div><strong>Attestation manquante</strong><p>Le Cerfa de TVA reduite doit etre signe avant le debut des travaux.</p></div></div>
  <div class="alerte alerte--erreur"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg><div><strong>Envoi impossible</strong><p>L'adresse du client est incomplete. <a href="#">Completer la fiche client</a></p></div></div>
</div>
.alerte {
  display: flex;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-4);
  border: 1px solid transparent;
  border-left-width: 4px;
  border-radius: var(--r-md);
}
.alerte > svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.alerte strong { display: block; font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--c-text); }
.alerte p { margin: 3px 0 0; font-size: var(--fs-sm); color: var(--c-text-soft); line-height: 1.5; }
.alerte a { color: inherit; font-weight: var(--fw-semi); }
.alerte--info      { border-color: var(--c-info);    background: var(--c-info-soft); }
.alerte--info > svg { color: var(--c-info); }
.alerte--ok        { border-color: var(--c-success); background: var(--c-success-soft); }
.alerte--ok > svg  { color: var(--c-success); }
.alerte--attention { border-color: var(--c-warn);    background: var(--c-warn-soft); }
.alerte--attention > svg { color: var(--c-warn); }
.alerte--erreur    { border-color: var(--c-danger);  background: var(--c-danger-soft); }
.alerte--erreur > svg { color: var(--c-danger); }
.alerte--erreur a  { color: var(--c-danger); }
flex gap-3 rounded-lg border-l-4 p-4 [&_svg]:h-5 [&_svg]:shrink-0
3 devis attendent une relance

Envoyes il y a plus de 7 jours et toujours sans reponse.

Bandeau avec actions

Quand le message appelle une decision. Les boutons sont dans le bandeau, pas ailleurs sur la page : l'utilisateur ne doit pas chercher.

Voir le codeMasquer le code
<div class="alerte alerte--attention alerte--actions">
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg>
  <div>
    <strong>3 devis attendent une relance</strong>
    <p>Envoyes il y a plus de 7 jours et toujours sans reponse.</p>
    <div class="alerte__boutons">
      <button class="btn btn--plein btn--sm">Relancer les 3</button>
      <button class="btn btn--fantome btn--sm">Voir la liste</button>
    </div>
  </div>
  <button class="btn btn--fermer" aria-label="Masquer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
</div>
.alerte--actions { align-items: flex-start; }
.alerte--actions > div { flex: 1; }
.alerte__boutons { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
[&_.boutons]:mt-3 [&_.boutons]:flex [&_.boutons]:gap-2
Votre periode d'essai se termine dans 6 jours. Choisir une formule
Mode consultation : vous voyez le compte de ATELIER ALBERT. Revenir a mon compte
Bandeau de page

Colle en haut de l'application : maintenance prevue, essai qui se termine, mode consultation. Pleine largeur, jamais de coins arrondis.

Voir le codeMasquer le code
<div class="demo-col demo-col--large">
  <div class="bandeau bandeau--info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg> Votre periode d'essai se termine dans 6 jours. <a href="#">Choisir une formule</a></div>
  <div class="bandeau bandeau--danger"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg> Mode consultation : vous voyez le compte de <strong>ATELIER ALBERT</strong>. <a href="#">Revenir a mon compte</a></div>
</div>
.bandeau {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-align: center;
}
.bandeau svg { width: 16px; height: 16px; flex: none; }
.bandeau a { color: inherit; font-weight: var(--fw-bold); text-underline-offset: 3px; }
.bandeau--info   { background: var(--c-info);   color: #fff; }
.bandeau--danger { background: var(--c-danger); color: #fff; }
.bandeau--attention { background: var(--c-warn); color: #fff; }
@media (max-width: 560px) { .bandeau { flex-wrap: wrap; } }
flex w-full items-center justify-center gap-2 px-4 py-2 text-sm
Il manque le domaine apres le point, par exemple .fr ou .com.
Etablissement verifie : JABS, Neuilly-sur-Marne.
Message de champ

Attache au champ, pas en haut du formulaire. L'erreur dit ce qui ne va pas ET comment corriger : « format invalide » ne sert a personne.

Voir le codeMasquer le code
<div class="demo-col demo-col--large">
  <div class="champ is-erreur">
    <label class="champ__label">Adresse de courriel</label>
    <input class="saisie" value="dupont@exemple" aria-invalid="true" aria-describedby="e1">
    <span class="champ__erreur" id="e1"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg> Il manque le domaine apres le point, par exemple .fr ou .com.</span>
  </div>
  <div class="champ is-valide">
    <label class="champ__label">Numero SIRET</label>
    <span class="champ__enveloppe"><input class="saisie" value="902 123 456 00014"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg></span>
    <span class="champ__aide">Etablissement verifie : JABS, Neuilly-sur-Marne.</span>
  </div>
</div>
/* voir .champ.is-erreur et .champ.is-valide dans forms.css */
aria-invalid:border-red-500 + message lie par aria-describedby

B. Messages flottants

Notifications breves, en carte, empilees, et info-bulles.

Devis enregistre
Document supprime
Envoi echoue, verifiez la connexion
Notification breve

Confirmation d'une action reussie. Elle disparait seule apres 4 secondes ; une action irreversible propose toujours d'annuler.

Voir le codeMasquer le code
<div class="demo-col demo-col--large">
  <div class="toast toast--ok"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg><span>Devis enregistre</span></div>
  <div class="toast"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg><span>Document supprime</span><button class="btn btn--texte btn--dense">Annuler</button></div>
  <div class="toast toast--erreur"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg><span>Envoi echoue, verifiez la connexion</span><button class="btn btn--texte btn--dense">Reessayer</button></div>
</div>
.toast {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-full);
  background: var(--c-text);
  color: var(--c-surface);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  box-shadow: var(--sh-3);
}
.toast svg { width: 18px; height: 18px; flex: none; }
.toast span { flex: 1; }
.toast .btn--texte { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.toast--ok svg     { color: #4ade80; }
.toast--erreur svg { color: #f87171; }
fixed bottom-6 right-6 flex items-center gap-3 rounded-full bg-slate-900 px-4 py-3 text-white shadow-xl
Dossier 2026-103 complet

Toutes les pieces ont ete validees. Le versement interviendra sous 15 jours.

Notification en carte

Message plus long, ou qui doit rester a l'ecran. Titre, texte et fermeture explicite : il ne part pas tout seul.

Voir le codeMasquer le code
<div class="toast-carte toast-carte--ok">
  <span class="toast-carte__puce"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg></span>
  <div><strong>Dossier 2026-103 complet</strong><p>Toutes les pieces ont ete validees. Le versement interviendra sous 15 jours.</p></div>
  <button class="btn btn--fermer" aria-label="Fermer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
</div>
.toast-carte {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  width: 100%;
  max-width: 360px;
  padding: var(--sp-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  background: var(--c-surface);
  box-shadow: var(--sh-3);
}
.toast-carte__puce {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--r-full);
  background: var(--c-surface-3);
  color: var(--c-text-soft);
}
.toast-carte__puce svg { width: 17px; height: 17px; }
.toast-carte > div { flex: 1; min-width: 0; }
.toast-carte strong { font-size: var(--fs-md); font-weight: var(--fw-bold); }
.toast-carte p { margin: 3px 0 0; font-size: var(--fs-sm); color: var(--c-text-soft); line-height: 1.5; }
.toast-carte--ok .toast-carte__puce     { background: var(--c-success-soft); color: var(--c-success); }
.toast-carte--erreur .toast-carte__puce { background: var(--c-danger-soft);  color: var(--c-danger); }
flex w-80 gap-3 rounded-xl border bg-white p-4 shadow-xl
Devis 2026-121 enregistre
Courriel envoye a Sophie Martin
Photo trop volumineuse (42 Mo)
Pile de notifications

Plusieurs messages a la suite. Les plus recents en haut, et une limite de 3 visibles : au-dela, la pile masque l'application.

Voir le codeMasquer le code
<div class="toast-pile">
  <div class="toast toast--ok"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg><span>Devis 2026-121 enregistre</span></div>
  <div class="toast"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 6 10-6"/></svg><span>Courriel envoye a Sophie Martin</span></div>
  <div class="toast toast--erreur"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg><span>Photo trop volumineuse (42 Mo)</span></div>
</div>
.toast-pile { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2); }
.toast-pile .toast { animation: toast-entree var(--t-spring) both; }
@keyframes toast-entree {
  from { opacity: 0; transform: translateY(12px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
fixed bottom-6 right-6 flex flex-col-reverse gap-2
Info-bulle

Precision courte au survol ET au focus clavier. Elle ne porte jamais une information indispensable : tout le monde ne survole pas.

Voir le codeMasquer le code
<div class="demo-row demo-row--haute">
  <button class="btn btn--secondaire btn--bulle" data-bulle="Genere un PDF avec la mise en page de la societe">Exporter</button>
  <span class="bulle-info" data-bulle="Le prix indique comprend la pose et la mise en service" tabindex="0" role="button" aria-label="Precision sur le prix"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg></span>
</div>
.bulle-info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: var(--r-full);
  background: var(--c-surface-3);
  color: var(--c-text-muted);
  cursor: help;
  position: relative;
  overflow: visible;
}
.bulle-info svg { width: 13px; height: 13px; }
.bulle-info:hover { background: var(--c-primary-soft); color: var(--c-primary); }
.bulle-info::before {
  content: attr(data-bulle);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: 240px;
  padding: 7px 11px;
  border-radius: var(--r-sm);
  background: var(--c-text);
  color: var(--c-surface);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  line-height: 1.4;
  white-space: normal;
  text-align: left;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast), transform var(--t-fast);
  z-index: 20;
}
.bulle-info:hover::before, .bulle-info:focus-visible::before { opacity: 1; transform: translateX(-50%) translateY(0); }
group relative [&_span]:invisible group-hover:[&_span]:visible

C. Fenetres et tiroirs

Modale, confirmation destructrice, tiroir lateral, tiroir du bas.

Fenetre modale

Une decision qui bloque la suite. Fermeture par la croix, par Echap et par un clic sur le voile ; le bouton d'action reste a droite.

Voir le codeMasquer le code
<div class="modale-scene">
  <div class="modale__voile"></div>
  <div class="modale" role="dialog" aria-modal="true" aria-labelledby="m1">
    <div class="modale__tete">
      <h3 id="m1">Envoyer le devis au client</h3>
      <button class="btn btn--fermer" aria-label="Fermer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
    </div>
    <div class="modale__corps">
      <p>Le devis LTP-2026-103 sera envoye a <strong>julien@exemple.fr</strong>. Le client pourra le signer en ligne.</p>
      <label class="coche"><input type="checkbox" checked><span class="coche__boite"></span> M'envoyer une copie</label>
    </div>
    <div class="modale__pied">
      <button class="btn btn--secondaire">Annuler</button>
      <button class="btn btn--plein">Envoyer</button>
    </div>
  </div>
</div>
.modale-scene { position: relative; display: grid; place-items: center; width: 100%; min-height: 300px; padding: var(--sp-5); border-radius: var(--r-md); background: var(--c-surface-3); overflow: hidden; }
.modale__voile { position: absolute; inset: 0; background: var(--c-overlay); }
.modale {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 440px;
  border-radius: var(--r-lg);
  background: var(--c-surface);
  box-shadow: var(--sh-4);
  overflow: hidden;
}
.modale__tete { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--c-border); }
.modale__tete h3 { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-bold); }
.modale__corps { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-5); }
.modale__corps p { margin: 0; font-size: var(--fs-md); color: var(--c-text-soft); line-height: 1.55; }
.modale__pied { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--c-border); background: var(--c-surface-2); }
@media (max-width: 560px) { .modale__pied { flex-direction: column-reverse; } .modale__pied .btn { width: 100%; } }
fixed inset-0 z-50 grid place-items-center [&_.voile]:absolute [&_.voile]:inset-0 [&_.voile]:bg-black/45

Supprimer le dossier 2026-103 ?

Les 12 documents et les 3 interventions liees seront supprimes. Cette action est definitive.

Confirmation destructrice

Suppression definitive. Le bouton nomme l'action (« Supprimer le dossier »), jamais « OK » : on doit pouvoir confirmer sans relire la question.

Voir le codeMasquer le code
<div class="modale-scene">
  <div class="modale__voile"></div>
  <div class="modale modale--danger" role="alertdialog" aria-modal="true" aria-labelledby="m2">
    <div class="modale__corps modale__corps--centre">
      <span class="modale__puce"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg></span>
      <h3 id="m2">Supprimer le dossier 2026-103 ?</h3>
      <p>Les 12 documents et les 3 interventions liees seront supprimes. Cette action est definitive.</p>
    </div>
    <div class="modale__pied">
      <button class="btn btn--secondaire">Annuler</button>
      <button class="btn btn--destructif">Supprimer le dossier</button>
    </div>
  </div>
</div>
.modale--danger { max-width: 400px; }
.modale__corps--centre { align-items: center; text-align: center; padding-top: var(--sp-6); }
.modale__corps--centre h3 { margin: 0; font-size: var(--fs-xl); font-weight: var(--fw-bold); }
.modale__puce {
  display: inline-flex; align-items: center; justify-content: center;
  width: 48px; height: 48px;
  border-radius: var(--r-full);
  background: var(--c-danger-soft);
  color: var(--c-danger);
}
.modale__puce svg { width: 22px; height: 22px; }
.modale--danger .modale__pied { justify-content: center; }
role="alertdialog" + bouton qui nomme l'action, jamais « OK »
Tiroir lateral

Formulaire ou detail qui ne merite pas un changement de page. Il garde le contexte visible derriere, contrairement a une modale plein ecran.

Voir le codeMasquer le code
<div class="modale-scene modale-scene--tiroir">
  <div class="modale__voile"></div>
  <aside class="tiroir" role="dialog" aria-modal="true" aria-labelledby="t1">
    <div class="modale__tete"><h3 id="t1">Nouvelle intervention</h3><button class="btn btn--fermer" aria-label="Fermer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg></button></div>
    <div class="modale__corps">
      <div class="champ"><label class="champ__label">Type</label>
        <div class="saisie-select"><select class="saisie"><option>Entretien annuel</option><option>Depannage</option></select><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></div></div>
      <div class="champ"><label class="champ__label">Date</label><input class="saisie" type="date" value="2026-09-24"></div>
      <div class="champ"><label class="champ__label">Notes</label><textarea class="saisie" rows="3"></textarea></div>
    </div>
    <div class="modale__pied"><button class="btn btn--secondaire">Annuler</button><button class="btn btn--plein">Creer</button></div>
  </aside>
</div>
.modale-scene--tiroir { justify-items: end; padding: 0; }
.tiroir {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 380px;
  height: 100%;
  background: var(--c-surface);
  box-shadow: var(--sh-4);
}
.tiroir .modale__corps { flex: 1; overflow-y: auto; }
fixed inset-y-0 right-0 z-50 flex w-[420px] flex-col bg-white shadow-2xl max-sm:w-full
Tiroir du bas

L'equivalent mobile du tiroir lateral. La poignee dit qu'il se referme au glissement, et le contenu depasse la zone de l'encoche.

Voir le codeMasquer le code
<div class="modale-scene modale-scene--bas">
  <div class="modale__voile"></div>
  <div class="tiroir-bas" role="dialog" aria-modal="true">
    <span class="tiroir-bas__poignee"></span>
    <h3>Actions du dossier</h3>
    <nav>
      <button class="btn btn--fantome"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg> Modifier le dossier</button>
      <button class="btn btn--fantome"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg> Exporter en PDF</button>
      <button class="btn btn--fantome"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 6 10-6"/></svg> Envoyer au client</button>
      <button class="btn btn--fantome" style="color:var(--c-danger)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg> Supprimer</button>
    </nav>
  </div>
</div>
.modale-scene--bas { align-items: end; padding: 0; }
.tiroir-bas {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: var(--c-surface);
  box-shadow: 0 -8px 32px rgba(16,24,40,.18);
}
.tiroir-bas__poignee { width: 38px; height: 4px; margin: 0 auto var(--sp-2); border-radius: var(--r-full); background: var(--c-border-strong); }
.tiroir-bas h3 { margin: 0 0 var(--sp-1); font-size: var(--fs-md); font-weight: var(--fw-bold); }
.tiroir-bas nav { display: flex; flex-direction: column; }
.tiroir-bas nav .btn { justify-content: flex-start; height: 46px; }
fixed inset-x-0 bottom-0 rounded-t-2xl bg-white pb-[env(safe-area-inset-bottom)]

D. Progression

Barres determinees et indeterminees, anneau, etapes de traitement, compte a rebours.

Barres de progression

Determinee quand la duree est connue, indeterminee sinon. Ne jamais afficher une barre determinee figee : elle passe pour un blocage.

Voir le codeMasquer le code
<div class="demo-col demo-col--large">
  <div class="progres"><span style="--p: 34%"></span></div>
  <div class="progres progres--epais"><span style="--p: 72%"></span></div>
  <div class="progres progres--indetermine"><span></span></div>
</div>
.progres { width: 100%; height: 6px; border-radius: var(--r-full); background: var(--c-surface-3); overflow: hidden; }
.progres > span { display: block; height: 100%; width: var(--p, 0%); border-radius: inherit; background: var(--c-primary); transition: width var(--t-slow); }
.progres--epais { height: 12px; }
.progres--indetermine > span {
  width: 35%;
  animation: progres-va-et-vient 1.4s ease-in-out infinite;
}
@keyframes progres-va-et-vient {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(340%); }
}
h-2 w-full overflow-hidden rounded-full bg-slate-200
34 %
Anneau de progression

Compact, pose sur une vignette ou dans un tableau. Le chiffre au centre reste la vraie information, l'anneau n'est qu'un repere.

Voir le codeMasquer le code
<div class="demo-row">
  <span class="anneau" style="--p: 34"><em>34 %</em></span>
  <span class="anneau anneau--ok" style="--p: 100"><em><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg></em></span>
  <span class="anneau anneau--sm" style="--p: 62"></span>
</div>
.anneau {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 60px; height: 60px;
  border-radius: 50%;
  background: conic-gradient(var(--c-primary) calc(var(--p) * 1%), var(--c-surface-3) 0);
}
.anneau::after {
  content: "";
  position: absolute;
  inset: 7px;
  border-radius: 50%;
  background: var(--c-surface);
}
.anneau em {
  position: relative;
  z-index: 1;
  font-style: normal;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
}
.anneau em svg { width: 18px; height: 18px; display: block; }
.anneau--ok { background: conic-gradient(var(--c-success) calc(var(--p) * 1%), var(--c-surface-3) 0); }
.anneau--ok em { color: var(--c-success); }
.anneau--sm { width: 34px; height: 34px; }
.anneau--sm::after { inset: 5px; }
conic-gradient + mask radial pour evider le centre
  1. Devis genereTermine il y a 2 min
  2. PDF converti en Factur-XTermine il y a 1 min
  3. Envoi au clientEn cours...
  4. 4
    Attente de signatureA venir
Etapes de traitement

Traitement long en plusieurs phases : envoi, conversion, signature. L'etape en cours est nommee, pas seulement numerotee.

Voir le codeMasquer le code
<ol class="etapes">
  <li class="is-fait"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg><div><strong>Devis genere</strong><small>Termine il y a 2 min</small></div></li>
  <li class="is-fait"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m20 6-11 11-5-5"/></svg><div><strong>PDF converti en Factur-X</strong><small>Termine il y a 1 min</small></div></li>
  <li class="is-encours"><span class="btn__spin"></span><div><strong>Envoi au client</strong><small>En cours...</small></div></li>
  <li><em>4</em><div><strong>Attente de signature</strong><small>A venir</small></div></li>
</ol>
.etapes { list-style: none; margin: 0; padding: 0; width: 100%; position: relative; }
.etapes::before { content: ""; position: absolute; left: 14px; top: 16px; bottom: 22px; width: 2px; background: var(--c-border); }
.etapes li { position: relative; display: flex; align-items: flex-start; gap: var(--sp-3); padding-bottom: var(--sp-5); }
.etapes li:last-child { padding-bottom: 0; }
.etapes li > svg, .etapes li > em, .etapes li > .btn__spin {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r-full);
  background: var(--c-surface-3);
  border: 3px solid var(--c-surface);
  color: var(--c-text-muted);
  font-style: normal;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  box-sizing: border-box;
  z-index: 1;
}
.etapes li > svg { padding: 6px; }
.etapes li > .btn__spin { border: 2px solid color-mix(in srgb, var(--c-primary) 30%, transparent); border-top-color: var(--c-primary); background: transparent; width: 24px; height: 24px; margin: 3px; }
.etapes .is-fait > svg { background: var(--c-success); color: #fff; }
.etapes .is-fait strong { color: var(--c-text); }
.etapes .is-encours strong { color: var(--c-primary-ink); }
.etapes li strong { display: block; font-size: var(--fs-md); font-weight: var(--fw-semi); color: var(--c-text-muted); }
.etapes li small  { font-size: var(--fs-xs); color: var(--c-text-muted); }
relative [&_li]:flex [&_li]:gap-3 [&_li]:pb-6 before:absolute before:left-3.5 before:h-full before:w-px
Creneau reserve encore 04:32
Compte a rebours

Delai contraint : creneau reserve, code qui expire, offre limitee. Les chiffres sont tabulaires pour que la largeur ne bouge pas a chaque seconde.

Voir le codeMasquer le code
<div class="rebours">
  <span class="rebours__label">Creneau reserve encore</span>
  <span class="rebours__temps"><em>04</em>:<em>32</em></span>
  <div class="progres"><span style="--p: 45%"></span></div>
</div>
.rebours {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  max-width: 240px;
  padding: var(--sp-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: var(--c-surface);
  text-align: center;
}
.rebours__label { font-size: var(--fs-xs); color: var(--c-text-muted); }
.rebours__temps { font-size: 30px; font-weight: 800; color: var(--c-text); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.rebours__temps em { font-style: normal; }
tabular-nums [&_em]:not-italic [&_em]:text-2xl [&_em]:font-extrabold