35 / 36
All 36 in 36 Tailwind Stacked Cards
- 01Tailwind Scroll-Driven Scaling Card Stack
- 02Tailwind Swipe Card Deck Drag to Dismiss
- 03Tailwind Glassmorphism Testimonial Card Stack
- 04Tailwind Stacked Cards Expand Accordion on Click
- 05Tailwind Fan Out Photo Stack on Hover
- 06Tailwind 3D Perspective Tilt Card Stack
- 07Tailwind Multi Step Form Card Stack
- 08Tailwind Tabbed Card Stack Bring to Front
- 09Tailwind Click to Front Stacked Cards Cycle
- 10Tailwind Classic Card Deck Stack Shadow
- 11Tailwind Fan Spread Hand of Cards
- 12Tailwind Polaroid Photo Stack Scattered Rotate
- 13Tailwind Staircase Offset Stacked Cards Cascade
- 14Tailwind Cards Spread Out Horizontally on Hover
- 15Tailwind Stacked Cards Lift on Hover Sibling Push
- 16Tailwind Accordion Stacked Cards Slide Out Menu
- 17Tailwind Shuffle Cards Animation Loop Deck
- 18Tailwind File Folder Tab Stacked Cards
- 19Tailwind Scattered Photos Pinch to Centre on Hover
- 20Tailwind Mobile Swipe Card Stack Translate3d
- 21Tailwind Single Accordion Card Nested Layers
- 22Tailwind Isometric Tower Stacked Cards 3D Column
- 23Tailwind 3D Flip Card Stack Reveal Back
- 24Tailwind Card Stack Tilt on Hover Quadrant
- 25Tailwind Notification Stack Pile Dismiss
- 26Tailwind Receipt Roll Unroll Stacked Items
- 27Tailwind Scratch Card Reveal Canvas Mask
- 28Tailwind Stacked Cards Hover Reveal Spread
- 29Tailwind Scroll Activated Sticky Stacking Cards
- 30Tailwind Stacked Testimonial Slider Radio
- 31Tailwind Isometric Overlapping Stacked Cards
- 32Tailwind Bento Grid Stacked Cards Hover Fan
- 33Tailwind Perspective Card Deck Depth Tunnel
- 34Tailwind Peel Back Corner Card Reveal
- 35Tailwind Timeline Stacked Milestone Cards
- 36Tailwind Spiral Tower Rotating Stacked Cards
Tailwind Timeline Stacked Milestone Cards
A gradient-spine timeline whose cards slide right and whose nodes bloom on hover. The port's contribution is semantic rather than visual: the div soup becomes a real <ol> with <time datetime> elements, and both the spine and the milestone nodes become before: utilities on the elements they belong to instead of two floating pseudo-element rules.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-35 grid w-full min-h-svh place-items-center px-5 py-15 font-stk35-sans bg-linear-[180deg,var(--color-stk35-p1),var(--color-stk35-p2)]" aria-label="Company timeline">
<ol class="relative m-0 w-[min(340px,92vw)] list-none pl-10 before:absolute before:inset-y-2.5 before:left-3.5 before:w-[3px] before:rounded before:bg-linear-[180deg,var(--color-stk35-spine1),var(--color-stk35-spine2)]">
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2021-03">2021</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">The Beginning</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">First prototype shipped to ten beta users.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2023-06">2023</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Going Public</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Launched the open platform to everyone.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2025-01">2025</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Scale</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Crossed one million active accounts.</p>
</div>
</li>
<li class="group relative before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine2 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2026-09">2026</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">What’s Next</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Reimagining the experience from scratch.</p>
</div>
</li>
</ol>
</section><section class="stk-35 grid w-full min-h-svh place-items-center px-5 py-15 font-stk35-sans bg-linear-[180deg,var(--color-stk35-p1),var(--color-stk35-p2)]" aria-label="Company timeline">
<ol class="relative m-0 w-[min(340px,92vw)] list-none pl-10 before:absolute before:inset-y-2.5 before:left-3.5 before:w-[3px] before:rounded before:bg-linear-[180deg,var(--color-stk35-spine1),var(--color-stk35-spine2)]">
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2021-03">2021</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">The Beginning</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">First prototype shipped to ten beta users.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2023-06">2023</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Going Public</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Launched the open platform to everyone.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2025-01">2025</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Scale</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Crossed one million active accounts.</p>
</div>
</li>
<li class="group relative before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine2 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2026-09">2026</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">What’s Next</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Reimagining the experience from scratch.</p>
</div>
</li>
</ol>
</section>@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@700&family=Schibsted+Grotesk:wght@400;500&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@700&family=Schibsted+Grotesk:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The webfonts load as
<link>tags insetup, not as an@importinside the stylesheet. Tailwind emits user@importrules after its own@layerblocks, and a late@importis invalid CSS — the browser drops it and the demo silently falls back to system fonts. - The substantive change is semantic: an
<ol>of<li>milestones with<time datetime>, so sequence and dates are machine-readable rather than implied by styling. - Both pseudo-elements move onto their owners — the spine as a
before:chain on the list, each node as abefore:chain on its milestone. - The node's hover response is
group-hover:before:scale-130: a variant reaching a pseudo-element, which the CSS version needs a second rule for. - The looping pulse becomes a hover bloom, which is why this demo ships no
@keyframesand needs no motion off-switch for the node. list-nonekeeps the list semantics while dropping the markers — the right way to style a list, rather than changing the element.- Status and dates are carried in words and attributes rather than colour, so the timeline survives being read aloud.
Here's a working CSS demo 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: Tailwind Timeline Stacked Milestone Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-timeline-stacked-milestone-cards/
A gradient-spine timeline whose cards slide right and whose nodes bloom on hover. The port's contribution is semantic rather than visual: the div soup becomes a real <ol> with <time datetime> elements, and both the spine and the milestone nodes become before: utilities on the elements they belong to instead of two floating pseudo-element rules.
## HTML
```html
<section class="stk-35 grid w-full min-h-svh place-items-center px-5 py-15 font-stk35-sans bg-linear-[180deg,var(--color-stk35-p1),var(--color-stk35-p2)]" aria-label="Company timeline">
<ol class="relative m-0 w-[min(340px,92vw)] list-none pl-10 before:absolute before:inset-y-2.5 before:left-3.5 before:w-[3px] before:rounded before:bg-linear-[180deg,var(--color-stk35-spine1),var(--color-stk35-spine2)]">
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2021-03">2021</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">The Beginning</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">First prototype shipped to ten beta users.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2023-06">2023</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Going Public</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Launched the open platform to everyone.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2025-01">2025</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Scale</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Crossed one million active accounts.</p>
</div>
</li>
<li class="group relative before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine2 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2026-09">2026</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">What’s Next</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Reimagining the experience from scratch.</p>
</div>
</li>
</ol>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```Here's a working CSS demo 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: Tailwind Timeline Stacked Milestone Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-timeline-stacked-milestone-cards/
A gradient-spine timeline whose cards slide right and whose nodes bloom on hover. The port's contribution is semantic rather than visual: the div soup becomes a real <ol> with <time datetime> elements, and both the spine and the milestone nodes become before: utilities on the elements they belong to instead of two floating pseudo-element rules.
## HTML
```html
<section class="stk-35 grid w-full min-h-svh place-items-center px-5 py-15 font-stk35-sans bg-linear-[180deg,var(--color-stk35-p1),var(--color-stk35-p2)]" aria-label="Company timeline">
<ol class="relative m-0 w-[min(340px,92vw)] list-none pl-10 before:absolute before:inset-y-2.5 before:left-3.5 before:w-[3px] before:rounded before:bg-linear-[180deg,var(--color-stk35-spine1),var(--color-stk35-spine2)]">
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2021-03">2021</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">The Beginning</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">First prototype shipped to ten beta users.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2023-06">2023</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Going Public</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Launched the open platform to everyone.</p>
</div>
</li>
<li class="group relative mb-3.5 before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine1 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2025-01">2025</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">Scale</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Crossed one million active accounts.</p>
</div>
</li>
<li class="group relative before:absolute before:-left-[33px] before:top-4.5 before:size-4 before:rounded-full before:border-[3px] before:border-stk35-spine2 before:bg-white before:transition-[transform,background-color] before:duration-350 hover:before:scale-130 hover:before:bg-stk35-accent motion-reduce:before:transition-none">
<div class="cursor-pointer rounded-[14px] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(120,60,80,0.12)] transition-[transform,box-shadow] duration-350 ease-stk35 group-hover:translate-x-3 group-hover:shadow-[0_14px_30px_rgba(120,60,80,0.2)] motion-reduce:transition-none motion-reduce:translate-none!">
<time class="font-stk35-display text-[0.8rem] tracking-[0.08em] text-stk35-accent" datetime="2026-09">2026</time>
<h3 class="m-0 mt-0.5 mb-1 font-stk35-display text-[1.1rem] text-stk35-ink">What’s Next</h3>
<p class="m-0 text-[0.85rem] text-stk35-body">Reimagining the experience from scratch.</p>
</div>
</li>
</ol>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk35-p1: #fffaf0;
--color-stk35-p1: oklch(0.98 0.014 90);
--color-stk35-p2: #f0e8dc;
--color-stk35-p2: oklch(0.93 0.016 80);
--color-stk35-spine1: #ff8a5c;
--color-stk35-spine1: oklch(0.74 0.145 45);
--color-stk35-spine2: #a83279;
--color-stk35-spine2: oklch(0.47 0.170 350);
--color-stk35-accent: #a83279;
--color-stk35-accent: oklch(0.47 0.170 350);
--color-stk35-ink: #2a2024;
--color-stk35-ink: oklch(0.26 0.014 20);
--color-stk35-body: #6a5a60;
--color-stk35-body: oklch(0.50 0.018 10);
--font-stk35-sans: 'Schibsted Grotesk', system-ui, sans-serif;
--font-stk35-display: 'Bricolage Grotesque', system-ui, sans-serif;
--ease-stk35: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```How this works
The markup change is the real port. A timeline is an ordered list, and saying so gets you sequence for free in the accessibility tree, plus dates search engines and assistants can parse:
<ol class="list-none relative pl-10 before:absolute …">
<li class="group relative mb-3.5 before:absolute …">
<time datetime="2021-03-01">2021</time>The visual result is identical; what changes is that the structure is now true.
Both pseudo-elements move into the markup. The spine belongs to the list and the node belongs to the milestone, and as before: chains each one sits on its owner. The node's hover response — group-hover:before:scale-130 group-hover:before:bg-stk35-accent — is then two utilities next to the element it decorates rather than a second rule keyed on a class name.
A pulse is a loop; a bloom is a response. The CSS version reaches for a pulsing @keyframes. A scale-and-fill on hover conveys the same focus without any autoplaying animation to suppress under reduced motion — which is why this demo needs no keyframes at all.
Short entries, by design. Timelines are scanned, not read. Keeping each milestone under about forty words is a content constraint that the layout should enforce rather than fight.
Make it yours
- Rebrand the spine through the two gradient tokens; the node border and hover fill read the same accent.
- Change the node shape by swapping
before:rounded-fullfor a square or a rotated diamond. - Set the slide distance with
group-hover:translate-x-3. - Add status pills (Shipped / In progress / Planned) inside each card, with the status in words as well as colour.
- Collapse to full-width cards below
sm:by reducing the list's left padding and the node offset.
Gotchas — read before shipping
- Use
<ol>and<time datetime>. A timeline built from divs loses its sequence in the accessibility tree and its dates to any parser. - Keep
list-nonerather than changing the element — you want the semantics without the markers. - The node is positioned relative to its
<li>, so the list's left padding and the node's negative offset have to stay in step. - Prefer a hover bloom to a looping pulse; a loop needs a reduced-motion off-switch and adds nothing here.
- Never encode status in colour alone — put the word in the card.
- Keep entries under about forty words; a timeline that has to be read line by line is a list in disguise.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Flex, gradients and pseudo-elements are universal (Chrome 45+, Safari 9+, Firefox 16+). The floor is Tailwind v4's own generated sheet.