feedback · toast · notification

Toast / Notification

Pile de notifications flottantes (succès, info, avertissement, erreur), chacune avec un bouton de fermeture fonctionnel.

Voir le code source
<style>
.bn-toast-stack {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;

  max-width: 340px;
  padding: 2rem;
  font-family: 'Inter', sans-serif;
  background: #0B0F10;
}

.bn-toast {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;

  padding: 0.9rem 1rem;
  border-radius: 8px;
  border: 1px solid;
  background: rgba(255, 255, 255, 0.02);

  transition: opacity 0.25s ease, transform 0.25s ease;
}

.bn-toast.is-closing {
  opacity: 0;
  transform: translateX(20px);
}

.bn-toast__icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;

  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
}

.bn-toast__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.bn-toast__body strong {
  color: #E6E4DD;
  font-size: 0.85rem;
}

.bn-toast__body span {
  color: #979793;
  font-size: 0.78rem;
}

.bn-toast__close {
  flex-shrink: 0;
  background: none;
  border: none;
  color: #5C6160;
  cursor: pointer;
  font-size: 0.7rem;
}

.bn-toast__close:hover {
  color: #E6E4DD;
}

/* --- Variantes de couleur --- */
.bn-toast--success { border-color: rgba(63, 185, 80, 0.35); }
.bn-toast--success .bn-toast__icon { background: rgba(63, 185, 80, 0.15); color: #3FB950; }

.bn-toast--info { border-color: rgba(96, 165, 250, 0.35); }
.bn-toast--info .bn-toast__icon { background: rgba(96, 165, 250, 0.15); color: #60A5FA; }

.bn-toast--warning { border-color: rgba(245, 197, 24, 0.35); }
.bn-toast--warning .bn-toast__icon { background: rgba(245, 197, 24, 0.15); color: #F5C518; }

.bn-toast--error { border-color: rgba(229, 72, 77, 0.35); }
.bn-toast--error .bn-toast__icon { background: rgba(229, 72, 77, 0.15); color: #E5484D; }

</style>

<div class="bn-toast-stack" data-fx-root>
  <div class="bn-toast bn-toast--success">
    <span class="bn-toast__icon">✓</span>
    <div class="bn-toast__body">
      <strong>Projet enregistré</strong>
      <span>Votre projet a bien été enregistré.</span>
    </div>
    <button type="button" class="bn-toast__close" aria-label="Fermer">✕</button>
  </div>

  <div class="bn-toast bn-toast--info">
    <span class="bn-toast__icon">i</span>
    <div class="bn-toast__body">
      <strong>Mise à jour disponible</strong>
      <span>Une nouvelle version est disponible.</span>
    </div>
    <button type="button" class="bn-toast__close" aria-label="Fermer">✕</button>
  </div>

  <div class="bn-toast bn-toast--warning">
    <span class="bn-toast__icon">!</span>
    <div class="bn-toast__body">
      <strong>Modification non enregistrée</strong>
      <span>Vos changements seront perdus.</span>
    </div>
    <button type="button" class="bn-toast__close" aria-label="Fermer">✕</button>
  </div>

  <div class="bn-toast bn-toast--error">
    <span class="bn-toast__icon">✕</span>
    <div class="bn-toast__body">
      <strong>Erreur de connexion</strong>
      <span>Impossible de se connecter au serveur.</span>
    </div>
    <button type="button" class="bn-toast__close" aria-label="Fermer">✕</button>
  </div>
</div>


<script>
/* ==========================================================
   TOAST / NOTIFICATION
   Chaque bouton "✕" fait disparaître SA notification (fondu +
   léger déplacement) puis la retire du DOM.
   ========================================================== */

(function () {
  const root = document.querySelector('.bn-toast-stack[data-fx-root]');
  if (!root) return;

  root.querySelectorAll('.bn-toast__close').forEach((btn) => {
    btn.addEventListener('click', () => {
      const toast = btn.closest('.bn-toast');
      toast.classList.add('is-closing');
      toast.addEventListener('transitionend', () => toast.remove(), { once: true });
    });
  });
})();

</script>