Learn how to create a modern vertical timeline using HTML and CSS with scroll animations and clean design. Step-by-step guide for beginners.
Table of Contents
Want to display your project journey, company history, or roadmap on your website? A vertical timeline is a great way to show events in a clean and attractive style.
In this guide, you will learn how to create a modern vertical timeline using just HTML and CSS. We'll use simple code, CSS variables, and scroll animations to make it eye-catching yet beginner-friendly.
Prerequisites
Before starting, make sure you know the basics of:
- HTML structure (elements, divs, headings)
- CSS (selectors, positioning, colors)
- Using a code editor like VS Code
Let’s begin!
Source Code
Step 1 (HTML Code):
To get started, we first need to create a basic HTML file. In this file, we will include the main structure for our timeline. Let's break down the HTML code section-by-section:
<!DOCTYPE html>
Defines the document type and version of HTML (HTML5).
<html lang="en">
The root element of the HTML document with the language set to English.
<head> Section
This is where all the meta-information, title, fonts, and CSS are defined:
<meta charset="UTF-8">: Sets the character encoding to UTF-8 (supports most characters from all languages).<meta name="viewport" content="width=device-width, initial-scale=1.0">: Ensures responsive design on mobile devices.<title>: Sets the title of the webpage (displayed in the browser tab).- Google Fonts:
- Preconnects to font services for faster loading.
- Loads the "Inter" font family in various weights.
<link rel="stylesheet" href="styles.css">: Links to the external CSS file for styling.
<body>
This is the visible content part of the webpage:
<header class="page-header">
This is the intro section of the timeline:
<h1>: Main heading/title – "The Anatomy of Progress".<p>: A short intro/description paragraph.<div class="scroll-hint">: A down arrow SVG prompting users to scroll down the page.
<main class="timeline-wrapper">
This is the main container for the entire timeline:
<div class="timeline-line">
A vertical (or horizontal) line element:
<div class="timeline-line-progress">: A scroll-based progress bar over the timeline line.
Timeline Sections (<div class="timeline-section">)
Each section represents a milestone (i.e., year + event):
Each section includes:
<div class="timeline-marker">: A visual marker (circle, dot) on the timeline line.<div class="timeline-content">: The main content box beside the marker.<div class="timeline-year">: The year (e.g., 2021, 2022… Today).<h3 class="timeline-title">: Title for the milestone (e.g., "The Initial Spark").<p class="timeline-description">: Description of what happened that year.
Step 2 (CSS Code):
Once the basic HTML structure of the timeline is in place, the next step is to add styling to the timeline using CSS. Let's break down the CSS code section-by-section:
Base Styles & Variables
:root {
--bg-color: #0a0a0a;
--text-color: #d1d1d1;
--primary-color: #ffffff;
--accent-glow: rgba(74, 144, 226, 0.5);
--line-color: #222222;
--card-bg: rgba(20, 20, 20, 0.5);
--card-border: rgba(255, 255, 255, 0.1);
}
Defines CSS variables for colors and reusability, e.g. background, text, glow effect, etc.
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
Resets default margin and padding, and applies consistent box-sizing.
html {
scroll-behavior: smooth;
}
Enables smooth scrolling between anchor points.
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Inter', sans-serif;
...
}
Sets the overall look and feel, including:
- Background gradients
- Font smoothing
- Font family
Header Section Styling
.page-header
Centered header content:
- Large heading (h1)
- Description paragraph (p)
- A scroll-hint with a bouncing arrow (svg)
.page-header h1 { ... }
.page-header p { ... }
.scroll-hint { ... }
.scroll-hint svg {
animation: bounce 2s infinite ease-out;
}
bounce animation gives the scroll arrow a hinting motion.
Main Timeline Container
.timeline-wrapper
A centered container for the entire timeline.
Timeline Line and Scroll Progress
.timeline-line
A vertical line in the center of the screen using position: absolute.
.timeline-line-progress
A scroll-linked animation (animation-timeline: scroll(root)) that:
- Starts with scaleY(0) (hidden)
- Animates with @keyframes fill-line to scaleY(1)
@keyframes fill-line {
to { transform: scaleY(1); }
}
Timeline Section Cards
.timeline-section
Each block (milestone) in the timeline.
- Uses flex layout
- Alternates layout on even-numbered sections using:
.timeline-section:nth-child(even) {
justify-content: flex-end;
}
Markers on the Timeline
.timeline-marker
Small circular dots are in the center of the timeline.
- Positioned in the center of the vertical line
- Animates using light-up when in view
@keyframes light-up {
50%, 100% {
border-color: white;
background-color: white;
box-shadow: 0 0 15px 5px var(--accent-glow);
}
}
Content Cards
.timeline-content
Each card beside the timeline marker:
- Semi-transparent background
- Blurred glass effect (backdrop-filter)
- Shadow for depth
- Scroll animation: fade-in-up
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(50px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
Responsive Design
@media (max-width: 768px) { ... }
Adjustments for smaller screens:
- Line and marker shift to the left
- Cards take full width
- No alternating layout on even sections
Animation Timeline Integration
These are modern CSS features:
animation-timeline: scroll(root);
animation-timeline: view();
These tie animations to scroll position visibility — part of the CSS Scroll-Linked Animations Specification. They let animations run:
- As the user scrolls (scroll(root))
- When elements enter or leave the viewport (view())
/* --- Base Styles & Variables --- */
:root {
--bg-color: #0a0a0a;
--text-color: #d1d1d1;
--primary-color: #ffffff;
--accent-glow: rgba(74, 144, 226, 0.5);
--line-color: #222222;
--card-bg: rgba(20, 20, 20, 0.5);
--card-border: rgba(255, 255, 255, 0.1);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Inter', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-image: radial-gradient(
circle at 10% 10%,
rgba(255, 255, 255, 0.04),
transparent 30%
),
radial-gradient(circle at 90% 80%, rgba(74, 144, 226, 0.1), transparent 40%);
}
/* --- Page Header for Context --- */
.page-header {
text-align: center;
padding: 10vh 2rem 5vh;
max-width: 800px;
margin: 0 auto;
}
.page-header h1 {
font-size: clamp(2.5rem, 6vw, 4rem);
color: var(--primary-color);
font-weight: 700;
line-height: 1.1;
margin-bottom: 1rem;
letter-spacing: -0.03em;
}
.page-header p {
font-size: clamp(1rem, 2vw, 1.125rem);
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.scroll-hint {
margin-top: 3rem;
color: var(--text-color);
font-size: 0.9rem;
display: inline-flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.scroll-hint svg {
animation: bounce 2s infinite ease-out;
}
/* --- Main Timeline Container --- */
.timeline-wrapper {
position: relative;
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding: 5vh 2rem 20vh; /* Extra padding at the bottom */
}
/* --- The Central Timeline Line (with scroll-driven progress) --- */
.timeline-line {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 100%;
background-color: var(--line-color);
z-index: 1;
}
.timeline-line-progress {
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
var(--accent-glow),
var(--primary-color)
);
transform-origin: top;
transform: scaleY(0); /* Initially hidden */
/* The magic: Link animation to scroll progress */
animation: fill-line linear forwards;
animation-timeline: scroll(root);
}
/* --- Individual Timeline Section --- */
.timeline-section {
position: relative;
z-index: 2;
display: flex;
justify-content: flex-start;
padding: 4rem 0;
min-height: 300px;
}
/* Alternate layout for even sections */
.timeline-section:nth-child(even) {
justify-content: flex-end;
}
/* --- The Dot on the Timeline --- */
.timeline-marker {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 18px;
height: 18px;
background-color: var(--bg-color);
border: 2px solid var(--line-color);
border-radius: 50%;
z-index: 3;
transition: background-color 0.3s, border-color 0.3s;
/* Animate marker when it's in the middle of the screen */
animation: light-up linear forwards;
animation-timeline: view();
animation-range: contain 40% contain 60%;
}
/* --- Content Card --- */
.timeline-content {
width: calc(50% - 40px);
padding: 2rem;
background: var(--card-bg);
border-radius: 12px;
border: 1px solid var(--card-border);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
position: relative;
/* Animate content as it enters the viewport */
animation: fade-in-up linear forwards;
animation-timeline: view();
animation-range: entry 20% cover 50%;
}
.timeline-section:nth-child(even) .timeline-content {
text-align: left;
}
.timeline-year {
font-size: clamp(1rem, 2vw, 1.1rem);
font-weight: 500;
color: var(--primary-color);
margin-bottom: 0.75rem;
letter-spacing: 0.05em;
}
.timeline-title {
font-size: clamp(1.5rem, 4vw, 1.75rem);
font-weight: 700;
margin-bottom: 1rem;
color: var(--primary-color);
line-height: 1.2;
}
.timeline-description {
font-size: clamp(0.9rem, 1.5vw, 1rem);
line-height: 1.6;
font-weight: 300;
}
/* --- Keyframe Animations --- */
@keyframes fill-line {
to {
transform: scaleY(1);
}
}
@keyframes light-up {
from,
to {
border-color: var(--line-color);
background-color: var(--bg-color);
box-shadow: none;
}
50% {
border-color: var(--primary-color);
background-color: var(--primary-color);
box-shadow: 0 0 15px 5px var(--accent-glow);
}
100% {
border-color: var(--primary-color);
background-color: var(--primary-color);
box-shadow: 0 0 15px 5px var(--accent-glow);
}
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(50px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes bounce {
0%,
20%,
50%,
80%,
100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}
/* --- Responsive Design --- */
@media (max-width: 768px) {
.timeline-wrapper {
padding-left: 1rem;
padding-right: 1rem;
}
.timeline-line {
left: 20px;
transform: none;
}
.timeline-marker {
left: 20px;
transform: none;
}
.timeline-section,
.timeline-section:nth-child(even) {
justify-content: flex-start;
padding-left: 40px;
}
.timeline-content,
.timeline-section:nth-child(even) .timeline-content {
width: 100%;
text-align: left;
}
} Final Output:
Conclusion:
A vertical timeline not only looks modern but also helps users follow your story in a clean way. With just HTML and CSS, you can build a stylish and responsive timeline to show your achievements, events, or roadmap.
This simple project is great for portfolio websites, startup milestones, or project documentation. You can always expand it by adding animations, icons, or JavaScript interactivity later.
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊


