22 / 27
All 27 in 27 CSS Calendars
- 01Glassmorphism CSS Calendar Widget
- 02Brutalist CSS Calendar Design
- 03Dark Mode CSS Calendar UI
- 04Pure CSS Calendar (No JavaScript)
- 05CSS Grid Calendar Layout
- 06Responsive Mobile-Friendly Calendar UI
- 07Glassmorphism Calendar Card
- 08Dark Mode Calendar Design
- 09Event Planner & Schedule Layout
- 10Neumorphic Calendar Widget
- 11Horizontal Scroll / Timeline Calendar
- 12Minimalist Sidebar Calendar Card
- 13CSS Booking Date Picker UI
- 14CSS Grid Advent Calendar
- 15Fluent / Material Design Calendar
- 16Minimalist Circular / Radial Calendar Layout
- 17Retro Neon / Cyberpunk Grid Calendar
- 18Brutalist Bold Typography Calendar
- 19Interactive 3D Flip Card Calendar
- 20Infinite Isometric Dashboard Calendar View
- 21Fluid Split-Screen Hero Calendar
- 22Micro-Interactions & Liquid Bubble Date Hover
- 23Bento Grid Style Booking System
- 24Vintage Skeuomorphic / Paper-Torn Tear-off Design
- 25Vertical Timeline Slipstream Calendar
- 26Kinetic Typography Changing Calendar
- 27Diagonal Slanted Grid Calendar
Micro-Interactions & Liquid Bubble Date Hover
A plain-looking grid whose magic is the hover. A goo layer with filter:blur+contrast and screen blending fuses circles into a liquid metaball; each cell's hidden blob springs to scale under the cursor and melts away. Today wobbles a morphing gradient blob.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<div class="cal22">
<div class="cal22__card">
<div class="cal22__head">
<div>
<div class="cal22__title">June <span>2026</span></div>
<div class="cal22__hint">// hover the dates — liquid goo</div>
</div>
<div class="cal22__nav">
<div class="cal22__nav-btn">‹</div>
<div class="cal22__nav-btn">›</div>
</div>
</div>
<div class="cal22__dow">
<div class="cal22__dw">S</div><div class="cal22__dw">M</div><div class="cal22__dw">T</div>
<div class="cal22__dw">W</div><div class="cal22__dw">T</div><div class="cal22__dw">F</div><div class="cal22__dw">S</div>
</div>
<div class="cal22__gridwrap">
<div class="cal22__goo" id="cal22Goo"></div>
<div class="cal22__grid" id="cal22Grid">
<div class="cal22__cell other">25</div><div class="cal22__cell other">26</div><div class="cal22__cell other">27</div>
<div class="cal22__cell other">28</div><div class="cal22__cell other">29</div><div class="cal22__cell other">30</div><div class="cal22__cell other">31</div>
<div class="cal22__cell">1</div>
<div class="cal22__cell">2<span class="ev"></span></div>
<div class="cal22__cell">3</div>
<div class="cal22__cell">4<span class="ev"></span></div>
<div class="cal22__cell">5</div>
<div class="cal22__cell">6</div>
<div class="cal22__cell">7<span class="ev"></span></div>
<div class="cal22__cell today">8</div>
<div class="cal22__cell">9<span class="ev"></span></div>
<div class="cal22__cell">10</div>
<div class="cal22__cell">11<span class="ev"></span></div>
<div class="cal22__cell">12</div>
<div class="cal22__cell">13<span class="ev"></span></div>
<div class="cal22__cell">14</div>
<div class="cal22__cell">15</div>
<div class="cal22__cell">16</div>
<div class="cal22__cell">17<span class="ev"></span></div>
<div class="cal22__cell">18</div>
<div class="cal22__cell">19</div>
<div class="cal22__cell">20<span class="ev"></span></div>
<div class="cal22__cell">21</div>
<div class="cal22__cell">22</div>
<div class="cal22__cell">23</div>
<div class="cal22__cell">24<span class="ev"></span></div>
<div class="cal22__cell">25</div>
<div class="cal22__cell">26</div>
<div class="cal22__cell">27<span class="ev"></span></div>
<div class="cal22__cell">28</div>
<div class="cal22__cell">29</div>
<div class="cal22__cell">30</div>
<div class="cal22__cell other">1</div><div class="cal22__cell other">2</div><div class="cal22__cell other">3</div>
<div class="cal22__cell other">4</div><div class="cal22__cell other">5</div>
</div>
</div>
<div class="cal22__foot">
<div class="cal22__foot-blob"></div>
<div class="cal22__foot-body">
<div class="cal22__foot-title">Sunday, June 8</div>
<div class="cal22__foot-sub">3 events · sprint, launch, review</div>
</div>
</div>
</div>
</div><div class="cal22">
<div class="cal22__card">
<div class="cal22__head">
<div>
<div class="cal22__title">June <span>2026</span></div>
<div class="cal22__hint">// hover the dates — liquid goo</div>
</div>
<div class="cal22__nav">
<div class="cal22__nav-btn">‹</div>
<div class="cal22__nav-btn">›</div>
</div>
</div>
<div class="cal22__dow">
<div class="cal22__dw">S</div><div class="cal22__dw">M</div><div class="cal22__dw">T</div>
<div class="cal22__dw">W</div><div class="cal22__dw">T</div><div class="cal22__dw">F</div><div class="cal22__dw">S</div>
</div>
<div class="cal22__gridwrap">
<div class="cal22__goo" id="cal22Goo"></div>
<div class="cal22__grid" id="cal22Grid">
<div class="cal22__cell other">25</div><div class="cal22__cell other">26</div><div class="cal22__cell other">27</div>
<div class="cal22__cell other">28</div><div class="cal22__cell other">29</div><div class="cal22__cell other">30</div><div class="cal22__cell other">31</div>
<div class="cal22__cell">1</div>
<div class="cal22__cell">2<span class="ev"></span></div>
<div class="cal22__cell">3</div>
<div class="cal22__cell">4<span class="ev"></span></div>
<div class="cal22__cell">5</div>
<div class="cal22__cell">6</div>
<div class="cal22__cell">7<span class="ev"></span></div>
<div class="cal22__cell today">8</div>
<div class="cal22__cell">9<span class="ev"></span></div>
<div class="cal22__cell">10</div>
<div class="cal22__cell">11<span class="ev"></span></div>
<div class="cal22__cell">12</div>
<div class="cal22__cell">13<span class="ev"></span></div>
<div class="cal22__cell">14</div>
<div class="cal22__cell">15</div>
<div class="cal22__cell">16</div>
<div class="cal22__cell">17<span class="ev"></span></div>
<div class="cal22__cell">18</div>
<div class="cal22__cell">19</div>
<div class="cal22__cell">20<span class="ev"></span></div>
<div class="cal22__cell">21</div>
<div class="cal22__cell">22</div>
<div class="cal22__cell">23</div>
<div class="cal22__cell">24<span class="ev"></span></div>
<div class="cal22__cell">25</div>
<div class="cal22__cell">26</div>
<div class="cal22__cell">27<span class="ev"></span></div>
<div class="cal22__cell">28</div>
<div class="cal22__cell">29</div>
<div class="cal22__cell">30</div>
<div class="cal22__cell other">1</div><div class="cal22__cell other">2</div><div class="cal22__cell other">3</div>
<div class="cal22__cell other">4</div><div class="cal22__cell other">5</div>
</div>
</div>
<div class="cal22__foot">
<div class="cal22__foot-blob"></div>
<div class="cal22__foot-body">
<div class="cal22__foot-title">Sunday, June 8</div>
<div class="cal22__foot-sub">3 events · sprint, launch, review</div>
</div>
</div>
</div>
</div>.cal22,
.cal22 *,
.cal22 *::before,
.cal22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal22 ::selection {
background: #ff5470;
color: #fff;
}
.cal22 {
--bg: #0f0e17;
--surface: #16151f;
--text: #fffffe;
--text2: #a7a9be;
--text3: #5a5b70;
--blob: #ff5470;
--blob2: #ff8906;
--accent: #e53170;
--soft: #2e2f3e;
font-family: 'Bricolage Grotesque', sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: var(--text);
}
@keyframes cal22-in {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal22-wobble {
0%,
100% {
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
}
50% {
border-radius: 56% 44% 48% 52% / 56% 44% 56% 44%;
}
}
.cal22__card {
max-width: 460px;
width: 100%;
background: var(--surface);
border-radius: 28px;
padding: 32px;
box-shadow: 0 30px 70px rgba(0,0,0,0.5);
animation: cal22-in 0.5s ease both;
position: relative;
}
/* ── Header ── */
.cal22__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.cal22__title {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.02em;
}
.cal22__title span {
background: linear-gradient(135deg, var(--blob), var(--blob2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.cal22__hint {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 3px;
}
.cal22__nav {
display: flex;
gap: 8px;
}
.cal22__nav-btn {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--soft);
border: none;
color: var(--text2);
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}
.cal22__nav-btn:hover {
background: var(--blob);
color: #fff;
}
/* ── DOW ── */
.cal22__dow {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 8px;
}
.cal22__dw {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 11px;
font-weight: 500;
color: var(--text3);
padding: 4px 0;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GOOEY LIQUID LAYER — the core trick.
A blurred + high-contrast filter on the
blob container fuses overlapping circles
into a single liquid mass (metaball effect).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal22__gridwrap {
position: relative;
}
/* Goo layer sits behind the numbers */
.cal22__goo {
position: absolute;
inset: 0;
filter: blur(8px) contrast(22);
pointer-events: none;
z-index: 1;
mix-blend-mode: screen;
}
/* Each blob is a small circle that grows + wobbles on its cell's hover */
.cal22__blob {
position: absolute;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--blob);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
will-change: transform;
}
.cal22__grid {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.cal22__cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
font-weight: 600;
color: var(--text);
cursor: pointer;
position: relative;
border-radius: 50%;
transition: color 0.2s;
}
.cal22__cell:hover {
color: #fff;
}
.cal22__cell.other {
color: var(--text3);
opacity: 0.4;
pointer-events: none;
}
/* Today — persistent gradient blob underneath via box-shadow ring */
.cal22__cell.today {
color: #fff;
font-weight: 800;
}
.cal22__cell.today::after {
content: '';
position: absolute;
inset: 6px;
border-radius: 50%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
z-index: -1;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__cell.selected {
color: #fff;
font-weight: 800;
}
.cal22__cell.selected::before {
content: '';
position: absolute;
inset: 5px;
border-radius: 50%;
border: 2px solid var(--blob2);
z-index: -1;
}
/* event dot */
.cal22__cell .ev {
position: absolute;
bottom: 8px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--blob2);
}
.cal22__cell.today .ev,
.cal22__cell:hover .ev {
background: #fff;
}
/* ── Footer readout ── */
.cal22__foot {
margin-top: 26px;
padding: 16px 18px;
border-radius: 18px;
background: var(--bg);
display: flex;
align-items: center;
gap: 14px;
}
.cal22__foot-blob {
width: 44px;
height: 44px;
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
flex-shrink: 0;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__foot-body {
}
.cal22__foot-title {
font-size: 14px;
font-weight: 600;
}
.cal22__foot-sub {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 2px;
}
@media (prefers-reduced-motion: reduce) {
.cal22 * {
animation: none !important;
}
}.cal22,
.cal22 *,
.cal22 *::before,
.cal22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal22 ::selection {
background: #ff5470;
color: #fff;
}
.cal22 {
--bg: #0f0e17;
--surface: #16151f;
--text: #fffffe;
--text2: #a7a9be;
--text3: #5a5b70;
--blob: #ff5470;
--blob2: #ff8906;
--accent: #e53170;
--soft: #2e2f3e;
font-family: 'Bricolage Grotesque', sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: var(--text);
}
@keyframes cal22-in {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal22-wobble {
0%,
100% {
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
}
50% {
border-radius: 56% 44% 48% 52% / 56% 44% 56% 44%;
}
}
.cal22__card {
max-width: 460px;
width: 100%;
background: var(--surface);
border-radius: 28px;
padding: 32px;
box-shadow: 0 30px 70px rgba(0,0,0,0.5);
animation: cal22-in 0.5s ease both;
position: relative;
}
/* ── Header ── */
.cal22__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.cal22__title {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.02em;
}
.cal22__title span {
background: linear-gradient(135deg, var(--blob), var(--blob2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.cal22__hint {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 3px;
}
.cal22__nav {
display: flex;
gap: 8px;
}
.cal22__nav-btn {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--soft);
border: none;
color: var(--text2);
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}
.cal22__nav-btn:hover {
background: var(--blob);
color: #fff;
}
/* ── DOW ── */
.cal22__dow {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 8px;
}
.cal22__dw {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 11px;
font-weight: 500;
color: var(--text3);
padding: 4px 0;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GOOEY LIQUID LAYER — the core trick.
A blurred + high-contrast filter on the
blob container fuses overlapping circles
into a single liquid mass (metaball effect).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal22__gridwrap {
position: relative;
}
/* Goo layer sits behind the numbers */
.cal22__goo {
position: absolute;
inset: 0;
filter: blur(8px) contrast(22);
pointer-events: none;
z-index: 1;
mix-blend-mode: screen;
}
/* Each blob is a small circle that grows + wobbles on its cell's hover */
.cal22__blob {
position: absolute;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--blob);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
will-change: transform;
}
.cal22__grid {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.cal22__cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
font-weight: 600;
color: var(--text);
cursor: pointer;
position: relative;
border-radius: 50%;
transition: color 0.2s;
}
.cal22__cell:hover {
color: #fff;
}
.cal22__cell.other {
color: var(--text3);
opacity: 0.4;
pointer-events: none;
}
/* Today — persistent gradient blob underneath via box-shadow ring */
.cal22__cell.today {
color: #fff;
font-weight: 800;
}
.cal22__cell.today::after {
content: '';
position: absolute;
inset: 6px;
border-radius: 50%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
z-index: -1;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__cell.selected {
color: #fff;
font-weight: 800;
}
.cal22__cell.selected::before {
content: '';
position: absolute;
inset: 5px;
border-radius: 50%;
border: 2px solid var(--blob2);
z-index: -1;
}
/* event dot */
.cal22__cell .ev {
position: absolute;
bottom: 8px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--blob2);
}
.cal22__cell.today .ev,
.cal22__cell:hover .ev {
background: #fff;
}
/* ── Footer readout ── */
.cal22__foot {
margin-top: 26px;
padding: 16px 18px;
border-radius: 18px;
background: var(--bg);
display: flex;
align-items: center;
gap: 14px;
}
.cal22__foot-blob {
width: 44px;
height: 44px;
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
flex-shrink: 0;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__foot-body {
}
.cal22__foot-title {
font-size: 14px;
font-weight: 600;
}
.cal22__foot-sub {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 2px;
}
@media (prefers-reduced-motion: reduce) {
.cal22 * {
animation: none !important;
}
}(() => {
const grid = document.getElementById('cal22Grid');
const goo = document.getElementById('cal22Goo');
const cells = grid.querySelectorAll('.cal22__cell:not(.other)');
// create one blob per real cell, track to its position
cells.forEach(cell => {
const blob = document.createElement('div');
blob.className = 'cal22__blob';
goo.appendChild(blob);
function place() {
const gr = grid.getBoundingClientRect();
const cr = cell.getBoundingClientRect();
blob.style.left = (cr.left - gr.left + cr.width/2) + 'px';
blob.style.top = (cr.top - gr.top + cr.height/2) + 'px';
}
cell.addEventListener('mouseenter', () => { place(); blob.style.transform = 'translate(-50%,-50%) scale(1.15)'; });
cell.addEventListener('mouseleave', () => { blob.style.transform = 'translate(-50%,-50%) scale(0)'; });
cell.addEventListener('click', function() {
cells.forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});
})();(() => {
const grid = document.getElementById('cal22Grid');
const goo = document.getElementById('cal22Goo');
const cells = grid.querySelectorAll('.cal22__cell:not(.other)');
// create one blob per real cell, track to its position
cells.forEach(cell => {
const blob = document.createElement('div');
blob.className = 'cal22__blob';
goo.appendChild(blob);
function place() {
const gr = grid.getBoundingClientRect();
const cr = cell.getBoundingClientRect();
blob.style.left = (cr.left - gr.left + cr.width/2) + 'px';
blob.style.top = (cr.top - gr.top + cr.height/2) + 'px';
}
cell.addEventListener('mouseenter', () => { place(); blob.style.transform = 'translate(-50%,-50%) scale(1.15)'; });
cell.addEventListener('mouseleave', () => { blob.style.transform = 'translate(-50%,-50%) scale(0)'; });
cell.addEventListener('click', function() {
cells.forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});
})();Here's a working CSS Calendar 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: Micro-Interactions & Liquid Bubble Date Hover
Source: https://codefronts.com/components/css-calendar-designs/micro-interactions-liquid-bubble-date-hover/
A plain-looking grid whose magic is the hover. A goo layer with filter:blur+contrast and screen blending fuses circles into a liquid metaball; each cell's hidden blob springs to scale under the cursor and melts away. Today wobbles a morphing gradient blob.
## HTML
```html
<div class="cal22">
<div class="cal22__card">
<div class="cal22__head">
<div>
<div class="cal22__title">June <span>2026</span></div>
<div class="cal22__hint">// hover the dates — liquid goo</div>
</div>
<div class="cal22__nav">
<div class="cal22__nav-btn">‹</div>
<div class="cal22__nav-btn">›</div>
</div>
</div>
<div class="cal22__dow">
<div class="cal22__dw">S</div><div class="cal22__dw">M</div><div class="cal22__dw">T</div>
<div class="cal22__dw">W</div><div class="cal22__dw">T</div><div class="cal22__dw">F</div><div class="cal22__dw">S</div>
</div>
<div class="cal22__gridwrap">
<div class="cal22__goo" id="cal22Goo"></div>
<div class="cal22__grid" id="cal22Grid">
<div class="cal22__cell other">25</div><div class="cal22__cell other">26</div><div class="cal22__cell other">27</div>
<div class="cal22__cell other">28</div><div class="cal22__cell other">29</div><div class="cal22__cell other">30</div><div class="cal22__cell other">31</div>
<div class="cal22__cell">1</div>
<div class="cal22__cell">2<span class="ev"></span></div>
<div class="cal22__cell">3</div>
<div class="cal22__cell">4<span class="ev"></span></div>
<div class="cal22__cell">5</div>
<div class="cal22__cell">6</div>
<div class="cal22__cell">7<span class="ev"></span></div>
<div class="cal22__cell today">8</div>
<div class="cal22__cell">9<span class="ev"></span></div>
<div class="cal22__cell">10</div>
<div class="cal22__cell">11<span class="ev"></span></div>
<div class="cal22__cell">12</div>
<div class="cal22__cell">13<span class="ev"></span></div>
<div class="cal22__cell">14</div>
<div class="cal22__cell">15</div>
<div class="cal22__cell">16</div>
<div class="cal22__cell">17<span class="ev"></span></div>
<div class="cal22__cell">18</div>
<div class="cal22__cell">19</div>
<div class="cal22__cell">20<span class="ev"></span></div>
<div class="cal22__cell">21</div>
<div class="cal22__cell">22</div>
<div class="cal22__cell">23</div>
<div class="cal22__cell">24<span class="ev"></span></div>
<div class="cal22__cell">25</div>
<div class="cal22__cell">26</div>
<div class="cal22__cell">27<span class="ev"></span></div>
<div class="cal22__cell">28</div>
<div class="cal22__cell">29</div>
<div class="cal22__cell">30</div>
<div class="cal22__cell other">1</div><div class="cal22__cell other">2</div><div class="cal22__cell other">3</div>
<div class="cal22__cell other">4</div><div class="cal22__cell other">5</div>
</div>
</div>
<div class="cal22__foot">
<div class="cal22__foot-blob"></div>
<div class="cal22__foot-body">
<div class="cal22__foot-title">Sunday, June 8</div>
<div class="cal22__foot-sub">3 events · sprint, launch, review</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.cal22,
.cal22 *,
.cal22 *::before,
.cal22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal22 ::selection {
background: #ff5470;
color: #fff;
}
.cal22 {
--bg: #0f0e17;
--surface: #16151f;
--text: #fffffe;
--text2: #a7a9be;
--text3: #5a5b70;
--blob: #ff5470;
--blob2: #ff8906;
--accent: #e53170;
--soft: #2e2f3e;
font-family: 'Bricolage Grotesque', sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: var(--text);
}
@keyframes cal22-in {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal22-wobble {
0%,
100% {
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
}
50% {
border-radius: 56% 44% 48% 52% / 56% 44% 56% 44%;
}
}
.cal22__card {
max-width: 460px;
width: 100%;
background: var(--surface);
border-radius: 28px;
padding: 32px;
box-shadow: 0 30px 70px rgba(0,0,0,0.5);
animation: cal22-in 0.5s ease both;
position: relative;
}
/* ── Header ── */
.cal22__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.cal22__title {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.02em;
}
.cal22__title span {
background: linear-gradient(135deg, var(--blob), var(--blob2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.cal22__hint {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 3px;
}
.cal22__nav {
display: flex;
gap: 8px;
}
.cal22__nav-btn {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--soft);
border: none;
color: var(--text2);
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}
.cal22__nav-btn:hover {
background: var(--blob);
color: #fff;
}
/* ── DOW ── */
.cal22__dow {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 8px;
}
.cal22__dw {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 11px;
font-weight: 500;
color: var(--text3);
padding: 4px 0;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GOOEY LIQUID LAYER — the core trick.
A blurred + high-contrast filter on the
blob container fuses overlapping circles
into a single liquid mass (metaball effect).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal22__gridwrap {
position: relative;
}
/* Goo layer sits behind the numbers */
.cal22__goo {
position: absolute;
inset: 0;
filter: blur(8px) contrast(22);
pointer-events: none;
z-index: 1;
mix-blend-mode: screen;
}
/* Each blob is a small circle that grows + wobbles on its cell's hover */
.cal22__blob {
position: absolute;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--blob);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
will-change: transform;
}
.cal22__grid {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.cal22__cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
font-weight: 600;
color: var(--text);
cursor: pointer;
position: relative;
border-radius: 50%;
transition: color 0.2s;
}
.cal22__cell:hover {
color: #fff;
}
.cal22__cell.other {
color: var(--text3);
opacity: 0.4;
pointer-events: none;
}
/* Today — persistent gradient blob underneath via box-shadow ring */
.cal22__cell.today {
color: #fff;
font-weight: 800;
}
.cal22__cell.today::after {
content: '';
position: absolute;
inset: 6px;
border-radius: 50%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
z-index: -1;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__cell.selected {
color: #fff;
font-weight: 800;
}
.cal22__cell.selected::before {
content: '';
position: absolute;
inset: 5px;
border-radius: 50%;
border: 2px solid var(--blob2);
z-index: -1;
}
/* event dot */
.cal22__cell .ev {
position: absolute;
bottom: 8px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--blob2);
}
.cal22__cell.today .ev,
.cal22__cell:hover .ev {
background: #fff;
}
/* ── Footer readout ── */
.cal22__foot {
margin-top: 26px;
padding: 16px 18px;
border-radius: 18px;
background: var(--bg);
display: flex;
align-items: center;
gap: 14px;
}
.cal22__foot-blob {
width: 44px;
height: 44px;
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
flex-shrink: 0;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__foot-body {
}
.cal22__foot-title {
font-size: 14px;
font-weight: 600;
}
.cal22__foot-sub {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 2px;
}
@media (prefers-reduced-motion: reduce) {
.cal22 * {
animation: none !important;
}
}
```
## JavaScript
```js
(() => {
const grid = document.getElementById('cal22Grid');
const goo = document.getElementById('cal22Goo');
const cells = grid.querySelectorAll('.cal22__cell:not(.other)');
// create one blob per real cell, track to its position
cells.forEach(cell => {
const blob = document.createElement('div');
blob.className = 'cal22__blob';
goo.appendChild(blob);
function place() {
const gr = grid.getBoundingClientRect();
const cr = cell.getBoundingClientRect();
blob.style.left = (cr.left - gr.left + cr.width/2) + 'px';
blob.style.top = (cr.top - gr.top + cr.height/2) + 'px';
}
cell.addEventListener('mouseenter', () => { place(); blob.style.transform = 'translate(-50%,-50%) scale(1.15)'; });
cell.addEventListener('mouseleave', () => { blob.style.transform = 'translate(-50%,-50%) scale(0)'; });
cell.addEventListener('click', function() {
cells.forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});
})();
```Here's a working CSS Calendar 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: Micro-Interactions & Liquid Bubble Date Hover
Source: https://codefronts.com/components/css-calendar-designs/micro-interactions-liquid-bubble-date-hover/
A plain-looking grid whose magic is the hover. A goo layer with filter:blur+contrast and screen blending fuses circles into a liquid metaball; each cell's hidden blob springs to scale under the cursor and melts away. Today wobbles a morphing gradient blob.
## HTML
```html
<div class="cal22">
<div class="cal22__card">
<div class="cal22__head">
<div>
<div class="cal22__title">June <span>2026</span></div>
<div class="cal22__hint">// hover the dates — liquid goo</div>
</div>
<div class="cal22__nav">
<div class="cal22__nav-btn">‹</div>
<div class="cal22__nav-btn">›</div>
</div>
</div>
<div class="cal22__dow">
<div class="cal22__dw">S</div><div class="cal22__dw">M</div><div class="cal22__dw">T</div>
<div class="cal22__dw">W</div><div class="cal22__dw">T</div><div class="cal22__dw">F</div><div class="cal22__dw">S</div>
</div>
<div class="cal22__gridwrap">
<div class="cal22__goo" id="cal22Goo"></div>
<div class="cal22__grid" id="cal22Grid">
<div class="cal22__cell other">25</div><div class="cal22__cell other">26</div><div class="cal22__cell other">27</div>
<div class="cal22__cell other">28</div><div class="cal22__cell other">29</div><div class="cal22__cell other">30</div><div class="cal22__cell other">31</div>
<div class="cal22__cell">1</div>
<div class="cal22__cell">2<span class="ev"></span></div>
<div class="cal22__cell">3</div>
<div class="cal22__cell">4<span class="ev"></span></div>
<div class="cal22__cell">5</div>
<div class="cal22__cell">6</div>
<div class="cal22__cell">7<span class="ev"></span></div>
<div class="cal22__cell today">8</div>
<div class="cal22__cell">9<span class="ev"></span></div>
<div class="cal22__cell">10</div>
<div class="cal22__cell">11<span class="ev"></span></div>
<div class="cal22__cell">12</div>
<div class="cal22__cell">13<span class="ev"></span></div>
<div class="cal22__cell">14</div>
<div class="cal22__cell">15</div>
<div class="cal22__cell">16</div>
<div class="cal22__cell">17<span class="ev"></span></div>
<div class="cal22__cell">18</div>
<div class="cal22__cell">19</div>
<div class="cal22__cell">20<span class="ev"></span></div>
<div class="cal22__cell">21</div>
<div class="cal22__cell">22</div>
<div class="cal22__cell">23</div>
<div class="cal22__cell">24<span class="ev"></span></div>
<div class="cal22__cell">25</div>
<div class="cal22__cell">26</div>
<div class="cal22__cell">27<span class="ev"></span></div>
<div class="cal22__cell">28</div>
<div class="cal22__cell">29</div>
<div class="cal22__cell">30</div>
<div class="cal22__cell other">1</div><div class="cal22__cell other">2</div><div class="cal22__cell other">3</div>
<div class="cal22__cell other">4</div><div class="cal22__cell other">5</div>
</div>
</div>
<div class="cal22__foot">
<div class="cal22__foot-blob"></div>
<div class="cal22__foot-body">
<div class="cal22__foot-title">Sunday, June 8</div>
<div class="cal22__foot-sub">3 events · sprint, launch, review</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.cal22,
.cal22 *,
.cal22 *::before,
.cal22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal22 ::selection {
background: #ff5470;
color: #fff;
}
.cal22 {
--bg: #0f0e17;
--surface: #16151f;
--text: #fffffe;
--text2: #a7a9be;
--text3: #5a5b70;
--blob: #ff5470;
--blob2: #ff8906;
--accent: #e53170;
--soft: #2e2f3e;
font-family: 'Bricolage Grotesque', sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: var(--text);
}
@keyframes cal22-in {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal22-wobble {
0%,
100% {
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
}
50% {
border-radius: 56% 44% 48% 52% / 56% 44% 56% 44%;
}
}
.cal22__card {
max-width: 460px;
width: 100%;
background: var(--surface);
border-radius: 28px;
padding: 32px;
box-shadow: 0 30px 70px rgba(0,0,0,0.5);
animation: cal22-in 0.5s ease both;
position: relative;
}
/* ── Header ── */
.cal22__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.cal22__title {
font-size: 32px;
font-weight: 800;
letter-spacing: -0.02em;
}
.cal22__title span {
background: linear-gradient(135deg, var(--blob), var(--blob2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.cal22__hint {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 3px;
}
.cal22__nav {
display: flex;
gap: 8px;
}
.cal22__nav-btn {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--soft);
border: none;
color: var(--text2);
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}
.cal22__nav-btn:hover {
background: var(--blob);
color: #fff;
}
/* ── DOW ── */
.cal22__dow {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 8px;
}
.cal22__dw {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 11px;
font-weight: 500;
color: var(--text3);
padding: 4px 0;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GOOEY LIQUID LAYER — the core trick.
A blurred + high-contrast filter on the
blob container fuses overlapping circles
into a single liquid mass (metaball effect).
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal22__gridwrap {
position: relative;
}
/* Goo layer sits behind the numbers */
.cal22__goo {
position: absolute;
inset: 0;
filter: blur(8px) contrast(22);
pointer-events: none;
z-index: 1;
mix-blend-mode: screen;
}
/* Each blob is a small circle that grows + wobbles on its cell's hover */
.cal22__blob {
position: absolute;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--blob);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
will-change: transform;
}
.cal22__grid {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.cal22__cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
font-weight: 600;
color: var(--text);
cursor: pointer;
position: relative;
border-radius: 50%;
transition: color 0.2s;
}
.cal22__cell:hover {
color: #fff;
}
.cal22__cell.other {
color: var(--text3);
opacity: 0.4;
pointer-events: none;
}
/* Today — persistent gradient blob underneath via box-shadow ring */
.cal22__cell.today {
color: #fff;
font-weight: 800;
}
.cal22__cell.today::after {
content: '';
position: absolute;
inset: 6px;
border-radius: 50%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
z-index: -1;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__cell.selected {
color: #fff;
font-weight: 800;
}
.cal22__cell.selected::before {
content: '';
position: absolute;
inset: 5px;
border-radius: 50%;
border: 2px solid var(--blob2);
z-index: -1;
}
/* event dot */
.cal22__cell .ev {
position: absolute;
bottom: 8px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--blob2);
}
.cal22__cell.today .ev,
.cal22__cell:hover .ev {
background: #fff;
}
/* ── Footer readout ── */
.cal22__foot {
margin-top: 26px;
padding: 16px 18px;
border-radius: 18px;
background: var(--bg);
display: flex;
align-items: center;
gap: 14px;
}
.cal22__foot-blob {
width: 44px;
height: 44px;
border-radius: 42% 58% 56% 44% / 48% 50% 50% 52%;
background: linear-gradient(135deg, var(--blob), var(--blob2));
flex-shrink: 0;
animation: cal22-wobble 4s ease-in-out infinite;
}
.cal22__foot-body {
}
.cal22__foot-title {
font-size: 14px;
font-weight: 600;
}
.cal22__foot-sub {
font-family: 'DM Mono', monospace;
font-size: 11px;
color: var(--text3);
margin-top: 2px;
}
@media (prefers-reduced-motion: reduce) {
.cal22 * {
animation: none !important;
}
}
```
## JavaScript
```js
(() => {
const grid = document.getElementById('cal22Grid');
const goo = document.getElementById('cal22Goo');
const cells = grid.querySelectorAll('.cal22__cell:not(.other)');
// create one blob per real cell, track to its position
cells.forEach(cell => {
const blob = document.createElement('div');
blob.className = 'cal22__blob';
goo.appendChild(blob);
function place() {
const gr = grid.getBoundingClientRect();
const cr = cell.getBoundingClientRect();
blob.style.left = (cr.left - gr.left + cr.width/2) + 'px';
blob.style.top = (cr.top - gr.top + cr.height/2) + 'px';
}
cell.addEventListener('mouseenter', () => { place(); blob.style.transform = 'translate(-50%,-50%) scale(1.15)'; });
cell.addEventListener('mouseleave', () => { blob.style.transform = 'translate(-50%,-50%) scale(0)'; });
cell.addEventListener('click', function() {
cells.forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});
})();
```How this works
A plain-looking grid whose magic is the hover interaction. When you hover any date, a “goo” layer above the cell uses filter:blur(10px) contrast(10) with mix-blend-mode:screen — the blur softens circular sub-elements to blobs, the contrast sharpens the alpha edges back to hard outlines, and the blend fuses adjacent circles into liquid-metal shapes.
Result: hovering a cell creates a liquid bubble that appears to squeeze and pop off adjacent cells' influence. This is the classic Lucas Bebber “goo filter” technique adapted for calendar interaction.
Zero JavaScript for the effect itself; a short JS handles month navigation.
Make it yours
- Rebrand the goo colour via
--goo— any solid saturated colour works; muted greys break the effect. - Tune blur radius — 8px subtle blob, 14px pronounced viscosity.
- Adjust contrast multiplier — 8 for softer edges, 12 for harder shapes.
- Change blend mode from
screentooverlayfor a darker viscous look. - Add multiple goo layers with different blur/contrast for depth.
Gotchas — read before shipping
- The
filter:blur() contrast()combo is GPU-expensive — cap the goo layer's rendering area to just the calendar grid, not the whole page. mix-blend-moderequires the parent to have an explicit background — transparent parents show no goo effect.- Under
prefers-reduced-motion:reduce, disable the goo animation entirely (or freeze at rest position). - The goo effect is visual signal only — don't rely on it to convey information; screen readers get nothing from the blob morphing.
- For dark backgrounds, the goo colour needs to be light (white/pastel) for the screen blend to be visible.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 9+ | 16+ | 45+ |
CSS filter and mix-blend-mode are universally supported.