← Back toCSS grid

Create a vertical timeline

Written byPhuoc Nguyen
25 Dec, 2023
vertical timeline
Here's what we'll cover:
  • Discover how to position an item precisely using the `grid-column` property.
  • Learn how to define the gap between columns with the `grid-column-gap` property.
  • Learn how to use the `grid-auto-flow` property.

A timeline is a visual representation of events that took place over time. It's a powerful tool for displaying information chronologically, making it easier for viewers to understand the sequence of events. Timelines are versatile and can be used in fields such as history, science, project management, and personal development.
In history, timelines are frequently used to show the progression of significant events or periods. They can also showcase important figures and their contributions during specific time periods.
In science, timelines can illustrate the evolution of species or the development of scientific theories.
In project management, timelines are essential tools for planning and tracking progress. They help teams visualize tasks and deadlines, ensuring that everyone is on the same page.
For personal development, timelines can track progress towards goals or milestones. They provide a clear picture of what has been accomplished and what still needs to be done.
In this post, we'll go through the steps to create a vertical timeline using CSS grid.

Creating the HTML structure for the timeline

To build a timeline, we first need to structure the content of the timeline using HTML. In our example, we'll start by creating a `div` element with a class of `timeline`. This `div` will serve as the container for all our timeline items.
<div class="timeline">
<!-- An item -->
<div class="timeline__date">...</div>
<div class="timeline__separator"></div>
<div class="timeline__content">
<div class="timeline__title">...</div>
<div class="timeline__description">...</div>

<!-- Repeat other items ... -->
Each item will be structured using three additional `div` elements: one for the date or time of the event, one as a separator, and one for the content related to that event. The content `div` has two child `div`s for the title and description. You can customize this structure as per your requirements.
With this structure, we can easily style each item using CSS grid properties, which we'll cover in detail in the next section.

Styling the timeline with CSS grid

To make our timeline look great, we'll use CSS grid properties. First, we set the container to `display: grid`, which turns it into a CSS grid container. Then, we define the gap between columns with the `grid-column-gap` property and set it to `1rem`. This creates a nice one-rem gap between each column.
Next, we define the columns using the `grid-template-columns` property. In our example, we have three columns. The first column is for the date or time of the event and is set to a fixed width of `5rem`. The second column is used as a separator between items and has a width of only `0.125rem`. The third column takes up the remaining space and is used for the content related to that event.
.timeline {
display: grid;
grid-column-gap: 1rem;
grid-template-columns: 5rem 0.125rem 1fr;
To create a vertical separator, all we need to do is set the `background` property to the color of our choice. In our example, we've gone with a light shade of gray (`rgb(203 213 225)`) to create a visible separation between each item on the timeline. If you'd like, you can play around with different colors and shades to match your design preferences.
.timeline__separator {
background: rgb(203 213 225);
To connect the date of each item with its corresponding content, we can use a dot as a visual aid. First, we set the position of the separator element to `relative`. Then, we add a `::before` pseudo-element to create the dot connector. We set the `content` property to an empty string to create a content-less element that only serves as a visual aid.
Next, we set its position to `absolute`, which takes it out of the normal flow of elements and allows us to position it with respect to its closest positioned ancestor (in this case, the separator element). We then position the dot connector by setting its top value to `0.25rem`, which is half of our desired distance from the top edge of the separator element.
Finally, we center it horizontally by setting its left value to 50% and translating it back by half its own width using the `transform: translateX(-50%)` property-value pair.
.timeline__separator {
position: relative;
.timeline__separator::before {
content: '';
position: absolute;
top: 0.25rem;
left: 50%;
transform: translateX(-50%);
To create a circle shape for the dot connector, we just need to add some additional CSS properties to the `::before` pseudo-element. First, we set the `border-radius` property to `50%`, which makes it a perfect circle. Then we set the `height` and `width` properties to the same value, like `1rem`, to create a uniform circular shape. This simple tweak transforms our dot connector into a visually appealing and informative element that effectively connects each date or time with its corresponding content.
.timeline__separator::before {
background: inherit;
border-radius: 50%;
height: 1rem;
width: 1rem;
Here's what the timeline looks like:
The content featured in the timeline is from The History of the Web

Reversing date and content in alternate rows

Let's mix things up a bit and create a new version of our timeline where the date and content are swapped in every other row. The date and content will have the same width, and we'll place a separator in the middle.
To achieve this new layout, we'll make some changes to the `.timeline` class. We'll still use `display: grid` to create a CSS grid container, but this time we'll adjust the `grid-template-columns` property to have two equal columns of `1fr`. This will give us an even amount of space on either side of our separator element.
.timeline {
grid-template-columns: 1fr 0.125rem 1fr;
Next, let's get into positioning the elements of each item explicitly. Since the separators are always placed in the middle, we can use the `grid-column` property to make it happen. This property specifies where an item should start and end within a grid row. In our case, we set the separator element to span across one column by using `grid-column: 2;`. This ensures that it is always positioned in the middle column of our three-column grid, effectively separating each date and content div.
.timeline__separator {
grid-column: 2;
We're going to use the same approach to position the date on our timeline. By default, it shows up in the first column. But we want to switch things up and move it to the third column in every other row.
To make this happen, we use the `nth-child(6n+4)` selector in the `.timeline__date` class. This selector targets every 6th item starting from the 4th item, which are the date divs we want to swap with their corresponding content divs. By setting the `grid-column` property of these selected date divs to 3, we position them on the right side of the timeline, swapping their position with their corresponding content divs.
.timeline__date {
grid-column: 1;
.timeline__date:nth-child(6n+4) {
grid-column: 3;
Similarly, to make the content span across the third column of our three-column grid, we use `grid-column: 3`. However, in alternate rows where we swap the position of the date and content divs, we set it to span across the first column by using `grid-column: 1`.
We can achieve this effect by using the `nth-child(6n)` selector in the `.timeline__content` class. This selector targets every sixth item, starting from the first item, and sets its `grid-column` property to 1. This ensures that in alternating rows, where the content div is positioned on the left side of the separator element, it spans across only one column instead of three.
.timeline__content {
grid-column: 3;
.timeline__content:nth-child(6n) {
grid-column: 1;
We can easily customize the position of timeline items according to our design preferences by using the `nth-child` selector along with other CSS properties.
Here's how the timeline layout looks now:
Although we've properly positioned the items in our grid container, the layout still seems to be broken. Specifically, in alternate rows, the separators don't take up the full height of the corresponding item. However, we can fix this issue by using the `grid-auto-flow` property.
The `grid-auto-flow` property controls how items are placed in a grid container when there isn't enough space to fit them all. By default, it's set to `row`, which places items in rows from left to right and top to bottom.
But we can change this behavior by setting `grid-auto-flow` to `column`. This will cause items to flow into columns from top to bottom and left to right, instead of rows. This is great for creating a vertical timeline with multiple columns of events.
Another value we can use for the `grid-auto-flow` property is `dense`. When set to `dense`, the grid algorithm will attempt to fill in gaps that are created when items have different heights or widths. It does this by placing smaller items in available spaces between larger ones, instead of leaving those spaces empty. This can result in a more compact and efficient use of space in our grid layout.
.timeline {
grid-auto-flow: dense;
Check out the modified timeline below:


To sum up, we've just covered how to create a vertical timeline using CSS grid properties. By defining columns and gaps, we can style our timeline to our liking and add visual elements like dot connectors and background colors.
But that's not all. We also explored an alternate layout where the date and content divs are swapped in alternating rows. To make this work, we used the `grid-column` property to position each item correctly and the `grid-auto-flow` property to fill in gaps.
CSS grid is a powerful tool for creating complex layouts with ease. Armed with these techniques, you can now craft stunning and informative timelines that are visually appealing.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn