30 CSS Notifications17 / 30

CSS + JSMIT licensed

Floating Bottom Alert Bar CSS

Two floating-bar temperaments: a cookie-consent bar that rises on an @starting-style transition, floats on a soft double shadow, and handles Accept/Preferences with a collapse exit — and a slim pill promo bar that shows the small-screen discipline: max-inline-size, safe-area-inset-bottom padding, and a snooze control that re-docks it as a corner chip instead of killing it.

Published

Live Demo
Try it

The code

<div class="ntf-17-group">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-17a" aria-label="Floating cookie consent bar">
  <div class="ntf-17a__page">
    <h2 class="ntf-17a__ghost">Your product UI</h2>
    <p class="ntf-17a__ghostsub">the bar floats over it, capped at 720px, lifted 16px off the bottom</p>
    <button class="ntf-17a__again" id="ntf-17a-again" type="button" hidden>Show the bar again</button>
  </div>
  <div class="ntf-17a__bar" id="ntf-17a-bar" role="region" aria-label="Cookie consent">
    <span class="ntf-17a__crumb" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"/><path d="M8.5 8.5v.01M16 15.5v.01M12 12v.01M11 17v.01M7 14v.01"/></svg></span>
    <p class="ntf-17a__tx"><b>We use cookies</b> for sign-in and anonymous usage stats — never for ads. <a href="#" onclick="return false">Cookie policy</a></p>
    <div class="ntf-17a__acts">
      <button class="ntf-17a__ghostbtn" data-ntf-17a="rejected" type="button">Reject non-essential</button>
      <button class="ntf-17a__solid" data-ntf-17a="accepted" type="button">Accept all</button>
    </div>
  </div>
  <p class="ntf-17a__verdict" id="ntf-17a-verdict" role="status" aria-live="polite"></p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-17b" aria-label="Floating promo pill that snoozes into a corner chip">
  <div class="ntf-17b__page">
    <h2 class="ntf-17b__ghost">Storefront</h2>
    <p class="ntf-17b__ghostsub">“Later” doesn't kill the promo — it morphs the same element into a corner chip</p>
  </div>
  <div class="ntf-17b__pill" id="ntf-17b-pill" role="region" aria-label="Free shipping offer">
    <span class="ntf-17b__spark" aria-hidden="true">&#10022;</span>
    <p class="ntf-17b__tx"><b>Free shipping</b> on orders over $40 — ends Sunday</p>
    <button class="ntf-17b__go" type="button">Shop now</button>
    <button class="ntf-17b__later" id="ntf-17b-later" type="button" aria-label="Snooze offer to corner">Later</button>
  </div>
</section>
</div>
.ntf-17-group {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.ntf-17-group > section {
  width: 100%;
}

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

.ntf-17a {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: radial-gradient(130% 110% at 50% 0%,#181d2b,#0c0f16);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-17a__page {
  text-align: center;
  padding: 24px;
}

.ntf-17a__ghost {
  font-size: clamp(26px,4vw,38px);
  font-weight: 600;
  letter-spacing: -.02em;
  color: rgba(226,234,250,.25);
}

.ntf-17a__ghostsub {
  margin-top: 8px;
  font-size: 13px;
  color: rgba(226,234,250,.18);
}

.ntf-17a__again {
  margin-top: 18px;
  padding: 10px 18px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: #e2eafa;
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(165,190,230,.25);
  border-radius: 10px;
  cursor: pointer;
}

.ntf-17a__bar {
  position: absolute;
  inset-inline: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom,0px));
  margin-inline: auto;
  max-inline-size: 720px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 16px 18px;
  border-radius: 18px;
  background: #f8fafd;
  box-shadow: 0 2px 6px rgba(8,10,16,.35),0 30px 60px -20px rgba(8,10,16,.7);
  transition: translate .5s cubic-bezier(.22,1.2,.36,1),opacity .35s ease,display .5s allow-discrete;
}

@starting-style {
  .ntf-17a__bar {
    translate: 0 24px;
    opacity: 0;
  }
}

.ntf-17a__bar.is-gone {
  translate: 0 24px;
  opacity: 0;
  display: none;
}

.ntf-17a__crumb {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  color: #f8fafd;
  background: #20242f;
}

.ntf-17a__crumb svg {
  width: 20px;
  height: 20px;
}

.ntf-17a__tx {
  flex: 1 1 260px;
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(29,33,43,.8);
}

.ntf-17a__tx b {
  color: #171a22;
  font-weight: 700;
}

.ntf-17a__tx a {
  color: oklch(0.5 0.15 260);
  font-weight: 600;
}

.ntf-17a__tx a:hover {
  color: oklch(0.42 0.15 260);
}

.ntf-17a__acts {
  display: flex;
  gap: 9px;
  margin-left: auto;
}

.ntf-17a__ghostbtn,
.ntf-17a__solid {
  padding: 11px 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  border-radius: 11px;
  cursor: pointer;
  transition: scale .12s,background .2s;
}

.ntf-17a__ghostbtn {
  color: #20242f;
  background: transparent;
  border: 1.5px solid rgba(29,33,43,.25);
}

.ntf-17a__ghostbtn:hover {
  background: rgba(29,33,43,.06);
}

.ntf-17a__solid {
  color: #f8fafd;
  background: #20242f;
  border: 1.5px solid #20242f;
}

.ntf-17a__solid:hover {
  background: #333947;
}

.ntf-17a__ghostbtn:active,
.ntf-17a__solid:active {
  scale: .96;
}

.ntf-17a__verdict {
  position: absolute;
  bottom: 18px;
  inset-inline: 0;
  text-align: center;
  font-size: 12.5px;
  color: rgba(226,234,250,.55);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-17a__bar {
    transition-duration: .01s;
  }

  @starting-style {
    .ntf-17a__bar {
      translate: 0 0;
    }
  }
}

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

.ntf-17b {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: linear-gradient(180deg,#f7f2e8,#eee5d1);
  font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}

.ntf-17b__page {
  text-align: center;
  padding: 24px;
}

.ntf-17b__ghost {
  font-size: clamp(28px,4.5vw,40px);
  font-weight: 700;
  letter-spacing: -.02em;
  color: rgba(37,32,24,.22);
}

.ntf-17b__ghostsub {
  margin-top: 8px;
  font-size: 13px;
  color: rgba(37,32,24,.35);
}

.ntf-17b__pill {
  position: absolute;
  left: 50%;
  bottom: calc(18px + env(safe-area-inset-bottom,0px));
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: 12px;
  max-inline-size: min(560px,calc(100% - 32px));
  padding: 10px 10px 10px 18px;
  border-radius: 999px;
  background: #252018;
  color: #f7f2e8;
  box-shadow: 0 2px 6px rgba(37,32,24,.3),0 26px 50px -18px rgba(37,32,24,.55);
  transition: left .55s cubic-bezier(.4,0,.2,1),translate .55s cubic-bezier(.4,0,.2,1),padding .45s,gap .45s,bottom .45s;
  white-space: nowrap;
}

@starting-style {
  .ntf-17b__pill {
    translate: -50% 22px;
    opacity: 0;
  }
}

.ntf-17b__spark {
  flex: none;
  font-size: 17px;
  color: oklch(0.85 0.14 85);
  animation: ntf-17b-spin 5s linear infinite;
}

@keyframes ntf-17b-spin {
  to {
    rotate: 360deg;
  }
}

.ntf-17b__tx {
  font-size: 13.5px;
  overflow: hidden;
  max-inline-size: 340px;
  transition: max-inline-size .45s,opacity .3s;
}

.ntf-17b__tx b {
  font-weight: 800;
  color: oklch(0.9 0.11 85);
}

.ntf-17b__go {
  flex: none;
  padding: 10px 16px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 800;
  color: #252018;
  background: oklch(0.85 0.14 85);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: scale .12s,max-inline-size .45s,padding .45s,opacity .3s;
  max-inline-size: 120px;
  overflow: hidden;
}

.ntf-17b__go:active {
  scale: .95;
}

.ntf-17b__later {
  flex: none;
  padding: 10px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: rgba(247,242,232,.65);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color .15s,max-inline-size .45s,padding .45s,opacity .3s;
  max-inline-size: 80px;
  overflow: hidden;
}

.ntf-17b__later:hover {
  color: #f7f2e8;
}

.ntf-17b__pill.is-chip {
  left: calc(100% - 16px);
  translate: -100% 0;
  padding: 10px 14px;
  gap: 0;
}

.ntf-17b__pill.is-chip .ntf-17b__tx,
.ntf-17b__pill.is-chip .ntf-17b__go,
.ntf-17b__pill.is-chip .ntf-17b__later {
  max-inline-size: 0;
  padding-inline: 0;
  opacity: 0;
  pointer-events: none;
}

.ntf-17b__pill.is-chip .ntf-17b__spark {
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .ntf-17b__pill,
    .ntf-17b__tx,
    .ntf-17b__go,
    .ntf-17b__later {
    transition-duration: .01s;
  }

  .ntf-17b__spark {
    animation: none;
  }
}
(function(){
  var bar=document.getElementById('ntf-17a-bar'); if(!bar) return;
  var verdict=document.getElementById('ntf-17a-verdict'),again=document.getElementById('ntf-17a-again');
  bar.querySelectorAll('[data-ntf-17a]').forEach(function(btn){
    btn.addEventListener('click',function(){
      bar.classList.add('is-gone');
      verdict.textContent='Choice recorded: '+btn.getAttribute('data-ntf-17a')+' (persist it in localStorage / a consent cookie).';
      again.hidden=false;
    });
  });
  again.addEventListener('click',function(){
    bar.classList.remove('is-gone'); verdict.textContent=''; again.hidden=true;
  });
})();

(function(){
  var pill=document.getElementById('ntf-17b-pill'); if(!pill) return;
  document.getElementById('ntf-17b-later').addEventListener('click',function(){
    pill.classList.add('is-chip');
    pill.setAttribute('aria-label','Snoozed offer — select to expand');
  });
  pill.addEventListener('click',function(){
    if(pill.classList.contains('is-chip')){
      pill.classList.remove('is-chip');
      pill.setAttribute('aria-label','Free shipping offer');
    }
  });
})();
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 Notification 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: Floating Bottom Alert Bar CSS
Source: https://codefronts.com/snippets/css-notifications/floating-bottom-alert-bar-css/

Two floating-bar temperaments: a cookie-consent bar that rises on an @starting-style transition, floats on a soft double shadow, and handles Accept/Preferences with a collapse exit — and a slim pill promo bar that shows the small-screen discipline: max-inline-size, safe-area-inset-bottom padding, and a snooze control that re-docks it as a corner chip instead of killing it.
## HTML
```html
<div class="ntf-17-group">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-17a" aria-label="Floating cookie consent bar">
  <div class="ntf-17a__page">
    <h2 class="ntf-17a__ghost">Your product UI</h2>
    <p class="ntf-17a__ghostsub">the bar floats over it, capped at 720px, lifted 16px off the bottom</p>
    <button class="ntf-17a__again" id="ntf-17a-again" type="button" hidden>Show the bar again</button>
  </div>
  <div class="ntf-17a__bar" id="ntf-17a-bar" role="region" aria-label="Cookie consent">
    <span class="ntf-17a__crumb" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"/><path d="M8.5 8.5v.01M16 15.5v.01M12 12v.01M11 17v.01M7 14v.01"/></svg></span>
    <p class="ntf-17a__tx"><b>We use cookies</b> for sign-in and anonymous usage stats — never for ads. <a href="#" onclick="return false">Cookie policy</a></p>
    <div class="ntf-17a__acts">
      <button class="ntf-17a__ghostbtn" data-ntf-17a="rejected" type="button">Reject non-essential</button>
      <button class="ntf-17a__solid" data-ntf-17a="accepted" type="button">Accept all</button>
    </div>
  </div>
  <p class="ntf-17a__verdict" id="ntf-17a-verdict" role="status" aria-live="polite"></p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-17b" aria-label="Floating promo pill that snoozes into a corner chip">
  <div class="ntf-17b__page">
    <h2 class="ntf-17b__ghost">Storefront</h2>
    <p class="ntf-17b__ghostsub">“Later” doesn't kill the promo — it morphs the same element into a corner chip</p>
  </div>
  <div class="ntf-17b__pill" id="ntf-17b-pill" role="region" aria-label="Free shipping offer">
    <span class="ntf-17b__spark" aria-hidden="true">&#10022;</span>
    <p class="ntf-17b__tx"><b>Free shipping</b> on orders over $40 — ends Sunday</p>
    <button class="ntf-17b__go" type="button">Shop now</button>
    <button class="ntf-17b__later" id="ntf-17b-later" type="button" aria-label="Snooze offer to corner">Later</button>
  </div>
</section>
</div>
```
## CSS
```css
.ntf-17-group {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.ntf-17-group > section {
  width: 100%;
}

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

.ntf-17a {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: radial-gradient(130% 110% at 50% 0%,#181d2b,#0c0f16);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-17a__page {
  text-align: center;
  padding: 24px;
}

.ntf-17a__ghost {
  font-size: clamp(26px,4vw,38px);
  font-weight: 600;
  letter-spacing: -.02em;
  color: rgba(226,234,250,.25);
}

.ntf-17a__ghostsub {
  margin-top: 8px;
  font-size: 13px;
  color: rgba(226,234,250,.18);
}

.ntf-17a__again {
  margin-top: 18px;
  padding: 10px 18px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: #e2eafa;
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(165,190,230,.25);
  border-radius: 10px;
  cursor: pointer;
}

.ntf-17a__bar {
  position: absolute;
  inset-inline: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom,0px));
  margin-inline: auto;
  max-inline-size: 720px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 16px 18px;
  border-radius: 18px;
  background: #f8fafd;
  box-shadow: 0 2px 6px rgba(8,10,16,.35),0 30px 60px -20px rgba(8,10,16,.7);
  transition: translate .5s cubic-bezier(.22,1.2,.36,1),opacity .35s ease,display .5s allow-discrete;
}

@starting-style {
  .ntf-17a__bar {
    translate: 0 24px;
    opacity: 0;
  }
}

.ntf-17a__bar.is-gone {
  translate: 0 24px;
  opacity: 0;
  display: none;
}

.ntf-17a__crumb {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  color: #f8fafd;
  background: #20242f;
}

.ntf-17a__crumb svg {
  width: 20px;
  height: 20px;
}

.ntf-17a__tx {
  flex: 1 1 260px;
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(29,33,43,.8);
}

.ntf-17a__tx b {
  color: #171a22;
  font-weight: 700;
}

.ntf-17a__tx a {
  color: oklch(0.5 0.15 260);
  font-weight: 600;
}

.ntf-17a__tx a:hover {
  color: oklch(0.42 0.15 260);
}

.ntf-17a__acts {
  display: flex;
  gap: 9px;
  margin-left: auto;
}

.ntf-17a__ghostbtn,
.ntf-17a__solid {
  padding: 11px 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  border-radius: 11px;
  cursor: pointer;
  transition: scale .12s,background .2s;
}

.ntf-17a__ghostbtn {
  color: #20242f;
  background: transparent;
  border: 1.5px solid rgba(29,33,43,.25);
}

.ntf-17a__ghostbtn:hover {
  background: rgba(29,33,43,.06);
}

.ntf-17a__solid {
  color: #f8fafd;
  background: #20242f;
  border: 1.5px solid #20242f;
}

.ntf-17a__solid:hover {
  background: #333947;
}

.ntf-17a__ghostbtn:active,
.ntf-17a__solid:active {
  scale: .96;
}

.ntf-17a__verdict {
  position: absolute;
  bottom: 18px;
  inset-inline: 0;
  text-align: center;
  font-size: 12.5px;
  color: rgba(226,234,250,.55);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-17a__bar {
    transition-duration: .01s;
  }

  @starting-style {
    .ntf-17a__bar {
      translate: 0 0;
    }
  }
}

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

.ntf-17b {
  position: relative;
  overflow: hidden;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: linear-gradient(180deg,#f7f2e8,#eee5d1);
  font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}

.ntf-17b__page {
  text-align: center;
  padding: 24px;
}

.ntf-17b__ghost {
  font-size: clamp(28px,4.5vw,40px);
  font-weight: 700;
  letter-spacing: -.02em;
  color: rgba(37,32,24,.22);
}

.ntf-17b__ghostsub {
  margin-top: 8px;
  font-size: 13px;
  color: rgba(37,32,24,.35);
}

.ntf-17b__pill {
  position: absolute;
  left: 50%;
  bottom: calc(18px + env(safe-area-inset-bottom,0px));
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: 12px;
  max-inline-size: min(560px,calc(100% - 32px));
  padding: 10px 10px 10px 18px;
  border-radius: 999px;
  background: #252018;
  color: #f7f2e8;
  box-shadow: 0 2px 6px rgba(37,32,24,.3),0 26px 50px -18px rgba(37,32,24,.55);
  transition: left .55s cubic-bezier(.4,0,.2,1),translate .55s cubic-bezier(.4,0,.2,1),padding .45s,gap .45s,bottom .45s;
  white-space: nowrap;
}

@starting-style {
  .ntf-17b__pill {
    translate: -50% 22px;
    opacity: 0;
  }
}

.ntf-17b__spark {
  flex: none;
  font-size: 17px;
  color: oklch(0.85 0.14 85);
  animation: ntf-17b-spin 5s linear infinite;
}

@keyframes ntf-17b-spin {
  to {
    rotate: 360deg;
  }
}

.ntf-17b__tx {
  font-size: 13.5px;
  overflow: hidden;
  max-inline-size: 340px;
  transition: max-inline-size .45s,opacity .3s;
}

.ntf-17b__tx b {
  font-weight: 800;
  color: oklch(0.9 0.11 85);
}

.ntf-17b__go {
  flex: none;
  padding: 10px 16px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 800;
  color: #252018;
  background: oklch(0.85 0.14 85);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: scale .12s,max-inline-size .45s,padding .45s,opacity .3s;
  max-inline-size: 120px;
  overflow: hidden;
}

.ntf-17b__go:active {
  scale: .95;
}

.ntf-17b__later {
  flex: none;
  padding: 10px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: rgba(247,242,232,.65);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color .15s,max-inline-size .45s,padding .45s,opacity .3s;
  max-inline-size: 80px;
  overflow: hidden;
}

.ntf-17b__later:hover {
  color: #f7f2e8;
}

.ntf-17b__pill.is-chip {
  left: calc(100% - 16px);
  translate: -100% 0;
  padding: 10px 14px;
  gap: 0;
}

.ntf-17b__pill.is-chip .ntf-17b__tx,
.ntf-17b__pill.is-chip .ntf-17b__go,
.ntf-17b__pill.is-chip .ntf-17b__later {
  max-inline-size: 0;
  padding-inline: 0;
  opacity: 0;
  pointer-events: none;
}

.ntf-17b__pill.is-chip .ntf-17b__spark {
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .ntf-17b__pill,
    .ntf-17b__tx,
    .ntf-17b__go,
    .ntf-17b__later {
    transition-duration: .01s;
  }

  .ntf-17b__spark {
    animation: none;
  }
}
```

## JavaScript
```js
(function(){
  var bar=document.getElementById('ntf-17a-bar'); if(!bar) return;
  var verdict=document.getElementById('ntf-17a-verdict'),again=document.getElementById('ntf-17a-again');
  bar.querySelectorAll('[data-ntf-17a]').forEach(function(btn){
    btn.addEventListener('click',function(){
      bar.classList.add('is-gone');
      verdict.textContent='Choice recorded: '+btn.getAttribute('data-ntf-17a')+' (persist it in localStorage / a consent cookie).';
      again.hidden=false;
    });
  });
  again.addEventListener('click',function(){
    bar.classList.remove('is-gone'); verdict.textContent=''; again.hidden=true;
  });
})();

(function(){
  var pill=document.getElementById('ntf-17b-pill'); if(!pill) return;
  document.getElementById('ntf-17b-later').addEventListener('click',function(){
    pill.classList.add('is-chip');
    pill.setAttribute('aria-label','Snoozed offer — select to expand');
  });
  pill.addEventListener('click',function(){
    if(pill.classList.contains('is-chip')){
      pill.classList.remove('is-chip');
      pill.setAttribute('aria-label','Free shipping offer');
    }
  });
})();
```

How this works

Floating bars are positioned like toasts but sized like banners — centered with inline margins, capped, and lifted off the edge:

.bar{
  position:absolute;         /* fixed, in production */
  inset-inline:16px; bottom:16px;
  margin-inline:auto; max-inline-size:720px;
  padding-bottom:env(safe-area-inset-bottom,0);
  border-radius:16px;
}

env(safe-area-inset-bottom) is the line everyone forgets — without it, iPhone home indicators sit ON your buttons. Entry uses the same @starting-style pattern as toasts (rise 24px + fade), and the float is sold by a two-part shadow: a tight dark contact shadow plus a wide soft ambient one. The pill variant adds the snooze idea: 'later' doesn't remove the promo, it transitions the SAME element into a 44px corner chip (width, padding and border-radius all animate), so the offer stays reachable without occupying the thumb zone.

Make it yours

  • The 720px cap keeps consent text readable on desktop; drop the cap and bottom bars become unscannable clotheslines.
  • Raise bottom:16px to 24px+ when a FAB or tab bar lives in the same corner — floating elements need negotiated airspace.
  • Contact shadow tint: use the page's ink color at 25–35% instead of pure black for a less sooty float on warm themes.
  • The snooze chip remembers nothing here — wire its state to localStorage with a timestamp for a real 24h snooze.

Gotchas — read before shipping

  • Cookie bars have legal weight: Reject must be as reachable as Accept (equal buttons here) — a buried reject invalidates consent under GDPR guidance.
  • safe-area-inset only resolves inside viewport-fit=cover contexts on iOS — include the meta viewport flag or the env() quietly returns 0.
  • position:fixed + transform on an ancestor = the bar pins to the ancestor, not the viewport; keep floating bars out of transformed containers.
  • Don't trap focus in a consent bar — it's a region, not a modal; users may scroll and read before deciding.

Browser support

ChromeSafariFirefoxEdge
117+17.5+129+117+

@starting-style entries: Chrome 117 / Safari 17.5 / Firefox 129 (older browsers show the bar instantly). env(safe-area-inset-*) is supported everywhere that has notches. Everything else is baseline flex + transitions.

Techniques used in this demo

Search CodeFronts

Loading…