Snackbar

Temporary message often containing an action.

Conversation archived

html

<div class="snackbar" role="status" aria-live="polite">
  <p class="snackbar__message">Conversation archived</p>
  <button type="button" class="snackbar__action">Undo</button>
  <span class="snackbar__timer" aria-hidden="true" style="animation-duration:5s;"></span>
</div>
<style>
.snackbar { position: relative; display: flex; align-items: center; gap: 16px; max-width: 380px; min-width: 260px;
  padding: 12px 16px; box-sizing: border-box; border-radius: 10px; background: #241430; color: #FAF7F2;
  font: 13.5px/1.4 system-ui, sans-serif; overflow: hidden; box-shadow: 0 10px 28px rgba(36,20,48,.25); }
.snackbar__message { flex: 1; margin: 0; }
.snackbar__action { border: none; background: transparent; color: #A8E6C1; font: 700 12.5px system-ui, sans-serif;
  cursor: pointer; padding: 4px 6px; border-radius: 4px; white-space: nowrap; }
.snackbar__action:hover { background: #FAF7F218; }
.snackbar__action:focus-visible { outline: 2px solid #FAF7F2; outline-offset: 2px; }
.snackbar__timer { position: absolute; left: 0; bottom: 0; height: 3px; width: 100%; background: #A8E6C1;
  transform-origin: left; }
@keyframes snackbar-shrink { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: no-preference) {
  .snackbar__timer { animation-name: snackbar-shrink; animation-timing-function: linear; animation-fill-mode: forwards; }
}
</style>

tailwind

<div role="status" aria-live="polite" class="snackbar-tw relative flex items-center gap-4 max-w-sm min-w-[260px] px-4 py-3 box-border rounded-lg bg-[#241430] text-[#FAF7F2] font-sans text-[13.5px] overflow-hidden shadow-lg">
  <p class="flex-1 m-0">Conversation archived</p>
  <button type="button" class="border-0 bg-transparent text-[#A8E6C1] font-bold text-[12.5px] cursor-pointer px-1.5 py-1 rounded whitespace-nowrap hover:bg-white/10">Undo</button>
  <span aria-hidden="true" class="snackbar-tw__timer absolute left-0 bottom-0 h-[3px] w-full bg-[#A8E6C1] origin-left" style="animation-duration:5s;"></span>
</div>
<style>
@keyframes snackbar-tw-shrink { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: no-preference) {
  .snackbar-tw__timer { animation-name: snackbar-tw-shrink; animation-timing-function: linear; animation-fill-mode: forwards; }
}
</style>

react

export function Snackbar({
  message = "Conversation archived",
  actionLabel = "Undo",
  showAction = true,
  duration = 5,
}) {
  return (
    <div role="status" aria-live="polite" style={{ position: "relative", display: "flex", alignItems: "center", gap: 16,
      maxWidth: 380, minWidth: 260, padding: "12px 16px", boxSizing: "border-box", borderRadius: 10, background: "#241430",
      color: "#FAF7F2", font: "13.5px/1.4 system-ui, sans-serif", overflow: "hidden", boxShadow: "0 10px 28px rgba(36,20,48,.25)" }}>
      <p style={{ flex: 1, margin: 0 }}>{message}</p>
      {showAction && (
        <button type="button" style={{ border: "none", background: "transparent", color: "#A8E6C1", fontWeight: 700,
          fontSize: 12.5, cursor: "pointer", padding: "4px 6px", borderRadius: 4, whiteSpace: "nowrap" }}>{actionLabel}</button>
      )}
      <span aria-hidden="true" className="snackbar-timer" style={{ position: "absolute", left: 0, bottom: 0, height: 3, width: "100%", background: "#A8E6C1",
        transformOrigin: "left" }} />
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          .snackbar-timer { animation: snackbar-react-shrink ${duration}s linear forwards; }
          @keyframes snackbar-react-shrink { from { transform: scaleX(1); } to { transform: scaleX(0); } }
        }
      `}</style>
    </div>
  );
}