30 CSS Notifications13 / 30
CSS Notification Badge Clip Path
clip-path as a badge tool, twice: an app-dock icon whose top-right corner is genuinely carved away by clip-path: path() so the count badge floats in a true notch (the SVG arc math annotated in the demo), and a merch card wearing polygon-clipped badges — a spinning 16-point starburst seal and a hexagon counter — that morph between polygon states on hover because same-vertex-count polygons interpolate.
Published
The code
<div class="ntf-13-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
<section class="ntf-13a" aria-label="App dock icons with clip-path notch badges">
<div class="ntf-13a__wrap">
<div class="ntf-13a__dock">
<span class="ntf-13a__slot" aria-label="Mail, 3 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.7 0.15 250);--g2:oklch(0.55 0.18 270)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="3"/><path d="m2 7 10 6 10-6"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">3</b>
</span>
<span class="ntf-13a__slot" aria-label="Chat, 12 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.75 0.15 160);--g2:oklch(0.6 0.15 180)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">12</b>
</span>
<span class="ntf-13a__slot" aria-label="Calendar, no notifications">
<i class="ntf-13a__tile ntf-13a__tile--plain" style="--g1:oklch(0.72 0.14 40);--g2:oklch(0.58 0.16 25)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="3"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></i>
</span>
</div>
<code class="ntf-13a__code">clip-path: path('M16 0 H40 <b>A17 17 0 0 0 64 21</b> V48 …')<br><span> — leave the top edge at x=40, bite an r17 arc, rejoin at y=21</span></code>
</div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-13b" aria-label="Starburst seal and hexagon badges cut with clip-path polygons">
<article class="ntf-13b__card">
<span class="ntf-13b__seal" aria-hidden="true"><i class="ntf-13b__sealtxt">NEW</i></span>
<span class="ntf-13b__hex" aria-label="6 in stock">6</span>
<div class="ntf-13b__art"></div>
<h2 class="ntf-13b__name">Studio print No. 7</h2>
<p class="ntf-13b__meta">A2 · archival paper · edition of 50</p>
<p class="ntf-13b__hint">hover the starburst — same-vertex polygons morph</p>
</article>
</section>
</div><div class="ntf-13-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
<section class="ntf-13a" aria-label="App dock icons with clip-path notch badges">
<div class="ntf-13a__wrap">
<div class="ntf-13a__dock">
<span class="ntf-13a__slot" aria-label="Mail, 3 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.7 0.15 250);--g2:oklch(0.55 0.18 270)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="3"/><path d="m2 7 10 6 10-6"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">3</b>
</span>
<span class="ntf-13a__slot" aria-label="Chat, 12 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.75 0.15 160);--g2:oklch(0.6 0.15 180)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">12</b>
</span>
<span class="ntf-13a__slot" aria-label="Calendar, no notifications">
<i class="ntf-13a__tile ntf-13a__tile--plain" style="--g1:oklch(0.72 0.14 40);--g2:oklch(0.58 0.16 25)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="3"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></i>
</span>
</div>
<code class="ntf-13a__code">clip-path: path('M16 0 H40 <b>A17 17 0 0 0 64 21</b> V48 …')<br><span> — leave the top edge at x=40, bite an r17 arc, rejoin at y=21</span></code>
</div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-13b" aria-label="Starburst seal and hexagon badges cut with clip-path polygons">
<article class="ntf-13b__card">
<span class="ntf-13b__seal" aria-hidden="true"><i class="ntf-13b__sealtxt">NEW</i></span>
<span class="ntf-13b__hex" aria-label="6 in stock">6</span>
<div class="ntf-13b__art"></div>
<h2 class="ntf-13b__name">Studio print No. 7</h2>
<p class="ntf-13b__meta">A2 · archival paper · edition of 50</p>
<p class="ntf-13b__hint">hover the starburst — same-vertex polygons morph</p>
</article>
</section>
</div>.ntf-13-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.ntf-13-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.ntf-13a,
.ntf-13a *,
.ntf-13a *::before,
.ntf-13a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13a {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(160deg,#191d2a,#0d0f17 70%);
font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}
.ntf-13a__wrap {
display: grid;
gap: 26px;
justify-items: center;
}
.ntf-13a__dock {
display: flex;
gap: 22px;
padding: 20px 24px;
border-radius: 26px;
background: rgba(255,255,255,.06);
border: 1px solid rgba(170,195,235,.16);
box-shadow: 0 30px 60px -30px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.07);
}
.ntf-13a__slot {
position: relative;
width: 64px;
height: 64px;
filter: drop-shadow(0 10px 16px rgba(0,0,0,.45));
}
.ntf-13a__tile {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: linear-gradient(150deg,var(--g1),var(--g2));
clip-path: path('M16 0 H40 A17 17 0 0 0 64 21 V48 Q64 64 48 64 H16 Q0 64 0 48 V16 Q0 0 16 0 Z');
}
.ntf-13a__tile--plain {
clip-path: none;
border-radius: 16px;
}
.ntf-13a__tile svg {
width: 28px;
height: 28px;
}
.ntf-13a__count {
position: absolute;
top: -7px;
inset-inline-end: -7px;
min-inline-size: 24px;
block-size: 24px;
display: grid;
place-items: center;
padding-inline: 6px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
font-style: normal;
color: #fff;
background: oklch(0.63 0.21 25);
animation: ntf-13a-pop .5s cubic-bezier(.22,1.6,.36,1) both .25s;
}
@keyframes ntf-13a-pop {
from {
scale: 0;
}
}
.ntf-13a__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: 11.5px;
line-height: 1.7;
color: rgba(205,220,248,.55);
text-align: center;
}
.ntf-13a__code b {
color: oklch(0.8 0.14 60);
font-weight: 500;
}
.ntf-13a__code span {
font-size: 10.5px;
color: rgba(205,220,248,.4);
}
@media (prefers-reduced-motion: reduce) {
.ntf-13a__count {
animation: none;
}
}
.ntf-13b,
.ntf-13b *,
.ntf-13b *::before,
.ntf-13b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f5f1e8,#ece4d3);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.ntf-13b__card {
position: relative;
width: min(330px,100%);
padding: 18px 18px 22px;
border-radius: 20px;
background: #fffdf7;
border: 1px solid rgba(36,31,23,.12);
box-shadow: 0 30px 64px -34px rgba(36,31,23,.5);
}
.ntf-13b__art {
height: 190px;
border-radius: 13px;
background: linear-gradient(135deg,oklch(0.55 0.11 260) 0 34%,oklch(0.8 0.13 85) 34% 62%,oklch(0.62 0.16 30) 62%);
}
.ntf-13b__name {
margin-top: 14px;
font-size: 19px;
font-weight: 700;
letter-spacing: -.01em;
color: #241f17;
}
.ntf-13b__meta {
margin-top: 3px;
font-size: 12.5px;
color: rgba(36,31,23,.55);
}
.ntf-13b__hint {
margin-top: 12px;
font-size: 11px;
color: rgba(36,31,23,.4);
}
.ntf-13b__seal {
position: absolute;
top: -24px;
inset-inline-start: -20px;
width: 86px;
height: 86px;
display: grid;
place-items: center;
background: oklch(0.63 0.19 25);
clip-path: polygon(50% 0%,57% 14%,71% 5%,71% 21%,87% 18%,80% 32%,96% 36%,84% 46%,97% 56%,81% 59%,89% 74%,73% 71%,74% 88%,60% 78%,54% 94%,46% 79%,34% 91%,32% 74%,16% 79%,22% 63%,5% 60%,18% 50%,4% 41%,19% 35%,10% 21%,27% 23%,26% 6%,40% 15%);
animation: ntf-13b-spin 24s linear infinite;
transition: clip-path .45s cubic-bezier(.22,1.2,.36,1),scale .3s;
}
.ntf-13b__seal:hover {
scale: 1.08;
clip-path: polygon(50% 4%,58% 16%,70% 9%,71% 23%,85% 20%,80% 33%,93% 38%,83% 47%,94% 56%,80% 59%,86% 72%,72% 69%,73% 84%,60% 77%,54% 90%,46% 77%,35% 87%,33% 72%,19% 76%,24% 62%,8% 59%,20% 50%,7% 42%,21% 37%,13% 24%,28% 26%,28% 10%,41% 17%);
}
.ntf-13b__sealtxt {
font-style: normal;
font-size: 15px;
font-weight: 800;
letter-spacing: .08em;
color: #fff;
animation: ntf-13b-spin 24s linear infinite reverse;
}
@keyframes ntf-13b-spin {
to {
rotate: 360deg;
}
}
.ntf-13b__hex {
position: absolute;
top: -13px;
inset-inline-end: -11px;
width: 38px;
height: 42px;
display: grid;
place-items: center;
font-size: 14px;
font-weight: 800;
color: #241f17;
background: oklch(0.85 0.14 85);
clip-path: polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
animation: ntf-13b-pop .5s cubic-bezier(.22,1.6,.36,1) both .3s;
}
@keyframes ntf-13b-pop {
from {
scale: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.ntf-13b__seal,
.ntf-13b__sealtxt {
animation: none;
}
.ntf-13b__hex {
animation: none;
}
}.ntf-13-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.ntf-13-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.ntf-13a,
.ntf-13a *,
.ntf-13a *::before,
.ntf-13a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13a {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(160deg,#191d2a,#0d0f17 70%);
font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}
.ntf-13a__wrap {
display: grid;
gap: 26px;
justify-items: center;
}
.ntf-13a__dock {
display: flex;
gap: 22px;
padding: 20px 24px;
border-radius: 26px;
background: rgba(255,255,255,.06);
border: 1px solid rgba(170,195,235,.16);
box-shadow: 0 30px 60px -30px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.07);
}
.ntf-13a__slot {
position: relative;
width: 64px;
height: 64px;
filter: drop-shadow(0 10px 16px rgba(0,0,0,.45));
}
.ntf-13a__tile {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: linear-gradient(150deg,var(--g1),var(--g2));
clip-path: path('M16 0 H40 A17 17 0 0 0 64 21 V48 Q64 64 48 64 H16 Q0 64 0 48 V16 Q0 0 16 0 Z');
}
.ntf-13a__tile--plain {
clip-path: none;
border-radius: 16px;
}
.ntf-13a__tile svg {
width: 28px;
height: 28px;
}
.ntf-13a__count {
position: absolute;
top: -7px;
inset-inline-end: -7px;
min-inline-size: 24px;
block-size: 24px;
display: grid;
place-items: center;
padding-inline: 6px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
font-style: normal;
color: #fff;
background: oklch(0.63 0.21 25);
animation: ntf-13a-pop .5s cubic-bezier(.22,1.6,.36,1) both .25s;
}
@keyframes ntf-13a-pop {
from {
scale: 0;
}
}
.ntf-13a__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: 11.5px;
line-height: 1.7;
color: rgba(205,220,248,.55);
text-align: center;
}
.ntf-13a__code b {
color: oklch(0.8 0.14 60);
font-weight: 500;
}
.ntf-13a__code span {
font-size: 10.5px;
color: rgba(205,220,248,.4);
}
@media (prefers-reduced-motion: reduce) {
.ntf-13a__count {
animation: none;
}
}
.ntf-13b,
.ntf-13b *,
.ntf-13b *::before,
.ntf-13b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f5f1e8,#ece4d3);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.ntf-13b__card {
position: relative;
width: min(330px,100%);
padding: 18px 18px 22px;
border-radius: 20px;
background: #fffdf7;
border: 1px solid rgba(36,31,23,.12);
box-shadow: 0 30px 64px -34px rgba(36,31,23,.5);
}
.ntf-13b__art {
height: 190px;
border-radius: 13px;
background: linear-gradient(135deg,oklch(0.55 0.11 260) 0 34%,oklch(0.8 0.13 85) 34% 62%,oklch(0.62 0.16 30) 62%);
}
.ntf-13b__name {
margin-top: 14px;
font-size: 19px;
font-weight: 700;
letter-spacing: -.01em;
color: #241f17;
}
.ntf-13b__meta {
margin-top: 3px;
font-size: 12.5px;
color: rgba(36,31,23,.55);
}
.ntf-13b__hint {
margin-top: 12px;
font-size: 11px;
color: rgba(36,31,23,.4);
}
.ntf-13b__seal {
position: absolute;
top: -24px;
inset-inline-start: -20px;
width: 86px;
height: 86px;
display: grid;
place-items: center;
background: oklch(0.63 0.19 25);
clip-path: polygon(50% 0%,57% 14%,71% 5%,71% 21%,87% 18%,80% 32%,96% 36%,84% 46%,97% 56%,81% 59%,89% 74%,73% 71%,74% 88%,60% 78%,54% 94%,46% 79%,34% 91%,32% 74%,16% 79%,22% 63%,5% 60%,18% 50%,4% 41%,19% 35%,10% 21%,27% 23%,26% 6%,40% 15%);
animation: ntf-13b-spin 24s linear infinite;
transition: clip-path .45s cubic-bezier(.22,1.2,.36,1),scale .3s;
}
.ntf-13b__seal:hover {
scale: 1.08;
clip-path: polygon(50% 4%,58% 16%,70% 9%,71% 23%,85% 20%,80% 33%,93% 38%,83% 47%,94% 56%,80% 59%,86% 72%,72% 69%,73% 84%,60% 77%,54% 90%,46% 77%,35% 87%,33% 72%,19% 76%,24% 62%,8% 59%,20% 50%,7% 42%,21% 37%,13% 24%,28% 26%,28% 10%,41% 17%);
}
.ntf-13b__sealtxt {
font-style: normal;
font-size: 15px;
font-weight: 800;
letter-spacing: .08em;
color: #fff;
animation: ntf-13b-spin 24s linear infinite reverse;
}
@keyframes ntf-13b-spin {
to {
rotate: 360deg;
}
}
.ntf-13b__hex {
position: absolute;
top: -13px;
inset-inline-end: -11px;
width: 38px;
height: 42px;
display: grid;
place-items: center;
font-size: 14px;
font-weight: 800;
color: #241f17;
background: oklch(0.85 0.14 85);
clip-path: polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
animation: ntf-13b-pop .5s cubic-bezier(.22,1.6,.36,1) both .3s;
}
@keyframes ntf-13b-pop {
from {
scale: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.ntf-13b__seal,
.ntf-13b__sealtxt {
animation: none;
}
.ntf-13b__hex {
animation: none;
}
}/* No JavaScript — the notch is carved from the TILE by clip-path:path() (sweep flag 0 makes the arc concave); the badge is an ordinary circle parked in the hole. The shadow lives on the un-clipped wrapper via filter:drop-shadow. */ /* No JavaScript — the seal spins while its text counter-rotates (same duration, reversed); hover morphs between two 28-vertex polygons, which interpolate because the vertex counts match. */
/* No JavaScript — the notch is carved from the TILE by clip-path:path() (sweep flag 0 makes the arc concave); the badge is an ordinary circle parked in the hole. The shadow lives on the un-clipped wrapper via filter:drop-shadow. */
/* No JavaScript — the seal spins while its text counter-rotates (same duration, reversed); hover morphs between two 28-vertex polygons, which interpolate because the vertex counts match. */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 Notification Badge Clip Path
Source: https://codefronts.com/snippets/css-notifications/css-notification-badge-clip-path/
clip-path as a badge tool, twice: an app-dock icon whose top-right corner is genuinely carved away by clip-path: path() so the count badge floats in a true notch (the SVG arc math annotated in the demo), and a merch card wearing polygon-clipped badges — a spinning 16-point starburst seal and a hexagon counter — that morph between polygon states on hover because same-vertex-count polygons interpolate.
## HTML
```html
<div class="ntf-13-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
<section class="ntf-13a" aria-label="App dock icons with clip-path notch badges">
<div class="ntf-13a__wrap">
<div class="ntf-13a__dock">
<span class="ntf-13a__slot" aria-label="Mail, 3 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.7 0.15 250);--g2:oklch(0.55 0.18 270)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="3"/><path d="m2 7 10 6 10-6"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">3</b>
</span>
<span class="ntf-13a__slot" aria-label="Chat, 12 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.75 0.15 160);--g2:oklch(0.6 0.15 180)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">12</b>
</span>
<span class="ntf-13a__slot" aria-label="Calendar, no notifications">
<i class="ntf-13a__tile ntf-13a__tile--plain" style="--g1:oklch(0.72 0.14 40);--g2:oklch(0.58 0.16 25)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="3"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></i>
</span>
</div>
<code class="ntf-13a__code">clip-path: path('M16 0 H40 <b>A17 17 0 0 0 64 21</b> V48 …')<br><span> — leave the top edge at x=40, bite an r17 arc, rejoin at y=21</span></code>
</div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-13b" aria-label="Starburst seal and hexagon badges cut with clip-path polygons">
<article class="ntf-13b__card">
<span class="ntf-13b__seal" aria-hidden="true"><i class="ntf-13b__sealtxt">NEW</i></span>
<span class="ntf-13b__hex" aria-label="6 in stock">6</span>
<div class="ntf-13b__art"></div>
<h2 class="ntf-13b__name">Studio print No. 7</h2>
<p class="ntf-13b__meta">A2 · archival paper · edition of 50</p>
<p class="ntf-13b__hint">hover the starburst — same-vertex polygons morph</p>
</article>
</section>
</div>
```
## CSS
```css
.ntf-13-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.ntf-13-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.ntf-13a,
.ntf-13a *,
.ntf-13a *::before,
.ntf-13a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13a {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(160deg,#191d2a,#0d0f17 70%);
font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}
.ntf-13a__wrap {
display: grid;
gap: 26px;
justify-items: center;
}
.ntf-13a__dock {
display: flex;
gap: 22px;
padding: 20px 24px;
border-radius: 26px;
background: rgba(255,255,255,.06);
border: 1px solid rgba(170,195,235,.16);
box-shadow: 0 30px 60px -30px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.07);
}
.ntf-13a__slot {
position: relative;
width: 64px;
height: 64px;
filter: drop-shadow(0 10px 16px rgba(0,0,0,.45));
}
.ntf-13a__tile {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: linear-gradient(150deg,var(--g1),var(--g2));
clip-path: path('M16 0 H40 A17 17 0 0 0 64 21 V48 Q64 64 48 64 H16 Q0 64 0 48 V16 Q0 0 16 0 Z');
}
.ntf-13a__tile--plain {
clip-path: none;
border-radius: 16px;
}
.ntf-13a__tile svg {
width: 28px;
height: 28px;
}
.ntf-13a__count {
position: absolute;
top: -7px;
inset-inline-end: -7px;
min-inline-size: 24px;
block-size: 24px;
display: grid;
place-items: center;
padding-inline: 6px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
font-style: normal;
color: #fff;
background: oklch(0.63 0.21 25);
animation: ntf-13a-pop .5s cubic-bezier(.22,1.6,.36,1) both .25s;
}
@keyframes ntf-13a-pop {
from {
scale: 0;
}
}
.ntf-13a__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: 11.5px;
line-height: 1.7;
color: rgba(205,220,248,.55);
text-align: center;
}
.ntf-13a__code b {
color: oklch(0.8 0.14 60);
font-weight: 500;
}
.ntf-13a__code span {
font-size: 10.5px;
color: rgba(205,220,248,.4);
}
@media (prefers-reduced-motion: reduce) {
.ntf-13a__count {
animation: none;
}
}
.ntf-13b,
.ntf-13b *,
.ntf-13b *::before,
.ntf-13b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f5f1e8,#ece4d3);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.ntf-13b__card {
position: relative;
width: min(330px,100%);
padding: 18px 18px 22px;
border-radius: 20px;
background: #fffdf7;
border: 1px solid rgba(36,31,23,.12);
box-shadow: 0 30px 64px -34px rgba(36,31,23,.5);
}
.ntf-13b__art {
height: 190px;
border-radius: 13px;
background: linear-gradient(135deg,oklch(0.55 0.11 260) 0 34%,oklch(0.8 0.13 85) 34% 62%,oklch(0.62 0.16 30) 62%);
}
.ntf-13b__name {
margin-top: 14px;
font-size: 19px;
font-weight: 700;
letter-spacing: -.01em;
color: #241f17;
}
.ntf-13b__meta {
margin-top: 3px;
font-size: 12.5px;
color: rgba(36,31,23,.55);
}
.ntf-13b__hint {
margin-top: 12px;
font-size: 11px;
color: rgba(36,31,23,.4);
}
.ntf-13b__seal {
position: absolute;
top: -24px;
inset-inline-start: -20px;
width: 86px;
height: 86px;
display: grid;
place-items: center;
background: oklch(0.63 0.19 25);
clip-path: polygon(50% 0%,57% 14%,71% 5%,71% 21%,87% 18%,80% 32%,96% 36%,84% 46%,97% 56%,81% 59%,89% 74%,73% 71%,74% 88%,60% 78%,54% 94%,46% 79%,34% 91%,32% 74%,16% 79%,22% 63%,5% 60%,18% 50%,4% 41%,19% 35%,10% 21%,27% 23%,26% 6%,40% 15%);
animation: ntf-13b-spin 24s linear infinite;
transition: clip-path .45s cubic-bezier(.22,1.2,.36,1),scale .3s;
}
.ntf-13b__seal:hover {
scale: 1.08;
clip-path: polygon(50% 4%,58% 16%,70% 9%,71% 23%,85% 20%,80% 33%,93% 38%,83% 47%,94% 56%,80% 59%,86% 72%,72% 69%,73% 84%,60% 77%,54% 90%,46% 77%,35% 87%,33% 72%,19% 76%,24% 62%,8% 59%,20% 50%,7% 42%,21% 37%,13% 24%,28% 26%,28% 10%,41% 17%);
}
.ntf-13b__sealtxt {
font-style: normal;
font-size: 15px;
font-weight: 800;
letter-spacing: .08em;
color: #fff;
animation: ntf-13b-spin 24s linear infinite reverse;
}
@keyframes ntf-13b-spin {
to {
rotate: 360deg;
}
}
.ntf-13b__hex {
position: absolute;
top: -13px;
inset-inline-end: -11px;
width: 38px;
height: 42px;
display: grid;
place-items: center;
font-size: 14px;
font-weight: 800;
color: #241f17;
background: oklch(0.85 0.14 85);
clip-path: polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
animation: ntf-13b-pop .5s cubic-bezier(.22,1.6,.36,1) both .3s;
}
@keyframes ntf-13b-pop {
from {
scale: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.ntf-13b__seal,
.ntf-13b__sealtxt {
animation: none;
}
.ntf-13b__hex {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the notch is carved from the TILE by clip-path:path() (sweep flag 0 makes the arc concave); the badge is an ordinary circle parked in the hole. The shadow lives on the un-clipped wrapper via filter:drop-shadow. */
/* No JavaScript — the seal spins while its text counter-rotates (same duration, reversed); hover morphs between two 28-vertex polygons, which interpolate because the vertex counts match. */
```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 Notification Badge Clip Path
Source: https://codefronts.com/snippets/css-notifications/css-notification-badge-clip-path/
clip-path as a badge tool, twice: an app-dock icon whose top-right corner is genuinely carved away by clip-path: path() so the count badge floats in a true notch (the SVG arc math annotated in the demo), and a merch card wearing polygon-clipped badges — a spinning 16-point starburst seal and a hexagon counter — that morph between polygon states on hover because same-vertex-count polygons interpolate.
## HTML
```html
<div class="ntf-13-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
<section class="ntf-13a" aria-label="App dock icons with clip-path notch badges">
<div class="ntf-13a__wrap">
<div class="ntf-13a__dock">
<span class="ntf-13a__slot" aria-label="Mail, 3 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.7 0.15 250);--g2:oklch(0.55 0.18 270)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="3"/><path d="m2 7 10 6 10-6"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">3</b>
</span>
<span class="ntf-13a__slot" aria-label="Chat, 12 unread">
<i class="ntf-13a__tile" style="--g1:oklch(0.75 0.15 160);--g2:oklch(0.6 0.15 180)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></i>
<b class="ntf-13a__count" aria-hidden="true">12</b>
</span>
<span class="ntf-13a__slot" aria-label="Calendar, no notifications">
<i class="ntf-13a__tile ntf-13a__tile--plain" style="--g1:oklch(0.72 0.14 40);--g2:oklch(0.58 0.16 25)"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="3"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></i>
</span>
</div>
<code class="ntf-13a__code">clip-path: path('M16 0 H40 <b>A17 17 0 0 0 64 21</b> V48 …')<br><span> — leave the top edge at x=40, bite an r17 arc, rejoin at y=21</span></code>
</div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="ntf-13b" aria-label="Starburst seal and hexagon badges cut with clip-path polygons">
<article class="ntf-13b__card">
<span class="ntf-13b__seal" aria-hidden="true"><i class="ntf-13b__sealtxt">NEW</i></span>
<span class="ntf-13b__hex" aria-label="6 in stock">6</span>
<div class="ntf-13b__art"></div>
<h2 class="ntf-13b__name">Studio print No. 7</h2>
<p class="ntf-13b__meta">A2 · archival paper · edition of 50</p>
<p class="ntf-13b__hint">hover the starburst — same-vertex polygons morph</p>
</article>
</section>
</div>
```
## CSS
```css
.ntf-13-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.ntf-13-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.ntf-13a,
.ntf-13a *,
.ntf-13a *::before,
.ntf-13a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13a {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(160deg,#191d2a,#0d0f17 70%);
font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}
.ntf-13a__wrap {
display: grid;
gap: 26px;
justify-items: center;
}
.ntf-13a__dock {
display: flex;
gap: 22px;
padding: 20px 24px;
border-radius: 26px;
background: rgba(255,255,255,.06);
border: 1px solid rgba(170,195,235,.16);
box-shadow: 0 30px 60px -30px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.07);
}
.ntf-13a__slot {
position: relative;
width: 64px;
height: 64px;
filter: drop-shadow(0 10px 16px rgba(0,0,0,.45));
}
.ntf-13a__tile {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: linear-gradient(150deg,var(--g1),var(--g2));
clip-path: path('M16 0 H40 A17 17 0 0 0 64 21 V48 Q64 64 48 64 H16 Q0 64 0 48 V16 Q0 0 16 0 Z');
}
.ntf-13a__tile--plain {
clip-path: none;
border-radius: 16px;
}
.ntf-13a__tile svg {
width: 28px;
height: 28px;
}
.ntf-13a__count {
position: absolute;
top: -7px;
inset-inline-end: -7px;
min-inline-size: 24px;
block-size: 24px;
display: grid;
place-items: center;
padding-inline: 6px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
font-variant-numeric: tabular-nums;
font-style: normal;
color: #fff;
background: oklch(0.63 0.21 25);
animation: ntf-13a-pop .5s cubic-bezier(.22,1.6,.36,1) both .25s;
}
@keyframes ntf-13a-pop {
from {
scale: 0;
}
}
.ntf-13a__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: 11.5px;
line-height: 1.7;
color: rgba(205,220,248,.55);
text-align: center;
}
.ntf-13a__code b {
color: oklch(0.8 0.14 60);
font-weight: 500;
}
.ntf-13a__code span {
font-size: 10.5px;
color: rgba(205,220,248,.4);
}
@media (prefers-reduced-motion: reduce) {
.ntf-13a__count {
animation: none;
}
}
.ntf-13b,
.ntf-13b *,
.ntf-13b *::before,
.ntf-13b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ntf-13b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(180deg,#f5f1e8,#ece4d3);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.ntf-13b__card {
position: relative;
width: min(330px,100%);
padding: 18px 18px 22px;
border-radius: 20px;
background: #fffdf7;
border: 1px solid rgba(36,31,23,.12);
box-shadow: 0 30px 64px -34px rgba(36,31,23,.5);
}
.ntf-13b__art {
height: 190px;
border-radius: 13px;
background: linear-gradient(135deg,oklch(0.55 0.11 260) 0 34%,oklch(0.8 0.13 85) 34% 62%,oklch(0.62 0.16 30) 62%);
}
.ntf-13b__name {
margin-top: 14px;
font-size: 19px;
font-weight: 700;
letter-spacing: -.01em;
color: #241f17;
}
.ntf-13b__meta {
margin-top: 3px;
font-size: 12.5px;
color: rgba(36,31,23,.55);
}
.ntf-13b__hint {
margin-top: 12px;
font-size: 11px;
color: rgba(36,31,23,.4);
}
.ntf-13b__seal {
position: absolute;
top: -24px;
inset-inline-start: -20px;
width: 86px;
height: 86px;
display: grid;
place-items: center;
background: oklch(0.63 0.19 25);
clip-path: polygon(50% 0%,57% 14%,71% 5%,71% 21%,87% 18%,80% 32%,96% 36%,84% 46%,97% 56%,81% 59%,89% 74%,73% 71%,74% 88%,60% 78%,54% 94%,46% 79%,34% 91%,32% 74%,16% 79%,22% 63%,5% 60%,18% 50%,4% 41%,19% 35%,10% 21%,27% 23%,26% 6%,40% 15%);
animation: ntf-13b-spin 24s linear infinite;
transition: clip-path .45s cubic-bezier(.22,1.2,.36,1),scale .3s;
}
.ntf-13b__seal:hover {
scale: 1.08;
clip-path: polygon(50% 4%,58% 16%,70% 9%,71% 23%,85% 20%,80% 33%,93% 38%,83% 47%,94% 56%,80% 59%,86% 72%,72% 69%,73% 84%,60% 77%,54% 90%,46% 77%,35% 87%,33% 72%,19% 76%,24% 62%,8% 59%,20% 50%,7% 42%,21% 37%,13% 24%,28% 26%,28% 10%,41% 17%);
}
.ntf-13b__sealtxt {
font-style: normal;
font-size: 15px;
font-weight: 800;
letter-spacing: .08em;
color: #fff;
animation: ntf-13b-spin 24s linear infinite reverse;
}
@keyframes ntf-13b-spin {
to {
rotate: 360deg;
}
}
.ntf-13b__hex {
position: absolute;
top: -13px;
inset-inline-end: -11px;
width: 38px;
height: 42px;
display: grid;
place-items: center;
font-size: 14px;
font-weight: 800;
color: #241f17;
background: oklch(0.85 0.14 85);
clip-path: polygon(50% 0%,100% 25%,100% 75%,50% 100%,0% 75%,0% 25%);
animation: ntf-13b-pop .5s cubic-bezier(.22,1.6,.36,1) both .3s;
}
@keyframes ntf-13b-pop {
from {
scale: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.ntf-13b__seal,
.ntf-13b__sealtxt {
animation: none;
}
.ntf-13b__hex {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the notch is carved from the TILE by clip-path:path() (sweep flag 0 makes the arc concave); the badge is an ordinary circle parked in the hole. The shadow lives on the un-clipped wrapper via filter:drop-shadow. */
/* No JavaScript — the seal spins while its text counter-rotates (same duration, reversed); hover morphs between two 28-vertex polygons, which interpolate because the vertex counts match. */
```How this works
A notch is just a path that detours around a circle. Clip the ICON, not the badge — the badge stays a plain circle sitting in the carved gap:
.icon{
clip-path:path('M16 0 H40 A17 17 0 0 0 64 21 \
V48 Q64 64 48 64 H16 Q0 64 0 48 V16 Q0 0 16 0 Z');
}
.badge{ position:absolute; top:-7px; inset-inline-end:-7px; }Reading the arc: leave the top edge at x=40, sweep an r=17 arc (sweep flag 0 = bowing INTO the tile) and rejoin the right edge at y=21 — the concave bite the badge sits in. path() uses fixed units, so this suits fixed-size tiles (app icons, dock items); for fluid elements prefer the mask technique from the avatar-badge topic. The polygon badges exploit a different superpower: clip-path:polygon() values with the SAME number of vertices animate smoothly, so a hover rule that swaps spike depths morphs the starburst — and animation:spin on the clipped element rotates the whole seal while the ribbon text counter-rotates to stay level.
Make it yours
- Notch size: arc radius = badge radius + gap + border allowance; the demo's 17 = 12px badge + 3px gap + rounding.
- Move the notch to any corner by mirroring the path commands — or generate the d-string in JS for arbitrary tile sizes.
- Starburst spikes: alternate outer/inner radii around a circle; more points = laurel seal, fewer = sheriff star. Keep vertex counts equal across states you morph between.
- Slow the seal's spin to 20s+ — decorative rotation should be felt, not watched.
Gotchas — read before shipping
- clip-path clips PAINT, not the hit-area on some older engines — but on evergreen browsers clicks outside the clip DO fall through; don't hide interactive edges inside a clip.
- path() ignores em/%/vw — it's px-only, which is why this pattern belongs on fixed-size tiles.
- Morphing only interpolates polygons with identical vertex counts; 16 points to 12 points snaps — pad the smaller shape with duplicate vertices.
- A clipped element's box-shadow is clipped away with everything else — put shadows on a parent wrapper, as the dock tile does here.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 88+.
clip-path:polygon() is ancient; clip-path:path() needs Chrome 88 / Safari 13.1 / Firefox 97. Everything here degrades to an un-notched tile or un-clipped rectangle — content and counts stay visible.