30 CSS Notifications07 / 30

CSS + JSMIT licensed

CSS Success Error Warning Info Alert Toasts

A complete status system, not four hand-tinted divs: one .toast class whose success/error/warning/info identities are a single --hue swap fed through oklch() and color-mix() (surface, border, icon and text all derive automatically), plus a live morphing toast where a segmented control swaps data-type and the whole card cross-fades to the new state.

Published

Live Demo
Try it

The code

<div class="ntf-07-group">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-07a" aria-label="Success error warning info toast suite">
  <div class="ntf-07a__wrap">
    <h2 class="ntf-07a__title">Four states, one <code>--hue</code></h2>
    <div class="ntf-07a__list">
      <div class="ntf-07a__toast" data-type="success" role="status"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></span><span class="ntf-07a__tx"><b>Payment received</b><small>Invoice #2041 marked as paid</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="error" role="alert"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></span><span class="ntf-07a__tx"><b>Payment failed</b><small>Card declined — try another method</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="warning" role="alert"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3L2 20h20L12 3zm0 8v4m0 3v.5"/></svg></span><span class="ntf-07a__tx"><b>Trial ends in 3 days</b><small>Add billing to keep your workspace</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="info" role="status"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M12 11v6m0-10v.5"/><circle cx="12" cy="12" r="9.2"/></svg></span><span class="ntf-07a__tx"><b>Scheduled maintenance</b><small>Sunday 02:00–02:20 UTC</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
    </div>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-07b" aria-label="One toast morphing between four states">
  <div class="ntf-07b__wrap">
    <div class="ntf-07b__seg" role="tablist" aria-label="Toast type">
      <button class="ntf-07b__segbtn is-on" data-ntf-07b="success" type="button">Success</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="error" type="button">Error</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="warning" type="button">Warning</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="info" type="button">Info</button>
    </div>
    <div class="ntf-07b__toast" id="ntf-07b-toast" data-type="success" role="status">
      <span class="ntf-07b__ico" aria-hidden="true">
        <svg class="ntf-07b__g ntf-07b__g--success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--error" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--warning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3L2 20h20L12 3zm0 8v4m0 3v.5"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M12 11v6m0-10v.5"/><circle cx="12" cy="12" r="9.2"/></svg>
      </span>
      <span class="ntf-07b__tx"><b id="ntf-07b-title">Changes saved</b><small id="ntf-07b-sub">Everything is up to date</small></span>
      <code class="ntf-07b__hue" id="ntf-07b-hue">--hue: 150</code>
    </div>
    <p class="ntf-07b__cap">Switching <b>data-type</b> swaps one custom property — the card cross-fades because its consumers transition.</p>
  </div>
</section>
</div>
.ntf-07-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

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

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

.ntf-07a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#f6f7f9,#eceef2);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-07a__wrap {
  width: min(440px,100%);
}

.ntf-07a__title {
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: #20242d;
  text-align: center;
}

.ntf-07a__title code {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .82em;
  background: rgba(32,36,45,.07);
  padding: 3px 8px;
  border-radius: 7px;
}

.ntf-07a__list {
  margin-top: 24px;
  display: grid;
  gap: 11px;
}

.ntf-07a__toast {
  --hue: 150;
  --acc: oklch(0.6 0.14 var(--hue));
  --bg: color-mix(in oklch,var(--acc),white 92%);
  --line: color-mix(in oklch,var(--acc),white 66%);
  --tx: oklch(0.33 0.06 var(--hue));
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 14px;
  border-radius: 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  animation: ntf-07a-in .5s cubic-bezier(.22,1.2,.36,1) both;
}

.ntf-07a__toast[data-type=error] {
  --hue: 25;
}

.ntf-07a__toast[data-type=warning] {
  --hue: 85;
  --tx: oklch(0.4 0.08 70);
}

.ntf-07a__toast[data-type=info] {
  --hue: 250;
}

.ntf-07a__toast:nth-child(2) {
  animation-delay: .07s;
}

.ntf-07a__toast:nth-child(3) {
  animation-delay: .14s;
}

.ntf-07a__toast:nth-child(4) {
  animation-delay: .21s;
}

.ntf-07a__ico {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  color: #fff;
  background: var(--acc);
}

.ntf-07a__ico svg {
  width: 17px;
  height: 17px;
}

.ntf-07a__tx {
  display: grid;
  gap: 1px;
  min-width: 0;
}

.ntf-07a__tx b {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--tx);
}

.ntf-07a__tx small {
  font-size: 11.5px;
  color: color-mix(in oklch,var(--tx),transparent 30%);
}

.ntf-07a__x {
  margin-left: auto;
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 8px;
  font-size: 11px;
  color: var(--tx);
  background: transparent;
  cursor: pointer;
  opacity: .55;
  transition: opacity .15s,background .15s;
}

.ntf-07a__x:hover {
  opacity: 1;
  background: color-mix(in oklch,var(--acc),transparent 85%);
}

@keyframes ntf-07a-in {
  from {
    opacity: 0;
    translate: 0 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ntf-07a__toast {
    animation: ntf-07a-rm .2s ease both;
  }

  @keyframes ntf-07a-rm {
    from {
      opacity: 0;
    }
  }
}

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

.ntf-07b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(120% 100% at 50% 0%,#151922,#0b0e14);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.ntf-07b__wrap {
  width: min(460px,100%);
  display: grid;
  gap: 20px;
  justify-items: center;
}

.ntf-07b__seg {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 12px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(165,190,230,.16);
}

.ntf-07b__segbtn {
  padding: 9px 16px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(220,230,248,.6);
  background: transparent;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
  transition: background .2s,color .2s;
}

.ntf-07b__segbtn:hover {
  color: #eef2fb;
}

.ntf-07b__segbtn.is-on {
  background: rgba(255,255,255,.12);
  color: #eef2fb;
}

.ntf-07b__toast {
  --hue: 150;
  --acc: oklch(0.76 0.15 var(--hue));
  width: 100%;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 16px;
  border-radius: 16px;
  background: color-mix(in oklch,var(--acc),#12151d 88%);
  border: 1px solid color-mix(in oklch,var(--acc),transparent 60%);
  box-shadow: 0 24px 55px -28px color-mix(in oklch,var(--acc),transparent 40%);
  transition: background .45s,border-color .45s,box-shadow .45s;
}

.ntf-07b__toast[data-type=error] {
  --hue: 25;
}

.ntf-07b__toast[data-type=warning] {
  --hue: 85;
}

.ntf-07b__toast[data-type=info] {
  --hue: 250;
}

.ntf-07b__ico {
  flex: none;
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--acc);
  color: #0b0e14;
  transition: background .45s;
}

.ntf-07b__g {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 19px;
  height: 19px;
  opacity: 0;
  scale: .6;
  transition: opacity .3s,scale .35s cubic-bezier(.22,1.4,.36,1);
}

.ntf-07b__toast[data-type=success] .ntf-07b__g--success,
.ntf-07b__toast[data-type=error] .ntf-07b__g--error,
.ntf-07b__toast[data-type=warning] .ntf-07b__g--warning,
.ntf-07b__toast[data-type=info] .ntf-07b__g--info {
  opacity: 1;
  scale: 1;
}

.ntf-07b__tx {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.ntf-07b__tx b {
  font-size: 14.5px;
  font-weight: 600;
  color: #eef2fb;
}

.ntf-07b__tx small {
  font-size: 12px;
  color: rgba(220,230,248,.55);
}

.ntf-07b__hue {
  margin-left: auto;
  flex: none;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--acc);
  background: color-mix(in oklch,var(--acc),transparent 88%);
  padding: 5px 9px;
  border-radius: 7px;
  transition: color .45s,background .45s;
}

.ntf-07b__cap {
  font-size: 12px;
  line-height: 1.6;
  color: rgba(220,230,248,.45);
  text-align: center;
  text-wrap: pretty;
}

.ntf-07b__cap b {
  color: rgba(238,242,251,.8);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-07b__toast,
    .ntf-07b__ico,
    .ntf-07b__g,
    .ntf-07b__hue {
    transition-duration: .01s;
  }
}
/* No JavaScript — each data-type sets ONE --hue; oklch() + color-mix() derive surface, border, icon chip and text from it, so all four states are automatically the same perceived weight. */

(function(){
  var toast=document.getElementById('ntf-07b-toast'); if(!toast) return;
  var COPY={success:['Changes saved','Everything is up to date',150],error:['Upload failed','File exceeds the 25 MB limit',25],warning:['Trial ends in 3 days','Add billing to keep your workspace',85],info:['New version available','Refresh to get v3.2',250]};
  var title=document.getElementById('ntf-07b-title'),sub=document.getElementById('ntf-07b-sub'),hue=document.getElementById('ntf-07b-hue');
  document.querySelectorAll('[data-ntf-07b]').forEach(function(btn){
    btn.addEventListener('click',function(){
      var type=btn.getAttribute('data-ntf-07b'),d=COPY[type];
      document.querySelectorAll('[data-ntf-07b]').forEach(function(b){ b.classList.toggle('is-on',b===btn); });
      toast.setAttribute('data-type',type);
      toast.setAttribute('role',type==='error'||type==='warning'?'alert':'status');
      title.textContent=d[0]; sub.textContent=d[1]; hue.textContent='--hue: '+d[2];
    });
  });
})();
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: CSS Success Error Warning Info Alert Toasts
Source: https://codefronts.com/snippets/css-notifications/css-success-error-warning-info-alert-toasts/

A complete status system, not four hand-tinted divs: one .toast class whose success/error/warning/info identities are a single --hue swap fed through oklch() and color-mix() (surface, border, icon and text all derive automatically), plus a live morphing toast where a segmented control swaps data-type and the whole card cross-fades to the new state.
## HTML
```html
<div class="ntf-07-group">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-07a" aria-label="Success error warning info toast suite">
  <div class="ntf-07a__wrap">
    <h2 class="ntf-07a__title">Four states, one <code>--hue</code></h2>
    <div class="ntf-07a__list">
      <div class="ntf-07a__toast" data-type="success" role="status"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></span><span class="ntf-07a__tx"><b>Payment received</b><small>Invoice #2041 marked as paid</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="error" role="alert"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></span><span class="ntf-07a__tx"><b>Payment failed</b><small>Card declined — try another method</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="warning" role="alert"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3L2 20h20L12 3zm0 8v4m0 3v.5"/></svg></span><span class="ntf-07a__tx"><b>Trial ends in 3 days</b><small>Add billing to keep your workspace</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
      <div class="ntf-07a__toast" data-type="info" role="status"><span class="ntf-07a__ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M12 11v6m0-10v.5"/><circle cx="12" cy="12" r="9.2"/></svg></span><span class="ntf-07a__tx"><b>Scheduled maintenance</b><small>Sunday 02:00–02:20 UTC</small></span><button class="ntf-07a__x" type="button" aria-label="Dismiss">&#10005;</button></div>
    </div>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-07b" aria-label="One toast morphing between four states">
  <div class="ntf-07b__wrap">
    <div class="ntf-07b__seg" role="tablist" aria-label="Toast type">
      <button class="ntf-07b__segbtn is-on" data-ntf-07b="success" type="button">Success</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="error" type="button">Error</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="warning" type="button">Warning</button>
      <button class="ntf-07b__segbtn" data-ntf-07b="info" type="button">Info</button>
    </div>
    <div class="ntf-07b__toast" id="ntf-07b-toast" data-type="success" role="status">
      <span class="ntf-07b__ico" aria-hidden="true">
        <svg class="ntf-07b__g ntf-07b__g--success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--error" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--warning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3L2 20h20L12 3zm0 8v4m0 3v.5"/></svg>
        <svg class="ntf-07b__g ntf-07b__g--info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M12 11v6m0-10v.5"/><circle cx="12" cy="12" r="9.2"/></svg>
      </span>
      <span class="ntf-07b__tx"><b id="ntf-07b-title">Changes saved</b><small id="ntf-07b-sub">Everything is up to date</small></span>
      <code class="ntf-07b__hue" id="ntf-07b-hue">--hue: 150</code>
    </div>
    <p class="ntf-07b__cap">Switching <b>data-type</b> swaps one custom property — the card cross-fades because its consumers transition.</p>
  </div>
</section>
</div>
```
## CSS
```css
.ntf-07-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

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

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

.ntf-07a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#f6f7f9,#eceef2);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-07a__wrap {
  width: min(440px,100%);
}

.ntf-07a__title {
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: #20242d;
  text-align: center;
}

.ntf-07a__title code {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .82em;
  background: rgba(32,36,45,.07);
  padding: 3px 8px;
  border-radius: 7px;
}

.ntf-07a__list {
  margin-top: 24px;
  display: grid;
  gap: 11px;
}

.ntf-07a__toast {
  --hue: 150;
  --acc: oklch(0.6 0.14 var(--hue));
  --bg: color-mix(in oklch,var(--acc),white 92%);
  --line: color-mix(in oklch,var(--acc),white 66%);
  --tx: oklch(0.33 0.06 var(--hue));
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 14px;
  border-radius: 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  animation: ntf-07a-in .5s cubic-bezier(.22,1.2,.36,1) both;
}

.ntf-07a__toast[data-type=error] {
  --hue: 25;
}

.ntf-07a__toast[data-type=warning] {
  --hue: 85;
  --tx: oklch(0.4 0.08 70);
}

.ntf-07a__toast[data-type=info] {
  --hue: 250;
}

.ntf-07a__toast:nth-child(2) {
  animation-delay: .07s;
}

.ntf-07a__toast:nth-child(3) {
  animation-delay: .14s;
}

.ntf-07a__toast:nth-child(4) {
  animation-delay: .21s;
}

.ntf-07a__ico {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  color: #fff;
  background: var(--acc);
}

.ntf-07a__ico svg {
  width: 17px;
  height: 17px;
}

.ntf-07a__tx {
  display: grid;
  gap: 1px;
  min-width: 0;
}

.ntf-07a__tx b {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--tx);
}

.ntf-07a__tx small {
  font-size: 11.5px;
  color: color-mix(in oklch,var(--tx),transparent 30%);
}

.ntf-07a__x {
  margin-left: auto;
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 8px;
  font-size: 11px;
  color: var(--tx);
  background: transparent;
  cursor: pointer;
  opacity: .55;
  transition: opacity .15s,background .15s;
}

.ntf-07a__x:hover {
  opacity: 1;
  background: color-mix(in oklch,var(--acc),transparent 85%);
}

@keyframes ntf-07a-in {
  from {
    opacity: 0;
    translate: 0 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ntf-07a__toast {
    animation: ntf-07a-rm .2s ease both;
  }

  @keyframes ntf-07a-rm {
    from {
      opacity: 0;
    }
  }
}

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

.ntf-07b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(120% 100% at 50% 0%,#151922,#0b0e14);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.ntf-07b__wrap {
  width: min(460px,100%);
  display: grid;
  gap: 20px;
  justify-items: center;
}

.ntf-07b__seg {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 12px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(165,190,230,.16);
}

.ntf-07b__segbtn {
  padding: 9px 16px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(220,230,248,.6);
  background: transparent;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
  transition: background .2s,color .2s;
}

.ntf-07b__segbtn:hover {
  color: #eef2fb;
}

.ntf-07b__segbtn.is-on {
  background: rgba(255,255,255,.12);
  color: #eef2fb;
}

.ntf-07b__toast {
  --hue: 150;
  --acc: oklch(0.76 0.15 var(--hue));
  width: 100%;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 16px;
  border-radius: 16px;
  background: color-mix(in oklch,var(--acc),#12151d 88%);
  border: 1px solid color-mix(in oklch,var(--acc),transparent 60%);
  box-shadow: 0 24px 55px -28px color-mix(in oklch,var(--acc),transparent 40%);
  transition: background .45s,border-color .45s,box-shadow .45s;
}

.ntf-07b__toast[data-type=error] {
  --hue: 25;
}

.ntf-07b__toast[data-type=warning] {
  --hue: 85;
}

.ntf-07b__toast[data-type=info] {
  --hue: 250;
}

.ntf-07b__ico {
  flex: none;
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--acc);
  color: #0b0e14;
  transition: background .45s;
}

.ntf-07b__g {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 19px;
  height: 19px;
  opacity: 0;
  scale: .6;
  transition: opacity .3s,scale .35s cubic-bezier(.22,1.4,.36,1);
}

.ntf-07b__toast[data-type=success] .ntf-07b__g--success,
.ntf-07b__toast[data-type=error] .ntf-07b__g--error,
.ntf-07b__toast[data-type=warning] .ntf-07b__g--warning,
.ntf-07b__toast[data-type=info] .ntf-07b__g--info {
  opacity: 1;
  scale: 1;
}

.ntf-07b__tx {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.ntf-07b__tx b {
  font-size: 14.5px;
  font-weight: 600;
  color: #eef2fb;
}

.ntf-07b__tx small {
  font-size: 12px;
  color: rgba(220,230,248,.55);
}

.ntf-07b__hue {
  margin-left: auto;
  flex: none;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--acc);
  background: color-mix(in oklch,var(--acc),transparent 88%);
  padding: 5px 9px;
  border-radius: 7px;
  transition: color .45s,background .45s;
}

.ntf-07b__cap {
  font-size: 12px;
  line-height: 1.6;
  color: rgba(220,230,248,.45);
  text-align: center;
  text-wrap: pretty;
}

.ntf-07b__cap b {
  color: rgba(238,242,251,.8);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-07b__toast,
    .ntf-07b__ico,
    .ntf-07b__g,
    .ntf-07b__hue {
    transition-duration: .01s;
  }
}
```

## JavaScript
```js
/* No JavaScript — each data-type sets ONE --hue; oklch() + color-mix() derive surface, border, icon chip and text from it, so all four states are automatically the same perceived weight. */

(function(){
  var toast=document.getElementById('ntf-07b-toast'); if(!toast) return;
  var COPY={success:['Changes saved','Everything is up to date',150],error:['Upload failed','File exceeds the 25 MB limit',25],warning:['Trial ends in 3 days','Add billing to keep your workspace',85],info:['New version available','Refresh to get v3.2',250]};
  var title=document.getElementById('ntf-07b-title'),sub=document.getElementById('ntf-07b-sub'),hue=document.getElementById('ntf-07b-hue');
  document.querySelectorAll('[data-ntf-07b]').forEach(function(btn){
    btn.addEventListener('click',function(){
      var type=btn.getAttribute('data-ntf-07b'),d=COPY[type];
      document.querySelectorAll('[data-ntf-07b]').forEach(function(b){ b.classList.toggle('is-on',b===btn); });
      toast.setAttribute('data-type',type);
      toast.setAttribute('role',type==='error'||type==='warning'?'alert':'status');
      title.textContent=d[0]; sub.textContent=d[1]; hue.textContent='--hue: '+d[2];
    });
  });
})();
```

How this works

Hand-picking twelve colors for four toast types guarantees drift. Derive them instead — each type sets one hue, and oklch() + color-mix() compute the family:

.toast{ --hue:150;   /* success */
  --acc:  oklch(0.62 0.15 var(--hue));
  --bg:   color-mix(in oklch, var(--acc), white 92%);
  --line: color-mix(in oklch, var(--acc), white 68%);
  --text: oklch(0.32 0.06 var(--hue));
}
.toast[data-type="error"]  { --hue:25  }
.toast[data-type="warning"]{ --hue:85  }
.toast[data-type="info"]   { --hue:250 }

Because every derived color shares the accent's hue, the four types are automatically siblings — same perceived lightness (that's oklch's whole job), same tint strength, zero eyedropper work. Each type also gets its own SVG glyph (check, cross, triangle, i) because roughly 1 in 12 men can't tell the red toast from the green one: shape carries the state, color reinforces it. The morphing variant transitions the custom properties themselves, so switching data-type cross-fades surface, border and icon in one declaration.

Make it yours

  • Add a fifth type (e.g. 'promo') in two lines: a data-type selector setting --hue plus a glyph — the palette derives itself.
  • Darker themes: flip the mixes (color-mix with black 78%) and raise accent lightness to ~0.75 — the hue numbers stay identical.
  • Tone the whole system calmer or louder by editing the two mix percentages — 92%/68% is pastel, 84%/50% is insistent.
  • Wire role by severity: error/warning get role=alert (interrupts), success/info get role=status (waits) — the markup shows both.

Gotchas — read before shipping

  • Never encode state ONLY in --hue: the glyphs are load-bearing for color-blind users, and the title text ('Payment failed') is load-bearing for everyone.
  • color-mix(in oklch, …) keeps perceived lightness stable across hues; mixing in srgb makes yellow glow and blue turn muddy — always name the space.
  • Yellow is the trap hue: warning text needs oklch lightness ≤ 0.45 on a pastel yellow surface to clear 4.5:1 — test that one first.
  • Transitioning custom properties works because the CONSUMERS (background, border-color) transition; list them explicitly rather than transition:all.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

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

oklch() and color-mix() land together (Chrome 111 / Safari 16.2–16.4 / Firefox 113). For older browsers ship literal hex per type as fallback declarations above the modern ones — the cascade does the rest.

Techniques used in this demo

Search CodeFronts

Loading…