25 CSS Glassmorphism Cards15 / 25

Pure CSSMIT licensed

Glassmorphism Notification Card Overlay

Overlay UI where blur is functional, not decorative: a toast stack that slides in staggered over a mock app (each with icon, copy, action and a draining time bar), and a notification-center panel with unread pulse dots and hover-revealed archive actions.

Published

Live Demo
Try it

The code

<div class="glz-15-group">
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
<section class="glz-15a" aria-label="Glass toast notifications demo">
  <div class="glz-15a__mock" aria-hidden="true">
    <div class="glz-15a__mockbar"><i></i><i></i><i></i></div>
    <div class="glz-15a__mockgrid"><b></b><b></b><b></b><b></b><b></b><b></b></div>
  </div>
  <div class="glz-15a__stack" role="log" aria-live="polite" aria-label="Notifications">
    <div class="glz-15a__toast" style="--tone:oklch(0.8 0.16 155);--d:.2s"><span class="glz-15a__ic">✓</span><div class="glz-15a__tx"><b>Deploy complete</b><em>codefronts.com is live — build #482 in 41s</em></div><button type="button">View</button></div>
    <div class="glz-15a__toast" style="--tone:oklch(0.78 0.13 230);--d:1.1s"><span class="glz-15a__ic">↓</span><div class="glz-15a__tx"><b>Export ready</b><em>glass-cards.zip · 2.4 MB</em></div><button type="button">Save</button></div>
    <div class="glz-15a__toast" style="--tone:oklch(0.8 0.14 75);--d:2s"><span class="glz-15a__ic">!</span><div class="glz-15a__tx"><b>Storage at 91%</b><em>Old previews can be cleared automatically</em></div><button type="button">Fix</button></div>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
<section class="glz-15b" aria-label="Glass notification center demo">
  <article class="glz-15b__panel">
    <header class="glz-15b__head"><h3>Notifications</h3><button type="button" class="glz-15b__clear">Mark all read</button></header>
    <div class="glz-15b__day">Today</div>
    <ul class="glz-15b__list">
      <li class="glz-15b__row glz-15b__row--new"><i class="glz-15b__dot" aria-label="Unread"></i><img src="https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?q=80&w=160&auto=format&fit=crop" alt="" loading="lazy"><div><b>Noor mentioned you</b><em>“@you the glass nav is 🔥 — ship it?”</em><time>2m</time></div><span class="glz-15b__act"><button type="button" aria-label="Reply">↩</button><button type="button" aria-label="Archive">🗄</button></span></li>
      <li class="glz-15b__row glz-15b__row--new"><i class="glz-15b__dot" aria-label="Unread"></i><span class="glz-15b__sys" aria-hidden="true">▲</span><div><b>Build passed</b><em>main · 128 tests · 38s</em><time>18m</time></div><span class="glz-15b__act"><button type="button" aria-label="Open build">→</button><button type="button" aria-label="Archive">🗄</button></span></li>
      <li class="glz-15b__row"><img src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=160&auto=format&fit=crop" alt="" loading="lazy"><div><b>Theo shared a file</b><em>pricing-v3.fig · 4.1 MB</em><time>1h</time></div><span class="glz-15b__act"><button type="button" aria-label="Download">↓</button><button type="button" aria-label="Archive">🗄</button></span></li>
    </ul>
  </article>
</section>
</div>
.glz-15-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.glz-15-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.glz-15a,
.glz-15a *,
.glz-15a *::before,
.glz-15a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.glz-15a {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #0d1120;
  font-family: 'Instrument Sans',system-ui,sans-serif;
}

.glz-15a__mock {
  position: absolute;
  inset: 8%;
  border-radius: 22px;
  background: linear-gradient(165deg,#161d33,#101527);
  border: 1px solid rgba(140,160,220,.15);
  padding: 18px;
  opacity: .9;
}

.glz-15a__mockbar {
  display: flex;
  gap: 7px;
}

.glz-15a__mockbar i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #2a3352;
}

.glz-15a__mockgrid {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: 14px;
  margin-top: 18px;
  height: calc(100% - 40px);
}

.glz-15a__mockgrid b {
  border-radius: 14px;
  background: linear-gradient(150deg,oklch(0.45 0.12 265/.5),oklch(0.4 0.1 320/.35));
  border: 1px solid rgba(140,160,220,.12);
}

.glz-15a__stack {
  position: relative;
  z-index: 2;
  width: min(360px,88vw);
  display: grid;
  gap: 12px;
  justify-self: end;
  align-self: start;
  margin: 6% 6% 0 0;
}

.glz-15a__toast {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px 16px;
  border-radius: 17px;
  color: #eef1fc;
  background: linear-gradient(160deg,rgba(255,255,255,.14),rgba(255,255,255,.05));
  backdrop-filter: blur(18px) saturate(170%);
  -webkit-backdrop-filter: blur(18px) saturate(170%);
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: 0 22px 46px -18px rgba(0,0,0,.75),inset 0 1px 0 rgba(255,255,255,.25);
  opacity: 0;
  transform: translateX(40px) scale(.95);
  animation: glz-15a-in .55s cubic-bezier(.2,.9,.3,1.2) var(--d) forwards;
}

.glz-15a__toast::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: 100%;
  background: var(--tone);
  transform-origin: left;
  animation: glz-15a-drain 6s linear calc(var(--d) + .5s) forwards;
}

.glz-15a__toast:hover::after {
  animation-play-state: paused;
}

.glz-15a__ic {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 11px;
  font-weight: 800;
  font-size: 14px;
  color: #0c1018;
  background: var(--tone);
  box-shadow: 0 0 16px color-mix(in oklab,var(--tone) 55%,transparent);
}

.glz-15a__tx {
  flex: 1;
  min-width: 0;
}

.glz-15a__tx b {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
}

.glz-15a__tx em {
  display: block;
  font-style: normal;
  font-size: 11.5px;
  color: rgba(238,241,252,.6);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.glz-15a__toast button {
  padding: 8px 12px;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,.25);
  background: rgba(255,255,255,.1);
  color: #fff;
  font: 700 11.5px 'Instrument Sans',sans-serif;
  cursor: pointer;
  transition: background .2s ease;
}

.glz-15a__toast button:hover {
  background: rgba(255,255,255,.2);
}

.glz-15a__toast button:focus-visible {
  outline: 2px solid var(--tone);
  outline-offset: 2px;
}

@keyframes glz-15a-in {
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes glz-15a-drain {
  to {
    transform: scaleX(0);
  }
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .glz-15a__toast {
    background: rgba(22,28,48,.94);
  }
}

@media (prefers-reduced-motion: reduce) {
  .glz-15a__toast {
    animation: glz-15a-in .001s var(--d) forwards;
  }

  .glz-15a__toast::after {
    animation: none;
    transform: scaleX(.4);
  }
}

.glz-15b,
.glz-15b *,
.glz-15b *::before,
.glz-15b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.glz-15b {
  --acc: oklch(0.75 0.17 300);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  position: relative;
  overflow: hidden;
  background: linear-gradient(200deg,#151129,#0d1020);
}

.glz-15b::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(44% 40% at 80% 16%,oklch(0.55 0.2 300/.4),transparent 70%),radial-gradient(40% 34% at 12% 86%,oklch(0.55 0.15 200/.35),transparent 70%);
}

.glz-15b__panel {
  position: relative;
  z-index: 1;
  width: min(410px,100%);
  padding: 22px;
  border-radius: 24px;
  color: #f0edfb;
  font-family: 'Instrument Sans',system-ui,sans-serif;
  background: linear-gradient(165deg,rgba(255,255,255,.12),rgba(255,255,255,.04));
  backdrop-filter: blur(24px) saturate(170%);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  border: 1px solid rgba(255,255,255,.2);
  box-shadow: 0 34px 70px -26px rgba(0,0,0,.85),inset 0 1px 0 rgba(255,255,255,.24);
}

.glz-15b__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.glz-15b__head h3 {
  font-size: 17px;
  font-weight: 700;
}

.glz-15b__clear {
  border: none;
  background: transparent;
  color: var(--acc);
  font: 600 12px 'Instrument Sans',sans-serif;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 8px;
}

.glz-15b__clear:hover {
  background: oklch(0.75 0.17 300/.14);
}

.glz-15b__clear:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

.glz-15b__day {
  margin-top: 14px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(240,237,251,.4);
}

.glz-15b__list {
  list-style: none;
  margin-top: 8px;
  display: grid;
  gap: 6px;
}

.glz-15b__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 12px 20px;
  border-radius: 16px;
  border: 1px solid transparent;
  transition: background .2s ease,border-color .2s ease;
}

.glz-15b__row:hover,
.glz-15b__row:focus-within {
  background: rgba(255,255,255,.07);
  border-color: rgba(255,255,255,.14);
}

.glz-15b__row--new {
  background: rgba(255,255,255,.045);
}

.glz-15b__dot {
  position: absolute;
  left: 7px;
  top: 50%;
  translate: 0 -50%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--acc);
  animation: glz-15b-pulse 2.4s ease-out infinite;
}

.glz-15b__row img {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
  background: #332a4d;
}

.glz-15b__sys {
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 13px;
  color: oklch(0.85 0.15 160);
  background: oklch(0.85 0.15 160/.12);
  border: 1px solid oklch(0.85 0.15 160/.35);
}

.glz-15b__row>div {
  flex: 1;
  min-width: 0;
  position: relative;
  padding-right: 26px;
}

.glz-15b__row b {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
}

.glz-15b__row em {
  display: block;
  font-style: normal;
  font-size: 12px;
  color: rgba(240,237,251,.6);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.glz-15b__row time {
  position: absolute;
  right: 0;
  top: 1px;
  font-size: 10.5px;
  color: rgba(240,237,251,.4);
}

.glz-15b__act {
  display: flex;
  gap: 6px;
  opacity: 0;
  translate: 6px 0;
  transition: opacity .2s ease,translate .2s ease;
}

.glz-15b__row:hover .glz-15b__act,
.glz-15b__row:focus-within .glz-15b__act {
  opacity: 1;
  translate: 0 0;
}

.glz-15b__act button {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,.2);
  background: rgba(255,255,255,.08);
  color: #f0edfb;
  font-size: 12px;
  cursor: pointer;
}

.glz-15b__act button:hover {
  background: rgba(255,255,255,.18);
}

.glz-15b__act button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

@keyframes glz-15b-pulse {
  0% {
    box-shadow: 0 0 0 0 oklch(0.75 0.17 300/.55);
  }

  70%,
    100% {
    box-shadow: 0 0 0 9px transparent;
  }
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .glz-15b__panel {
    background: rgba(26,21,44,.94);
  }
}

@media (prefers-reduced-motion: reduce) {
  .glz-15b__dot {
    animation: none;
  }

  .glz-15b__act {
    opacity: 1;
    translate: 0 0;
  }
}
/* No JavaScript — toasts stagger in via per-toast --d delays; each ::after bar drains over 6s and pauses while hovered, mirroring production toast behaviour. */

/* No JavaScript — unread dots pulse via expanding transparent box-shadow; row actions reveal on hover AND :focus-within (and stay visible under reduced-motion). */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Glassmorphism Card from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Glassmorphism Notification Card Overlay
Source: https://codefronts.com/components/css-glassmorphism-cards/glassmorphism-notification-card-overlay/

Overlay UI where blur is functional, not decorative: a toast stack that slides in staggered over a mock app (each with icon, copy, action and a draining time bar), and a notification-center panel with unread pulse dots and hover-revealed archive actions.
## HTML
```html
<div class="glz-15-group">
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
<section class="glz-15a" aria-label="Glass toast notifications demo">
  <div class="glz-15a__mock" aria-hidden="true">
    <div class="glz-15a__mockbar"><i></i><i></i><i></i></div>
    <div class="glz-15a__mockgrid"><b></b><b></b><b></b><b></b><b></b><b></b></div>
  </div>
  <div class="glz-15a__stack" role="log" aria-live="polite" aria-label="Notifications">
    <div class="glz-15a__toast" style="--tone:oklch(0.8 0.16 155);--d:.2s"><span class="glz-15a__ic">✓</span><div class="glz-15a__tx"><b>Deploy complete</b><em>codefronts.com is live — build #482 in 41s</em></div><button type="button">View</button></div>
    <div class="glz-15a__toast" style="--tone:oklch(0.78 0.13 230);--d:1.1s"><span class="glz-15a__ic">↓</span><div class="glz-15a__tx"><b>Export ready</b><em>glass-cards.zip · 2.4 MB</em></div><button type="button">Save</button></div>
    <div class="glz-15a__toast" style="--tone:oklch(0.8 0.14 75);--d:2s"><span class="glz-15a__ic">!</span><div class="glz-15a__tx"><b>Storage at 91%</b><em>Old previews can be cleared automatically</em></div><button type="button">Fix</button></div>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
<section class="glz-15b" aria-label="Glass notification center demo">
  <article class="glz-15b__panel">
    <header class="glz-15b__head"><h3>Notifications</h3><button type="button" class="glz-15b__clear">Mark all read</button></header>
    <div class="glz-15b__day">Today</div>
    <ul class="glz-15b__list">
      <li class="glz-15b__row glz-15b__row--new"><i class="glz-15b__dot" aria-label="Unread"></i><img src="https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?q=80&w=160&auto=format&fit=crop" alt="" loading="lazy"><div><b>Noor mentioned you</b><em>“@you the glass nav is 🔥 — ship it?”</em><time>2m</time></div><span class="glz-15b__act"><button type="button" aria-label="Reply">↩</button><button type="button" aria-label="Archive">🗄</button></span></li>
      <li class="glz-15b__row glz-15b__row--new"><i class="glz-15b__dot" aria-label="Unread"></i><span class="glz-15b__sys" aria-hidden="true">▲</span><div><b>Build passed</b><em>main · 128 tests · 38s</em><time>18m</time></div><span class="glz-15b__act"><button type="button" aria-label="Open build">→</button><button type="button" aria-label="Archive">🗄</button></span></li>
      <li class="glz-15b__row"><img src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=160&auto=format&fit=crop" alt="" loading="lazy"><div><b>Theo shared a file</b><em>pricing-v3.fig · 4.1 MB</em><time>1h</time></div><span class="glz-15b__act"><button type="button" aria-label="Download">↓</button><button type="button" aria-label="Archive">🗄</button></span></li>
    </ul>
  </article>
</section>
</div>
```
## CSS
```css
.glz-15-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.glz-15-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.glz-15a,
.glz-15a *,
.glz-15a *::before,
.glz-15a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.glz-15a {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #0d1120;
  font-family: 'Instrument Sans',system-ui,sans-serif;
}

.glz-15a__mock {
  position: absolute;
  inset: 8%;
  border-radius: 22px;
  background: linear-gradient(165deg,#161d33,#101527);
  border: 1px solid rgba(140,160,220,.15);
  padding: 18px;
  opacity: .9;
}

.glz-15a__mockbar {
  display: flex;
  gap: 7px;
}

.glz-15a__mockbar i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #2a3352;
}

.glz-15a__mockgrid {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: 14px;
  margin-top: 18px;
  height: calc(100% - 40px);
}

.glz-15a__mockgrid b {
  border-radius: 14px;
  background: linear-gradient(150deg,oklch(0.45 0.12 265/.5),oklch(0.4 0.1 320/.35));
  border: 1px solid rgba(140,160,220,.12);
}

.glz-15a__stack {
  position: relative;
  z-index: 2;
  width: min(360px,88vw);
  display: grid;
  gap: 12px;
  justify-self: end;
  align-self: start;
  margin: 6% 6% 0 0;
}

.glz-15a__toast {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px 16px;
  border-radius: 17px;
  color: #eef1fc;
  background: linear-gradient(160deg,rgba(255,255,255,.14),rgba(255,255,255,.05));
  backdrop-filter: blur(18px) saturate(170%);
  -webkit-backdrop-filter: blur(18px) saturate(170%);
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: 0 22px 46px -18px rgba(0,0,0,.75),inset 0 1px 0 rgba(255,255,255,.25);
  opacity: 0;
  transform: translateX(40px) scale(.95);
  animation: glz-15a-in .55s cubic-bezier(.2,.9,.3,1.2) var(--d) forwards;
}

.glz-15a__toast::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: 100%;
  background: var(--tone);
  transform-origin: left;
  animation: glz-15a-drain 6s linear calc(var(--d) + .5s) forwards;
}

.glz-15a__toast:hover::after {
  animation-play-state: paused;
}

.glz-15a__ic {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 11px;
  font-weight: 800;
  font-size: 14px;
  color: #0c1018;
  background: var(--tone);
  box-shadow: 0 0 16px color-mix(in oklab,var(--tone) 55%,transparent);
}

.glz-15a__tx {
  flex: 1;
  min-width: 0;
}

.glz-15a__tx b {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
}

.glz-15a__tx em {
  display: block;
  font-style: normal;
  font-size: 11.5px;
  color: rgba(238,241,252,.6);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.glz-15a__toast button {
  padding: 8px 12px;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,.25);
  background: rgba(255,255,255,.1);
  color: #fff;
  font: 700 11.5px 'Instrument Sans',sans-serif;
  cursor: pointer;
  transition: background .2s ease;
}

.glz-15a__toast button:hover {
  background: rgba(255,255,255,.2);
}

.glz-15a__toast button:focus-visible {
  outline: 2px solid var(--tone);
  outline-offset: 2px;
}

@keyframes glz-15a-in {
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes glz-15a-drain {
  to {
    transform: scaleX(0);
  }
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .glz-15a__toast {
    background: rgba(22,28,48,.94);
  }
}

@media (prefers-reduced-motion: reduce) {
  .glz-15a__toast {
    animation: glz-15a-in .001s var(--d) forwards;
  }

  .glz-15a__toast::after {
    animation: none;
    transform: scaleX(.4);
  }
}

.glz-15b,
.glz-15b *,
.glz-15b *::before,
.glz-15b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.glz-15b {
  --acc: oklch(0.75 0.17 300);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  position: relative;
  overflow: hidden;
  background: linear-gradient(200deg,#151129,#0d1020);
}

.glz-15b::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(44% 40% at 80% 16%,oklch(0.55 0.2 300/.4),transparent 70%),radial-gradient(40% 34% at 12% 86%,oklch(0.55 0.15 200/.35),transparent 70%);
}

.glz-15b__panel {
  position: relative;
  z-index: 1;
  width: min(410px,100%);
  padding: 22px;
  border-radius: 24px;
  color: #f0edfb;
  font-family: 'Instrument Sans',system-ui,sans-serif;
  background: linear-gradient(165deg,rgba(255,255,255,.12),rgba(255,255,255,.04));
  backdrop-filter: blur(24px) saturate(170%);
  -webkit-backdrop-filter: blur(24px) saturate(170%);
  border: 1px solid rgba(255,255,255,.2);
  box-shadow: 0 34px 70px -26px rgba(0,0,0,.85),inset 0 1px 0 rgba(255,255,255,.24);
}

.glz-15b__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.glz-15b__head h3 {
  font-size: 17px;
  font-weight: 700;
}

.glz-15b__clear {
  border: none;
  background: transparent;
  color: var(--acc);
  font: 600 12px 'Instrument Sans',sans-serif;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 8px;
}

.glz-15b__clear:hover {
  background: oklch(0.75 0.17 300/.14);
}

.glz-15b__clear:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

.glz-15b__day {
  margin-top: 14px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(240,237,251,.4);
}

.glz-15b__list {
  list-style: none;
  margin-top: 8px;
  display: grid;
  gap: 6px;
}

.glz-15b__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 12px 20px;
  border-radius: 16px;
  border: 1px solid transparent;
  transition: background .2s ease,border-color .2s ease;
}

.glz-15b__row:hover,
.glz-15b__row:focus-within {
  background: rgba(255,255,255,.07);
  border-color: rgba(255,255,255,.14);
}

.glz-15b__row--new {
  background: rgba(255,255,255,.045);
}

.glz-15b__dot {
  position: absolute;
  left: 7px;
  top: 50%;
  translate: 0 -50%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--acc);
  animation: glz-15b-pulse 2.4s ease-out infinite;
}

.glz-15b__row img {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  object-fit: cover;
  background: #332a4d;
}

.glz-15b__sys {
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 13px;
  color: oklch(0.85 0.15 160);
  background: oklch(0.85 0.15 160/.12);
  border: 1px solid oklch(0.85 0.15 160/.35);
}

.glz-15b__row>div {
  flex: 1;
  min-width: 0;
  position: relative;
  padding-right: 26px;
}

.glz-15b__row b {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
}

.glz-15b__row em {
  display: block;
  font-style: normal;
  font-size: 12px;
  color: rgba(240,237,251,.6);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.glz-15b__row time {
  position: absolute;
  right: 0;
  top: 1px;
  font-size: 10.5px;
  color: rgba(240,237,251,.4);
}

.glz-15b__act {
  display: flex;
  gap: 6px;
  opacity: 0;
  translate: 6px 0;
  transition: opacity .2s ease,translate .2s ease;
}

.glz-15b__row:hover .glz-15b__act,
.glz-15b__row:focus-within .glz-15b__act {
  opacity: 1;
  translate: 0 0;
}

.glz-15b__act button {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,.2);
  background: rgba(255,255,255,.08);
  color: #f0edfb;
  font-size: 12px;
  cursor: pointer;
}

.glz-15b__act button:hover {
  background: rgba(255,255,255,.18);
}

.glz-15b__act button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

@keyframes glz-15b-pulse {
  0% {
    box-shadow: 0 0 0 0 oklch(0.75 0.17 300/.55);
  }

  70%,
    100% {
    box-shadow: 0 0 0 9px transparent;
  }
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .glz-15b__panel {
    background: rgba(26,21,44,.94);
  }
}

@media (prefers-reduced-motion: reduce) {
  .glz-15b__dot {
    animation: none;
  }

  .glz-15b__act {
    opacity: 1;
    translate: 0 0;
  }
}
```

## JavaScript
```js
/* No JavaScript — toasts stagger in via per-toast --d delays; each ::after bar drains over 6s and pauses while hovered, mirroring production toast behaviour. */

/* No JavaScript — unread dots pulse via expanding transparent box-shadow; row actions reveal on hover AND :focus-within (and stay visible under reduced-motion). */
```

How this works

Each toast enters with a spring-ish transform and its lifetime is visualised by a scaling ::after bar — pausing on hover by suspending the animation, exactly how real toast libraries behave:

.toast{animation:in .55s cubic-bezier(.2,.9,.3,1.2) both}
.toast::after{transform-origin:left;animation:drain 6s linear forwards}
.toast:hover::after{animation-play-state:paused}
@keyframes drain{to{transform:scaleX(0)}}

The stack is a fixed-width column pinned to the stage corner; toasts get incremental animation-delays so they arrive as a sequence. Under the overlay sits a deliberately busy mock UI — the point of the demo is watching the glass separate layers. The tray variant pulses unread dots with an expanding transparent box-shadow (no layout impact) and reveals actions on row hover/focus.

Make it yours

  • Toast lifetime = the drain duration; sync your JS removal timer to it.
  • Stagger rhythm is 0.9s between delays — tighten for chat-like bursts.
  • Severity theming: each toast takes --tone (success/info/warn) feeding icon chip and bar.
  • Tray rows: the action column is any number of buttons; it fades in as a group.

Gotchas — read before shipping

  • Toasts must be aria-live='polite' (the container, not each toast) or screen readers announce nothing.
  • Never blur the ENTIRE backdrop under a toast — blur the toast itself; full-screen backdrop-filter repaints everything on scroll.
  • Hover-paused drains need JS parity in production (pause the removal timer too) or the bar lies.

Browser support

ChromeSafariFirefoxEdge
111+16.2+113+111+

Floor set by oklch(), color-mix(), backdrop-filter as this demo is written. The core technique itself goes back further — Chrome 76+.

Pure Baseline CSS; the demo is presentation-layer only — wire real timers in JS.

Techniques used in this demo

Search CodeFronts

Loading…