Go to homepage

Projects /

Horizontal Timeline

An easy to customize, horizontal timeline powered by CSS and JavaScript.

Horizontal Timeline
Check our new component library →

Our vertical timeline is so far one of the most popular resources on CodyHouse. Many of you asked us to include a horizontal timeline as well. Here it is!

Building a horizontal timeline was a bit tricky, because you can’t rely on the vertical (more intuitive) scrolling behaviour. We decided to separate timeline and events, with the first one working like a slider, while the second one occupying the full width and showing a single event at a time.

👋 Important: this experiment is built using the CodyHouse Framework.

Creating the structure

The HTML structure is composed by two main ordered lists: the first one containing the timeline dates and the second one the events.
An additional unordered list has been used for the navigation arrows (.cd-h-timeline__navigation elements), and a span.cd-h-timeline__filling-line to create the filling effect when a new event is selected.

<section class="cd-h-timeline js-cd-h-timeline margin-bottom-md">
   <div class="cd-h-timeline__container container">
      <div class="cd-h-timeline__dates">
         <div class="cd-h-timeline__line">
               <li><a href="#0" data-date="16/01/2014" class="cd-h-timeline__date cd-h-timeline__date--selected">16 Jan</a></li>
               <li><a href="#0" data-date="28/02/2014" class="cd-h-timeline__date">28 Feb</a></li>
               <!-- other dates here -->

            <span class="cd-h-timeline__filling-line" aria-hidden="true"></span>
         </div> <!-- .cd-h-timeline__line -->
      </div> <!-- .cd-h-timeline__dates -->

         <li><a href="#0" class="text-replace cd-h-timeline__navigation cd-h-timeline__navigation--prev cd-h-timeline__navigation--inactive">Prev</a></li>
         <li><a href="#0" class="text-replace cd-h-timeline__navigation cd-h-timeline__navigation--next">Next</a></li>
   </div> <!-- .cd-h-timeline__container -->

   <div class="cd-h-timeline__events">
         <li class="cd-h-timeline__event cd-h-timeline__event--selected text-component">
            <div class="cd-h-timeline__event-content container">
               <h2 class="cd-h-timeline__event-title">Horizontal Timeline</h2>
               <em class="cd-h-timeline__event-date">January 16th, 2014</em>
               <p class="cd-h-timeline__event-description color-contrast-medium"> 
               Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illum praesentium officia, fugit recusandae ipsa, quia velit nulla adipisci? Consequuntur aspernatur at, eaque hic repellendus sit dicta consequatur quae, ut harum ipsam molestias maxime non nisi reiciendis eligendi! Doloremque quia pariatur harum ea amet quibusdam quisquam, quae, temporibus dolores porro doloribus.

         <li class="cd-h-timeline__event text-component">
            <!-- event description here -->

         <!-- other descriptions here -->
   </div> <!-- .cd-h-timeline__events -->

Adding style & Guidelines

Let's start from the events style: all the items are translated to the left, outside the viewport (translateX(-100%)); the .cd-h-timeline__event--selected class is added to the visible event item to move it back into the viewport (translateX(0)).

4 classes have been used to create the slider animation: the .cd-h-timeline__event--enter-right/.cd-h-timeline__event--enter-left classes added to the selected event item entering the viewport from right/left, and the .cd-h-timeline__event--leave-right/.cd-h-timeline__event--leave-left classes added to the event item moving to the right/left while leaving the viewport. These classes are used to apply two different CSS animations: cd-enter-right and cd-enter-left.

.cd-h-timeline__events {
  position: relative;

.cd-h-timeline__event {
  position: absolute;
  z-index: 1;
  width: 100%;
  left: 0;
  top: 0;
  transform: translateX(-100%);
  opacity: 0;
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;

.cd-h-timeline__event--selected { // selected event info
  position: relative;
  z-index: 2;
  opacity: 1;
  transform: translateX(0);

.cd-h-timeline__event--leave-right { // animate event info
   animation-name: cd-enter-right;

.cd-h-timeline__event--leave-left { // animate event info
   animation-name: cd-enter-left;

.cd-h-timeline__event--leave-left {
   animation-direction: reverse;

@keyframes cd-enter-right {
   0% {
      opacity: 0;
      transform: translateX(100%);

   100% {
      opacity: 1;
      transform: translateX(0%);

@keyframes cd-enter-left {
   0% {
      opacity: 0;
      transform: translateX(-100%);

   100% {
      opacity: 1;
      transform: translateX(0%);

About the timeline: the position of each date along the timeline is set using Javascript. Dates are not equally distributed along the timeline, but the distance between a date and the next one is proportional to the difference between these dates.

First of all, in the main.js file, we set a minimum distance between two consecutive dates, using the eventsMinDistance property of the HorizontalTimeline object; in our case, we set eventsMinDistance = 60 (so the minimum distance will be 60px). We also set a maximum distance between events (eventsMaxDistance = 200).

We then evaluate all the differences between a date and the following one; to do that we use the data-date attribute added to each date. The minimum difference is then used as a reference to evaluate the distances between two consecutive dates.

About the date format: we used the date format DD/MM/YYYY, but you can also add time, if you need to take that into account. There are 3 different date formats you can use:

  • DD/MM/YYYY -> day only;
  • DD/MM/YYYYTHH:MM -> if you need to consider time too (e.g., 02/10/2015T19:45);
  • HH:MM -> time only (for events happening within the same day).

⚠️ Note: if you need to initilize your rimeline at a later time (e.g., your timeline is populated with a delay), remove the .js-cd-h-timeline class form the .cd-h-timeline (so that the timeline is not initialized before the content has been loaded). Once the timeline is ready, use the HorizontalTimeline object to initialize it:

new HorizontalTimeline(document.getElementByID('your-timeline-id'));

Level up your CSS skills

Each month we email a 1-minute CSS tutorial to 20K developers

Awesome! We just sent you a confirmation link by email

Error - please try again or contact us

Your email address is already subscribed

Project duplicated

Project created

Globals imported

There was an error while trying to export your project. Please try again or contact us.