CSS Tutorial

CSS Project: Timeline

A vertical timeline of events with a center line, dots, and alternating cards.

What you will build

A vertical history of Harbor studio: four seasons from the first press to the cafe door that opened next to it. A line runs down the center. Each event is a card. Dots sit on the line. On a wide window the cards alternate left and right. On a narrow one they stack on the right so nothing collides.

The line and the dots are CSS, not extra images. position and a::before pseudo-element draw the spine. Grid places the cards.

Open an example with Try it in CSS. That loads /css/try — a live page preview.

Properties you will use

PropertyJob on this page
position: relative / absolutePins the spine and the dots
::beforeDraws the center line and each event dot
display: gridTwo columns that alternate the cards
grid-columnSends odd events left and even events right
@mediaCollapses to one column on a phone

The list is still an ol. CSS paints the timeline. Do not fake order with randomdivs if the dates have a sequence.

Build in slices

First one event card and a vertical line. The list item is relative. The line is an absolute::before on the list.

Example

<style>
  body { font-family: Georgia, serif; background: #e0f2fe; color: #0c4a6e; margin: 1.5rem; }
  .timeline {
    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .timeline::before {
    content: "";
    position: absolute;
    left: 1rem;
    top: 0;
    bottom: 0;
    width: 3px;
    background: #0284c7;
  }
  .event {
    position: relative;
    margin: 0 0 1.25rem 2.4rem;
    background: #fff;
    border-radius: 0.85rem;
    padding: 0.9rem 1rem;
    box-shadow: 0 8px 18px rgba(12, 74, 110, 0.1);
  }
  .event::before {
    content: "";
    position: absolute;
    left: -1.72rem;
    top: 1.1rem;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
    background: #fff;
    border: 3px solid #0284c7;
  }
  time { display: block; color: #0284c7; font-weight: 700; font-size: 0.9rem; }
</style>
<ol class="timeline">
  <li class="event">
    <time datetime="2019">2019</time>
    <h2>First press</h2>
    <p>Harbor studio rented the chart-room loft and ran menus for the cafe downstairs.</p>
  </li>
</ol>

Then, on a wide window, switch to two columns so cards alternate. Odd items go left. Even items go right. The spine moves to the center.

Example

<style>
  @media (min-width: 44rem) {
    .timeline::before { left: 50%; transform: translateX(-1px); }
    .event {
      width: calc(50% - 2rem);
      margin: 0 0 1.4rem;
    }
    .event:nth-child(odd) { margin-right: auto; }
    .event:nth-child(even) { margin-left: auto; }
    .event:nth-child(odd)::before { left: auto; right: -2.05rem; }
    .event:nth-child(even)::before { left: -2.05rem; }
  }
</style>

Widen and narrow the preview at /css/try. Dots should stay on the line. If a card covers the spine, the offset on ::before is wrong.

Complete page

Four dated events. The complete stylesheet uses a single-column spine on small screens and the alternating layout from 44 rem up. Copy the whole document into the CSS editor.

Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Harbor studio — from press to cafe</title>
  <style>
    * { box-sizing: border-box; }
    body {
      margin: 0;
      background: #e0f2fe;
      color: #0c4a6e;
      font-family: Georgia, "Times New Roman", serif;
      padding: 2rem 1.25rem 2.5rem;
    }
    h1 { text-align: center; margin: 0 0 0.35rem; }
    .lead { text-align: center; color: #075985; margin: 0 0 1.8rem; }
    .timeline {
      position: relative;
      list-style: none;
      margin: 0 auto;
      padding: 0;
      max-width: 52rem;
    }
    .timeline::before {
      content: "";
      position: absolute;
      left: 1rem;
      top: 0.4rem;
      bottom: 0.4rem;
      width: 3px;
      background: #0284c7;
    }
    .event {
      position: relative;
      margin: 0 0 1.3rem 2.5rem;
      background: #fff;
      border-radius: 0.9rem;
      padding: 0.95rem 1.05rem 1.05rem;
      box-shadow: 0 10px 22px rgba(12, 74, 110, 0.12);
    }
    .event::before {
      content: "";
      position: absolute;
      left: -1.78rem;
      top: 1.15rem;
      width: 0.85rem;
      height: 0.85rem;
      border-radius: 50%;
      background: #fff;
      border: 3px solid #0284c7;
    }
    time { display: block; color: #0284c7; font-weight: 700; font-size: 0.88rem; }
    h2 { margin: 0.15rem 0 0.35rem; font-size: 1.15rem; }
    p { margin: 0; color: #075985; line-height: 1.45; }
    @media (min-width: 44rem) {
      .timeline::before {
        left: 50%;
        transform: translateX(-1.5px);
      }
      .event {
        width: calc(50% - 2.1rem);
        margin: 0 0 1.5rem;
      }
      .event:nth-child(odd) { margin-right: auto; }
      .event:nth-child(even) { margin-left: auto; }
      .event:nth-child(odd)::before {
        left: auto;
        right: -2.15rem;
      }
      .event:nth-child(even)::before { left: -2.15rem; }
    }
  </style>
</head>
<body>
  <h1>From press to cafe door</h1>
  <p class="lead">Four seasons at Harbor studio. The line is CSS. The dates are the story.</p>
  <ol class="timeline">
    <li class="event">
      <time datetime="2019">2019</time>
      <h2>First press</h2>
      <p>The chart-room loft took a small press. Menus for the cafe downstairs were the first paid work.</p>
    </li>
    <li class="event">
      <time datetime="2021">2021</time>
      <h2>Open mornings</h2>
      <p>Harbor studio unlocked at 8:00 so bakers could proof dough while prints dried on the line.</p>
    </li>
    <li class="event">
      <time datetime="2023">2023</time>
      <h2>Workshop passes</h2>
      <p>Weekday, Harbor, and Season seats went on the board. The cafe kept a tab for studio members.</p>
    </li>
    <li class="event">
      <time datetime="2025">2025</time>
      <h2>Shared door</h2>
      <p>A second door opened onto Pier 2. Visitors step into the studio or the cafe without crossing the street.</p>
    </li>
  </ol>
</body>
</html>

Practice tasks

  1. Add a fifth event for 2026. Keep the alternating pattern on wide windows.
  2. Change the spine color to #0c4a6e and the dots to #38bdf8.
  3. Lower the breakpoint to 36rem and check the stack at /css/try.

FAQ: CSS Project: Timeline

Common questions about this page.

What is the StudyGrid CSS tutorial?

The StudyGrid CSS tutorial is a full beginner track: selectors, the box model, text, layout, flex, grid, and responsive design. Each chapter has copy-and-preview examples.

Should I run css timeline project examples locally for better learning?

Yes. Use the browser editor on StudyGrid for a quick check, then Download the example and run it on your computer. Local runs show real errors and the real toolchain, which is one of the fastest ways to learn css timeline project in this CSS CSS lesson (CSS Project: Timeline).

Is the CSS editor the same as Try HTML?

No. Try CSS is a stylesheet preview at /css/try. Try HTML stays at /html/try. CSS lessons never open the HTML-only editor or the Python editor.

Do I need to install anything to learn CSS?

No. Open a chapter, click Try it in CSS, and the page preview updates in the browser.

Where should I start the CSS tutorial?

Start at CSS Intro, then Syntax, Selectors, and How To. After colors and the box model, continue to flex, grid, and media queries. Use Next at the bottom of each chapter.

Is the CSS tutorial free?

Yes. The CSS studio on StudyGrid (studygrid.in) is free: dashboard, chapters, and the live preview editor.