27 CSS Calendars25 / 27
Vertical Timeline Slipstream Calendar
Dates cascade down a central spine, alternating left and right via a 2-column grid with directional slide-in. Nodes pin to a gradient line; today is a green card with a pulsing node. Each card stacks date, weekday and a dotted event list; collapses to a single rail on mobile.
Published
The code
<div class="cal25">
<div class="cal25__wrap">
<div class="cal25__head">
<div class="cal25__title">June <b>2026</b></div>
<div class="cal25__sub">Timeline · this week</div>
</div>
<div class="cal25__stream" id="cal25Stream">
<div class="cal25__divider"><span>Week 23</span></div>
<!-- Sun 8 — today -->
<div class="cal25__item cal25__item--left cal25__item--today">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">8</span>
<span class="cal25__date-dow">Sun · Today</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Sprint Planning · 10:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Product Launch · 14:00</span>
</div>
</div>
</div>
<!-- Mon 9 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">9</span>
<span class="cal25__date-dow">Monday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Design Review · 11:00</span>
</div>
</div>
</div>
<!-- Tue 10 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">10</span>
<span class="cal25__date-dow">Tuesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>Free day</span>
</div>
</div>
</div>
<!-- Wed 11 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">11</span>
<span class="cal25__date-dow">Wednesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Board Meeting · 09:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Team Dinner · 19:30</span>
</div>
</div>
</div>
<!-- Thu 12 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">12</span>
<span class="cal25__date-dow">Thursday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>1:1 with Sam · 15:00</span>
</div>
</div>
</div>
<!-- Fri 13 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">13</span>
<span class="cal25__date-dow">Friday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Retro · 16:00</span>
</div>
</div>
</div>
</div>
</div>
</div><div class="cal25">
<div class="cal25__wrap">
<div class="cal25__head">
<div class="cal25__title">June <b>2026</b></div>
<div class="cal25__sub">Timeline · this week</div>
</div>
<div class="cal25__stream" id="cal25Stream">
<div class="cal25__divider"><span>Week 23</span></div>
<!-- Sun 8 — today -->
<div class="cal25__item cal25__item--left cal25__item--today">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">8</span>
<span class="cal25__date-dow">Sun · Today</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Sprint Planning · 10:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Product Launch · 14:00</span>
</div>
</div>
</div>
<!-- Mon 9 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">9</span>
<span class="cal25__date-dow">Monday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Design Review · 11:00</span>
</div>
</div>
</div>
<!-- Tue 10 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">10</span>
<span class="cal25__date-dow">Tuesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>Free day</span>
</div>
</div>
</div>
<!-- Wed 11 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">11</span>
<span class="cal25__date-dow">Wednesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Board Meeting · 09:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Team Dinner · 19:30</span>
</div>
</div>
</div>
<!-- Thu 12 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">12</span>
<span class="cal25__date-dow">Thursday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>1:1 with Sam · 15:00</span>
</div>
</div>
</div>
<!-- Fri 13 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">13</span>
<span class="cal25__date-dow">Friday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Retro · 16:00</span>
</div>
</div>
</div>
</div>
</div>
</div>.cal25,
.cal25 *,
.cal25 *::before,
.cal25 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal25 ::selection {
background: #5b8a72;
color: #fff;
}
.cal25 {
--bg: #faf8f4;
--line: #d9d2c4;
--ink: #2a2620;
--ink2: #6e665a;
--ink3: #a59c8c;
--green: #5b8a72;
--terra: #c17a52;
--gold: #c9a44a;
--gold2: #b89344;
--card: #ffffff;
font-family: 'Inter', sans-serif;
background: var(--bg);
background-image: radial-gradient(circle at 50% 0%, #fffdf9, #f2ece1);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 48px 20px;
color: var(--ink);
}
@keyframes cal25-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal25-slip-l {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-slip-r {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(91,138,114,0.4);
}
50% {
box-shadow: 0 0 0 8px rgba(91,138,114,0);
}
}
.cal25__wrap {
max-width: 680px;
width: 100%;
animation: cal25-in 0.5s ease both;
}
/* ── Header ── */
.cal25__head {
text-align: center;
margin-bottom: 40px;
}
.cal25__title {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 46px;
font-weight: 400;
line-height: 1;
}
.cal25__title b {
font-style: normal;
font-weight: 600;
color: var(--green);
}
.cal25__sub {
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink3);
margin-top: 8px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VERTICAL TIMELINE — central line,
items alternate left/right via grid.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal25__stream {
position: relative;
padding: 10px 0;
}
/* the central spine */
.cal25__stream::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: linear-gradient(180deg, transparent, var(--line) 6%, var(--line) 94%, transparent);
transform: translateX(-50%);
}
.cal25__item {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
margin-bottom: 14px;
}
/* node on the spine */
.cal25__node {
position: absolute;
left: 50%;
top: 22px;
transform: translateX(-50%);
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--card);
border: 3px solid var(--ink3);
z-index: 3;
}
/* card */
.cal25__card {
background: var(--card);
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px 18px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(42,38,32,0.05);
}
.cal25__card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(42,38,32,0.1);
border-color: var(--green);
}
/* LEFT item: card in col 1, animate from left */
.cal25__item--left .cal25__card {
grid-column: 1;
margin-right: 32px;
text-align: right;
animation: cal25-slip-l 0.5s ease both;
}
/* RIGHT item: card in col 2, animate from right */
.cal25__item--right .cal25__card {
grid-column: 2;
margin-left: 32px;
animation: cal25-slip-r 0.5s ease both;
}
/* card content */
.cal25__date-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.cal25__item--left .cal25__date-row {
justify-content: flex-end;
}
.cal25__date-num {
font-family: 'Fraunces', serif;
font-size: 30px;
font-weight: 600;
line-height: 1;
color: var(--ink);
}
.cal25__date-dow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink3);
}
.cal25__events {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.cal25__item--left .cal25__events {
align-items: flex-end;
}
.cal25__ev {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--ink2);
}
.cal25__ev-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
/* Today node + card emphasis */
.cal25__item--today .cal25__node {
background: var(--green);
border-color: var(--green);
width: 18px;
height: 18px;
animation: cal25-pulse 2.5s ease-in-out infinite;
}
.cal25__item--today .cal25__card {
background: linear-gradient(135deg, var(--green), #4a7560);
border-color: transparent;
color: #fff;
}
.cal25__item--today .cal25__date-num {
color: #fff;
}
.cal25__item--today .cal25__date-dow {
color: rgba(255,255,255,0.7);
}
.cal25__item--today .cal25__ev {
color: rgba(255,255,255,0.9);
}
/* selected */
.cal25__card.selected {
border-color: var(--terra);
box-shadow: 0 0 0 2px var(--terra);
}
/* month divider chip */
.cal25__divider {
position: relative;
text-align: center;
margin: 8px 0 20px;
z-index: 3;
}
.cal25__divider span {
display: inline-block;
background: var(--bg);
padding: 4px 16px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink3);
border: 1px solid var(--line);
border-radius: 20px;
}
@media (max-width: 560px) {
.cal25__stream::before {
left: 20px;
}
.cal25__item {
grid-template-columns: 1fr;
}
.cal25__node {
left: 20px;
}
.cal25__item--left .cal25__card,
.cal25__item--right .cal25__card {
grid-column: 1;
margin-left: 44px;
margin-right: 0;
text-align: left;
animation: cal25-slip-r 0.5s ease both;
}
.cal25__item--left .cal25__date-row,
.cal25__item--left .cal25__events {
justify-content: flex-start;
align-items: flex-start;
}
.cal25__divider {
text-align: left;
padding-left: 12px;
}
}
@media (prefers-reduced-motion: reduce) {
.cal25 * {
animation: none !important;
}
}.cal25,
.cal25 *,
.cal25 *::before,
.cal25 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal25 ::selection {
background: #5b8a72;
color: #fff;
}
.cal25 {
--bg: #faf8f4;
--line: #d9d2c4;
--ink: #2a2620;
--ink2: #6e665a;
--ink3: #a59c8c;
--green: #5b8a72;
--terra: #c17a52;
--gold: #c9a44a;
--gold2: #b89344;
--card: #ffffff;
font-family: 'Inter', sans-serif;
background: var(--bg);
background-image: radial-gradient(circle at 50% 0%, #fffdf9, #f2ece1);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 48px 20px;
color: var(--ink);
}
@keyframes cal25-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal25-slip-l {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-slip-r {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(91,138,114,0.4);
}
50% {
box-shadow: 0 0 0 8px rgba(91,138,114,0);
}
}
.cal25__wrap {
max-width: 680px;
width: 100%;
animation: cal25-in 0.5s ease both;
}
/* ── Header ── */
.cal25__head {
text-align: center;
margin-bottom: 40px;
}
.cal25__title {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 46px;
font-weight: 400;
line-height: 1;
}
.cal25__title b {
font-style: normal;
font-weight: 600;
color: var(--green);
}
.cal25__sub {
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink3);
margin-top: 8px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VERTICAL TIMELINE — central line,
items alternate left/right via grid.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal25__stream {
position: relative;
padding: 10px 0;
}
/* the central spine */
.cal25__stream::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: linear-gradient(180deg, transparent, var(--line) 6%, var(--line) 94%, transparent);
transform: translateX(-50%);
}
.cal25__item {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
margin-bottom: 14px;
}
/* node on the spine */
.cal25__node {
position: absolute;
left: 50%;
top: 22px;
transform: translateX(-50%);
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--card);
border: 3px solid var(--ink3);
z-index: 3;
}
/* card */
.cal25__card {
background: var(--card);
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px 18px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(42,38,32,0.05);
}
.cal25__card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(42,38,32,0.1);
border-color: var(--green);
}
/* LEFT item: card in col 1, animate from left */
.cal25__item--left .cal25__card {
grid-column: 1;
margin-right: 32px;
text-align: right;
animation: cal25-slip-l 0.5s ease both;
}
/* RIGHT item: card in col 2, animate from right */
.cal25__item--right .cal25__card {
grid-column: 2;
margin-left: 32px;
animation: cal25-slip-r 0.5s ease both;
}
/* card content */
.cal25__date-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.cal25__item--left .cal25__date-row {
justify-content: flex-end;
}
.cal25__date-num {
font-family: 'Fraunces', serif;
font-size: 30px;
font-weight: 600;
line-height: 1;
color: var(--ink);
}
.cal25__date-dow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink3);
}
.cal25__events {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.cal25__item--left .cal25__events {
align-items: flex-end;
}
.cal25__ev {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--ink2);
}
.cal25__ev-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
/* Today node + card emphasis */
.cal25__item--today .cal25__node {
background: var(--green);
border-color: var(--green);
width: 18px;
height: 18px;
animation: cal25-pulse 2.5s ease-in-out infinite;
}
.cal25__item--today .cal25__card {
background: linear-gradient(135deg, var(--green), #4a7560);
border-color: transparent;
color: #fff;
}
.cal25__item--today .cal25__date-num {
color: #fff;
}
.cal25__item--today .cal25__date-dow {
color: rgba(255,255,255,0.7);
}
.cal25__item--today .cal25__ev {
color: rgba(255,255,255,0.9);
}
/* selected */
.cal25__card.selected {
border-color: var(--terra);
box-shadow: 0 0 0 2px var(--terra);
}
/* month divider chip */
.cal25__divider {
position: relative;
text-align: center;
margin: 8px 0 20px;
z-index: 3;
}
.cal25__divider span {
display: inline-block;
background: var(--bg);
padding: 4px 16px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink3);
border: 1px solid var(--line);
border-radius: 20px;
}
@media (max-width: 560px) {
.cal25__stream::before {
left: 20px;
}
.cal25__item {
grid-template-columns: 1fr;
}
.cal25__node {
left: 20px;
}
.cal25__item--left .cal25__card,
.cal25__item--right .cal25__card {
grid-column: 1;
margin-left: 44px;
margin-right: 0;
text-align: left;
animation: cal25-slip-r 0.5s ease both;
}
.cal25__item--left .cal25__date-row,
.cal25__item--left .cal25__events {
justify-content: flex-start;
align-items: flex-start;
}
.cal25__divider {
text-align: left;
padding-left: 12px;
}
}
@media (prefers-reduced-motion: reduce) {
.cal25 * {
animation: none !important;
}
}document.querySelectorAll('#cal25Stream .cal25__card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('#cal25Stream .cal25__card').forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});document.querySelectorAll('#cal25Stream .cal25__card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('#cal25Stream .cal25__card').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: Vertical Timeline Slipstream Calendar
Source: https://codefronts.com/components/css-calendar-designs/vertical-timeline-slipstream-calendar/
Dates cascade down a central spine, alternating left and right via a 2-column grid with directional slide-in. Nodes pin to a gradient line; today is a green card with a pulsing node. Each card stacks date, weekday and a dotted event list; collapses to a single rail on mobile.
## HTML
```html
<div class="cal25">
<div class="cal25__wrap">
<div class="cal25__head">
<div class="cal25__title">June <b>2026</b></div>
<div class="cal25__sub">Timeline · this week</div>
</div>
<div class="cal25__stream" id="cal25Stream">
<div class="cal25__divider"><span>Week 23</span></div>
<!-- Sun 8 — today -->
<div class="cal25__item cal25__item--left cal25__item--today">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">8</span>
<span class="cal25__date-dow">Sun · Today</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Sprint Planning · 10:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Product Launch · 14:00</span>
</div>
</div>
</div>
<!-- Mon 9 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">9</span>
<span class="cal25__date-dow">Monday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Design Review · 11:00</span>
</div>
</div>
</div>
<!-- Tue 10 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">10</span>
<span class="cal25__date-dow">Tuesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>Free day</span>
</div>
</div>
</div>
<!-- Wed 11 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">11</span>
<span class="cal25__date-dow">Wednesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Board Meeting · 09:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Team Dinner · 19:30</span>
</div>
</div>
</div>
<!-- Thu 12 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">12</span>
<span class="cal25__date-dow">Thursday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>1:1 with Sam · 15:00</span>
</div>
</div>
</div>
<!-- Fri 13 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">13</span>
<span class="cal25__date-dow">Friday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Retro · 16:00</span>
</div>
</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.cal25,
.cal25 *,
.cal25 *::before,
.cal25 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal25 ::selection {
background: #5b8a72;
color: #fff;
}
.cal25 {
--bg: #faf8f4;
--line: #d9d2c4;
--ink: #2a2620;
--ink2: #6e665a;
--ink3: #a59c8c;
--green: #5b8a72;
--terra: #c17a52;
--gold: #c9a44a;
--gold2: #b89344;
--card: #ffffff;
font-family: 'Inter', sans-serif;
background: var(--bg);
background-image: radial-gradient(circle at 50% 0%, #fffdf9, #f2ece1);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 48px 20px;
color: var(--ink);
}
@keyframes cal25-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal25-slip-l {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-slip-r {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(91,138,114,0.4);
}
50% {
box-shadow: 0 0 0 8px rgba(91,138,114,0);
}
}
.cal25__wrap {
max-width: 680px;
width: 100%;
animation: cal25-in 0.5s ease both;
}
/* ── Header ── */
.cal25__head {
text-align: center;
margin-bottom: 40px;
}
.cal25__title {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 46px;
font-weight: 400;
line-height: 1;
}
.cal25__title b {
font-style: normal;
font-weight: 600;
color: var(--green);
}
.cal25__sub {
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink3);
margin-top: 8px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VERTICAL TIMELINE — central line,
items alternate left/right via grid.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal25__stream {
position: relative;
padding: 10px 0;
}
/* the central spine */
.cal25__stream::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: linear-gradient(180deg, transparent, var(--line) 6%, var(--line) 94%, transparent);
transform: translateX(-50%);
}
.cal25__item {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
margin-bottom: 14px;
}
/* node on the spine */
.cal25__node {
position: absolute;
left: 50%;
top: 22px;
transform: translateX(-50%);
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--card);
border: 3px solid var(--ink3);
z-index: 3;
}
/* card */
.cal25__card {
background: var(--card);
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px 18px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(42,38,32,0.05);
}
.cal25__card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(42,38,32,0.1);
border-color: var(--green);
}
/* LEFT item: card in col 1, animate from left */
.cal25__item--left .cal25__card {
grid-column: 1;
margin-right: 32px;
text-align: right;
animation: cal25-slip-l 0.5s ease both;
}
/* RIGHT item: card in col 2, animate from right */
.cal25__item--right .cal25__card {
grid-column: 2;
margin-left: 32px;
animation: cal25-slip-r 0.5s ease both;
}
/* card content */
.cal25__date-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.cal25__item--left .cal25__date-row {
justify-content: flex-end;
}
.cal25__date-num {
font-family: 'Fraunces', serif;
font-size: 30px;
font-weight: 600;
line-height: 1;
color: var(--ink);
}
.cal25__date-dow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink3);
}
.cal25__events {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.cal25__item--left .cal25__events {
align-items: flex-end;
}
.cal25__ev {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--ink2);
}
.cal25__ev-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
/* Today node + card emphasis */
.cal25__item--today .cal25__node {
background: var(--green);
border-color: var(--green);
width: 18px;
height: 18px;
animation: cal25-pulse 2.5s ease-in-out infinite;
}
.cal25__item--today .cal25__card {
background: linear-gradient(135deg, var(--green), #4a7560);
border-color: transparent;
color: #fff;
}
.cal25__item--today .cal25__date-num {
color: #fff;
}
.cal25__item--today .cal25__date-dow {
color: rgba(255,255,255,0.7);
}
.cal25__item--today .cal25__ev {
color: rgba(255,255,255,0.9);
}
/* selected */
.cal25__card.selected {
border-color: var(--terra);
box-shadow: 0 0 0 2px var(--terra);
}
/* month divider chip */
.cal25__divider {
position: relative;
text-align: center;
margin: 8px 0 20px;
z-index: 3;
}
.cal25__divider span {
display: inline-block;
background: var(--bg);
padding: 4px 16px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink3);
border: 1px solid var(--line);
border-radius: 20px;
}
@media (max-width: 560px) {
.cal25__stream::before {
left: 20px;
}
.cal25__item {
grid-template-columns: 1fr;
}
.cal25__node {
left: 20px;
}
.cal25__item--left .cal25__card,
.cal25__item--right .cal25__card {
grid-column: 1;
margin-left: 44px;
margin-right: 0;
text-align: left;
animation: cal25-slip-r 0.5s ease both;
}
.cal25__item--left .cal25__date-row,
.cal25__item--left .cal25__events {
justify-content: flex-start;
align-items: flex-start;
}
.cal25__divider {
text-align: left;
padding-left: 12px;
}
}
@media (prefers-reduced-motion: reduce) {
.cal25 * {
animation: none !important;
}
}
```
## JavaScript
```js
document.querySelectorAll('#cal25Stream .cal25__card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('#cal25Stream .cal25__card').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: Vertical Timeline Slipstream Calendar
Source: https://codefronts.com/components/css-calendar-designs/vertical-timeline-slipstream-calendar/
Dates cascade down a central spine, alternating left and right via a 2-column grid with directional slide-in. Nodes pin to a gradient line; today is a green card with a pulsing node. Each card stacks date, weekday and a dotted event list; collapses to a single rail on mobile.
## HTML
```html
<div class="cal25">
<div class="cal25__wrap">
<div class="cal25__head">
<div class="cal25__title">June <b>2026</b></div>
<div class="cal25__sub">Timeline · this week</div>
</div>
<div class="cal25__stream" id="cal25Stream">
<div class="cal25__divider"><span>Week 23</span></div>
<!-- Sun 8 — today -->
<div class="cal25__item cal25__item--left cal25__item--today">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">8</span>
<span class="cal25__date-dow">Sun · Today</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Sprint Planning · 10:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:#fff"></span>Product Launch · 14:00</span>
</div>
</div>
</div>
<!-- Mon 9 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">9</span>
<span class="cal25__date-dow">Monday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Design Review · 11:00</span>
</div>
</div>
</div>
<!-- Tue 10 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">10</span>
<span class="cal25__date-dow">Tuesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>Free day</span>
</div>
</div>
</div>
<!-- Wed 11 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">11</span>
<span class="cal25__date-dow">Wednesday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--terra)"></span>Board Meeting · 09:00</span>
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Team Dinner · 19:30</span>
</div>
</div>
</div>
<!-- Thu 12 -->
<div class="cal25__item cal25__item--left">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">12</span>
<span class="cal25__date-dow">Thursday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--gold2)"></span>1:1 with Sam · 15:00</span>
</div>
</div>
</div>
<!-- Fri 13 -->
<div class="cal25__item cal25__item--right">
<div class="cal25__node"></div>
<div class="cal25__card">
<div class="cal25__date-row">
<span class="cal25__date-num">13</span>
<span class="cal25__date-dow">Friday</span>
</div>
<div class="cal25__events">
<span class="cal25__ev"><span class="cal25__ev-dot" style="background:var(--green)"></span>Retro · 16:00</span>
</div>
</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.cal25,
.cal25 *,
.cal25 *::before,
.cal25 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cal25 ::selection {
background: #5b8a72;
color: #fff;
}
.cal25 {
--bg: #faf8f4;
--line: #d9d2c4;
--ink: #2a2620;
--ink2: #6e665a;
--ink3: #a59c8c;
--green: #5b8a72;
--terra: #c17a52;
--gold: #c9a44a;
--gold2: #b89344;
--card: #ffffff;
font-family: 'Inter', sans-serif;
background: var(--bg);
background-image: radial-gradient(circle at 50% 0%, #fffdf9, #f2ece1);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 48px 20px;
color: var(--ink);
}
@keyframes cal25-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cal25-slip-l {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-slip-r {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes cal25-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(91,138,114,0.4);
}
50% {
box-shadow: 0 0 0 8px rgba(91,138,114,0);
}
}
.cal25__wrap {
max-width: 680px;
width: 100%;
animation: cal25-in 0.5s ease both;
}
/* ── Header ── */
.cal25__head {
text-align: center;
margin-bottom: 40px;
}
.cal25__title {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 46px;
font-weight: 400;
line-height: 1;
}
.cal25__title b {
font-style: normal;
font-weight: 600;
color: var(--green);
}
.cal25__sub {
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink3);
margin-top: 8px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VERTICAL TIMELINE — central line,
items alternate left/right via grid.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.cal25__stream {
position: relative;
padding: 10px 0;
}
/* the central spine */
.cal25__stream::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: linear-gradient(180deg, transparent, var(--line) 6%, var(--line) 94%, transparent);
transform: translateX(-50%);
}
.cal25__item {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
margin-bottom: 14px;
}
/* node on the spine */
.cal25__node {
position: absolute;
left: 50%;
top: 22px;
transform: translateX(-50%);
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--card);
border: 3px solid var(--ink3);
z-index: 3;
}
/* card */
.cal25__card {
background: var(--card);
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px 18px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(42,38,32,0.05);
}
.cal25__card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(42,38,32,0.1);
border-color: var(--green);
}
/* LEFT item: card in col 1, animate from left */
.cal25__item--left .cal25__card {
grid-column: 1;
margin-right: 32px;
text-align: right;
animation: cal25-slip-l 0.5s ease both;
}
/* RIGHT item: card in col 2, animate from right */
.cal25__item--right .cal25__card {
grid-column: 2;
margin-left: 32px;
animation: cal25-slip-r 0.5s ease both;
}
/* card content */
.cal25__date-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.cal25__item--left .cal25__date-row {
justify-content: flex-end;
}
.cal25__date-num {
font-family: 'Fraunces', serif;
font-size: 30px;
font-weight: 600;
line-height: 1;
color: var(--ink);
}
.cal25__date-dow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink3);
}
.cal25__events {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.cal25__item--left .cal25__events {
align-items: flex-end;
}
.cal25__ev {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--ink2);
}
.cal25__ev-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
/* Today node + card emphasis */
.cal25__item--today .cal25__node {
background: var(--green);
border-color: var(--green);
width: 18px;
height: 18px;
animation: cal25-pulse 2.5s ease-in-out infinite;
}
.cal25__item--today .cal25__card {
background: linear-gradient(135deg, var(--green), #4a7560);
border-color: transparent;
color: #fff;
}
.cal25__item--today .cal25__date-num {
color: #fff;
}
.cal25__item--today .cal25__date-dow {
color: rgba(255,255,255,0.7);
}
.cal25__item--today .cal25__ev {
color: rgba(255,255,255,0.9);
}
/* selected */
.cal25__card.selected {
border-color: var(--terra);
box-shadow: 0 0 0 2px var(--terra);
}
/* month divider chip */
.cal25__divider {
position: relative;
text-align: center;
margin: 8px 0 20px;
z-index: 3;
}
.cal25__divider span {
display: inline-block;
background: var(--bg);
padding: 4px 16px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink3);
border: 1px solid var(--line);
border-radius: 20px;
}
@media (max-width: 560px) {
.cal25__stream::before {
left: 20px;
}
.cal25__item {
grid-template-columns: 1fr;
}
.cal25__node {
left: 20px;
}
.cal25__item--left .cal25__card,
.cal25__item--right .cal25__card {
grid-column: 1;
margin-left: 44px;
margin-right: 0;
text-align: left;
animation: cal25-slip-r 0.5s ease both;
}
.cal25__item--left .cal25__date-row,
.cal25__item--left .cal25__events {
justify-content: flex-start;
align-items: flex-start;
}
.cal25__divider {
text-align: left;
padding-left: 12px;
}
}
@media (prefers-reduced-motion: reduce) {
.cal25 * {
animation: none !important;
}
}
```
## JavaScript
```js
document.querySelectorAll('#cal25Stream .cal25__card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('#cal25Stream .cal25__card').forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
});
});
```How this works
Dates cascade down a central spine, alternating left and right. A 2-column CSS Grid holds the events, with a central spine drawn via ::before on the wrapper (a fixed-width vertical bar with a gradient fill).
Events on the left get text-align:right and margin-right; events on the right get text-align:left and margin-left. Each event's dot pins to the spine at its milestone.
Directional slide-in via @keyframes translateX on scroll (with animation-timeline:view() for scroll-driven CSS or IntersectionObserver for cross-browser JS). Nodes pulse subtly when they reach viewport centre.
Make it yours
- Rebrand the spine gradient via
--spine-a,--spine-b. - Change alternation from left-right to left-only or right-only for a single-column variant.
- Adjust spine thickness (default 2px).
- Add per-event category colours on the milestone dots.
- For roadmap use, add status pills (Shipped / In Progress / Planned) inside each event card.
Gotchas — read before shipping
- Vertical timelines must use semantic
<ol>or<ul>— not<div>soup — for screen reader parity. - Include ISO dates in
<time datetime="YYYY-MM-DD">so search engines and AI assistants parse the timeline. - The alternating left/right layout MUST collapse to a single left-aligned column on mobile — a 2-column timeline is unreadable under 640px.
- Scroll-driven animations via
animation-timeline:view()require Chrome 115+; use IntersectionObserver as universal fallback. - Node pulse must respect
prefers-reduced-motion:reduce.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 57+ | 10.1+ | 52+ | 57+ |
CSS Grid, pseudo-elements, and IntersectionObserver are universally supported. Scroll-driven CSS via animation-timeline is Chrome 115+, still landing in other browsers.