Home / Snippets / UI Components /

Timeline

Vertical timeline with connected dots and alternating content cards. Pure CSS using border and pseudo-elements.

  • March 2026

    Project kickoff

    Initial design approved. Repo created and team onboarded to new workflow.

  • April 2026

    Alpha release

    Core features shipped. Internal testing revealed three critical layout bugs — all fixed within 48 hours.

  • May 2026

    Public beta

    Opened to 500 early adopters. Feedback collected via in-app survey.

  • June 2026

    v1.0 launch

    Full public launch with documentation site and onboarding flow.

Widely Supported
uino-js

Quick implementation

/* HTML:
   <ul class="tl">
     <li class="tl-item">
       <p class="tl-date">March 2026</p>
       <div class="tl-card">
         <h3>Event title</h3>
         <p>Event description.</p>
       </div>
     </li>
   </ul>
*/

.tl {
  position: relative;
  padding-left: 2rem;
  list-style: none;
  margin: 0;
}

/* The vertical connecting line */
.tl::before {
  content: '';
  position: absolute;
  left: 0.5625rem;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 2px;
  background: oklch(0.35 0.05 265);
}

.tl-item {
  position: relative;
  padding-bottom: 1.75rem;
}

.tl-item:last-child { padding-bottom: 0; }

/* The dot for each event */
.tl-item::before {
  content: '';
  position: absolute;
  left: -1.625rem;
  top: 0.35rem;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: oklch(0.52 0.22 265);
  border: 2px solid oklch(0.13 0.02 260);
  box-shadow: 0 0 0 2px oklch(0.52 0.22 265 / 0.4);
}

.tl-date {
  font-size: 0.75rem;
  font-weight: 600;
  color: oklch(0.72 0.19 265);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.25rem;
}

.tl-card {
  background: oklch(0.19 0.02 260);
  border: 1px solid oklch(0.28 0.03 260);
  border-radius: 0.5rem;
  padding: 0.875rem 1rem;
}

.tl-card h3 {
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0 0 0.25rem;
}

.tl-card p {
  font-size: 0.85rem;
  color: oklch(0.63 0.02 260);
  margin: 0;
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building accessible layout components.

Goal: A vertical timeline component with a continuous connecting line, dot markers per event, date labels, and card-style content — pure CSS, no JavaScript.

Technical constraints:
- The connecting line is drawn with a ::before pseudo-element on the list container, absolutely positioned between the first and last dot.
- Each item's dot is its own ::before, sized, border-radius: 50%, with a double-ring glow via box-shadow.
- Use padding-left on the list and negative left offsets on pseudo-elements to align dots on the line precisely.
- Card backgrounds use oklch() at low lightness (around 0.19) with a border 1 step lighter.
- Date labels use uppercase letter-spacing and a distinct accent hue.
- All colors use oklch() — no hex, no hsl().

Framework variant (pick one):
A) Vanilla HTML + CSS — semantic <ul>/<li> list with BEM modifier classes.
B) React component — accept an array of event objects (date, title, description) and render the timeline dynamically.

Edge cases to handle:
- Single event: the vertical line still renders cleanly with bottom: 0.5rem stopping it short.
- Very long descriptions: card must expand vertically without overflowing the dot.
- Right-to-left layouts: mirror the left offsets using logical properties (padding-inline-start, inset-inline-start).
- Reduced motion: if the timeline animates items in on scroll, wrap in @media (prefers-reduced-motion: reduce).

Return HTML + CSS.

Why this matters in 2026

Timelines communicate sequence and progress — project histories, changelogs, event feeds, user activity streams. The temptation is to reach for a charting library, but the entire visual structure is achievable with a <ul>, two ::before pseudo-elements, and absolute positioning. No SVG, no canvas, no JavaScript. That means it's server-renderable, accessible by default, and adds zero runtime to your page.

The logic

The vertical line is a single 2px-wide ::before on the list container, pinned with position: absolute. Its top and bottom are inset slightly so the line terminates near the first and last dot rather than bleeding out of the container. Each .tl-item::before is the dot — a small circle absolutely positioned left of the card, offset to sit centered on the line. The double-ring glow effect uses a box-shadow with a transparent inner ring (matching the page background) and a colored outer ring, creating visual separation without extra markup. The date label sits above the card, outside the card's background, so it reads as metadata rather than content.

Accessibility & performance

Using a semantic <ul>/<li> structure means screen readers announce the list count and each item in sequence, matching the visual order. Dates as <p> elements sit in reading order before the card title, so the audio experience matches the visual one. Heading levels inside cards (<h3>) should follow the document outline — if the page uses <h2> for section headings, <h3> here is correct. The layout uses only position, padding, and border — no animated properties — so there is no compositor or paint cost at all.