31 Pure CSS1 Light JSPublished 8 new designs · Updated on
32 hand-coded CSS timelines you can copy-paste into any project — GitHub-style commit history, CI/CD deploy pipelines with conic-gradient status spinners, resume timelines with CSS subgrid, order tracking steppers, product roadmap quarters with a live TODAY line, SaaS changelogs, Gantt swimlanes, conference agenda hour grids, scrollytelling panels, and 23 more field-tested patterns for dashboards, healthcare records, DevOps postmortems, and portfolio pages. Every timeline is a semantic <ol> of <li> events with real <time datetime> elements and decoration in aria-hidden pseudo-elements; modern platform features (animation-timeline: view()/scroll(), container queries, subgrid, :has(), :target, scroll-margin) sit behind @supports so unsupported browsers get identical static content — never hidden behind an effect that can't play. Compositor-friendly transform/opacity motion keeps CLS at 0 and 31 of 32 ship Zero JavaScript; each demo themes from 3-6 CSS custom properties (--tl-accent, --tl-line, --tl-bg) so retuning takes one ruleset. Scoped under .tls-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Want to build production-ready CSS layouts, animations and UI from scratch?
You just browsed 32 timelines. Learn the Flexbox, Grid and animation thinking behind them, so the next component starts in your head, not in a copy-paste.
The developer-native vertical log: circular commit markers on a continuous rail, monospace hashes, relative timestamps and merge badges — the exact pattern for changelogs, release notes and activity feeds, built on a semantic <ol> so screen readers announce it as the numbered sequence it is.
A deployment pipeline strip in the dashboard idiom: Build → Test → Deploy steps joined by connector lines that fill with state, a conic-gradient spinner on the running step, and success/pending/failed expressed with icons AND text — never color alone — so the pipeline reads correctly for color-blind engineers and screen readers alike.
The classic career timeline done properly: a two-column grid where every date column, marker and description stays perfectly aligned across entries via CSS subgrid — even when one role has three bullet points and another has one — with duration chips computed into real <time> ranges.
The e-commerce essential: Placed → Packed → Shipped → Out for delivery → Delivered as a horizontal stepper whose connector fills to the current stage with one custom property, an animated pulse on the live step, ETA copy, and a container-query flip to the vertical mobile layout — the exact component order-status emails link to.
A Strava-flavored vertical feed: day-grouped workout cards with CSS-drawn activity glyphs, tabular metric rows (distance · pace · time), a personal-record flare, and effort bars — the mobile-first card timeline pattern that fits health apps, habit trackers and any per-day activity log.
The scroll-driven flagship: milestones fade-and-rise into place and their markers pop exactly as they scroll into view — driven entirely by the CSS animation-timeline: view() API, zero IntersectionObserver, zero JavaScript. Scrub the page up and down and the entrances play forwards and backwards like a scrubbing video, because the scrollbar IS the timeline.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
A real project Gantt without a charting library: team swimlanes as grid rows, a 12-week header, and every task bar positioned purely by grid-column line numbers — change a task's start and length by editing two integers in the HTML. Week gridlines come from one repeating gradient; the current week is a highlighted column band.
A day-of-event schedule the calendar-app way: an hour ruler down the left, two stage columns across, and every session block sized by its real duration — 30 minutes = 1 grid row — so the geometry IS the schedule. Overlaps are impossible to misread, gaps are honest, and a lunch block spans both tracks with one declaration.
Deep time as sediment: stacked era blocks whose heights are proportional to duration, textured with pure-CSS grain gradients, flanked by a depth scale — the layered-block metaphor that fits geology, archaeology dig reports, company history 'eras' and any dataset where duration should be FELT, not just labeled.
Every event a ticket: tour dates rendered as stub cards with real punched notches (radial-gradient masks), perforated tear lines, rotated date tabs and a sold-out stamp — the textured timeline for concert schedules, festival lineups, event series and travel itineraries.
A clinical monitoring strip: timestamped vitals readings (HR, SpO₂, BP) on a shared time axis, each with a pure-CSS sparkline drawn from custom properties via clip-path — plus threshold flags that mark out-of-range readings with icon + text, in the restrained visual language of medical dashboards.
History as celluloid: a vertical strip bordered by repeating sprocket holes (one repeating-radial-gradient, zero images), frame-numbered milestones, and a projector-flicker hover — the cinematic treatment for studio histories, film-festival retrospectives and media-brand 'our story' pages.
Launch-console aesthetics with dashboard discipline: a T-minus/T-plus event sequence in strict monospace, GO/HOLD/NO-GO status lights paired with text labels, a phosphor-green 'now' cursor line, and scan-line texture — the high-contrast technical idiom for aerospace dashboards, infra monitoring and countdown pages.
A Gantt in an apron: process stages (primary ferment, cold crash, conditioning) drawn as duration bars on a shared week grid, each bar positioned and sized purely by grid-column line math from inline tokens — the duration-first timeline for recipes, aging processes, brewing logs and any 'what happens when' production cycle.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
The social-thread structure: a chronological root post with nested replies hanging off curved elbow connectors (border-radius corners, no SVG), avatar-anchored rails that stop exactly at the last child, and collapse toggles built from <details> — the hierarchy-plus-time layout of comment systems, support threads and design-review trails.
The space-saver: a vertical milestone rail where each date row is a <details> disclosure — one click expands the story, and the name attribute makes the set EXCLUSIVE (opening one closes the others) with zero JavaScript — the mobile-first pattern for company histories, FAQs-over-time and product changelogs.
A widescreen horizontal narrative: each chapter of a career or story is a letterboxed 'frame' in a scroll-snapping strip with chapter numbers, act labels and a progress dot rail driven by scroll-snap alone — the storytelling layout for portfolios, agency case studies and anniversary microsites.
An itinerary that walks: waypoints connected by a winding dashed trail built from alternating border-radius arcs (no SVG path), elevation chips, distance-between labels riding the curves, and a trailhead-to-summit narrative — the organic layout for hiking guides, travel itineraries and campus tours.
Developer-docs native: a dark terminal session where each timeline event is a timestamped log line with severity-colored level tags, a blinking block caret typing the newest entry via steps() animation, and a window-chrome header — the format for deploy logs, system journals, incident timelines and CLI tutorials.
A long-read companion: a progress bar that fills as the article scrolls and a chapter rail whose dots light up as each section passes — both driven by the CSS scroll() and view() timelines, the pure-CSS replacement for the scroll-listener progress bars every publisher ships in JavaScript today.
The classic center-spine zigzag — cards alternating left and right of a vertical rail with arrow notches pointing at their markers — made truly portable: it reshapes to a single left-rail column when its CONTAINER drops below 560px, not the viewport. Drop it in a sidebar, a modal or a split view and it lays itself out correctly with zero configuration.
A horizontal era bar where clicking a period (or arrowing between them with a keyboard) swaps the detail panel below — powered entirely by hidden radio inputs and :checked sibling selectors. The pattern for 'company history by era', museum exhibits and product generations, with a sliding underline indicator that moves between tabs using only CSS.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
Every milestone is a URL: a year-chip navigation jumps to events down the page, and the :target pseudo-class lights up whichever milestone the hash points at — spotlight ring, tinted card, raised marker. Share #tls24-2024 and the recipient lands on that exact event, highlighted, with scroll-margin keeping it clear of any sticky header. Zero JavaScript, fully back-button friendly.
The long-form journalism pattern: a chapter panel (giant year, chapter title, running progress dots) pins to the left while its story beats scroll past on the right, then the next chapter's panel takes over — the NYT-interactive structure, built from nothing but position:sticky and grid, no scroll library, no observers.
The public-roadmap layout SaaS companies publish: four quarter columns of feature cards, each double-coded with a status chip (Shipped ✓ / In progress ◐ / Planned ○), and a 'today' line dropped precisely between quarters with a labeled flag — the format that turns 'when is dark mode coming' support tickets into a URL.
The collection's finale: a century of milestones laid out as one wide horizontal panorama that travels sideways as you scroll DOWN — the sticky-viewport + scroll-driven-transform pattern behind award-site history pages, finally possible in pure CSS with animation-timeline: scroll(). No scroll hijacking, no wheel listeners: the scrollbar keeps its native physics and the panorama is simply mapped onto it.
The docs-site staple: releases on a left rail with semver badges, category chips (New / Improved / Fixed) double-coded with glyphs, and month labels that stick in the gutter as their releases scroll by — the layout Linear, Stripe and every serious devtool converged on for 'What's new' pages.
The museum-catalog treatment: decades of history grouped under enormous serif year headings that STICK to the top while their era's events scroll past, then hand off to the next era with a gentle push — position:sticky choreography that makes long histories navigable without a single script, styled like a broadsheet retrospective.
Two parallel stories on a shared chronological spine: company A on the left, company B on the right, year markers riding the center rail, with color-coded sides announced in headers (never color alone) — the format for rivalry retrospectives, before/after process comparisons and 'us vs the old way' marketing pages.
A career or product history drawn as a subway map: two colored lines running in parallel, stations as ring dots with flag labels, and an interchange station where the lines meet — the visual grammar everyone on Earth already knows how to read, built from borders and radial dots with zero SVG.
The booking-confirmation pattern: flight legs as airport-code pairs joined by a dotted route with a plane glyph riding it, duration bars proportional to real flight time, and the layover rendered as an honest dashed gap with a 'short connection' warning chip — the domain where a timeline's geometry genuinely helps people not miss planes.
A team activity stream that behaves like a product, not a demo: new events arrive on top every few seconds with a height-then-fade entrance that never shoves the page, a 'Mentions only' toggle filters the feed with :has() and zero listener code, and the stream politely stops growing while your pointer is inside it — the courtesy every real feed (Slack, GitHub, Linear) implements and most tutorials skip.
FAQ
Frequently asked questions
What is a CSS timeline and when should I use one?
A CSS timeline is a layout that orders content chronologically or sequentially so readers can scan the story before diving into detail. Use one whenever the DATE, STAGE, or ORDER is part of the meaning — release notes, project history, audit trails, delivery tracking, conference agendas, medical records, resume/CV. The 32 demos in this collection map to real product surfaces: GitHub-style commit history for dev-tools changelogs, CI/CD deploy pipelines with conic-gradient status spinners for DevOps dashboards, resume timelines with CSS subgrid for portfolio pages, order tracking steppers for e-commerce checkouts, product roadmaps with quarter columns for SaaS marketing pages, Gantt swimlanes for PM tools, conference agendas with hour grids, and scrollytelling panels for long-form journalism. Every demo is graded on four axes tier-1 (US/UK/CA/AU) front-end developers actually care about: (1) MODERN CSS gated properly — platform features like animation-timeline: view()/scroll(), container queries, subgrid, and :has() sit behind @supports so unsupported browsers get identical static content, never hidden behind an effect that can't play. (2) SEMANTIC & ACCESSIBLE by construction — every timeline is a semantic <ol> of <li> events, dates are real <time datetime> elements, decoration lives in aria-hidden pseudo-elements, state is never color-alone (glyph + text always). (3) PERFORMANCE and Core Web Vitals — animations are transform/opacity only, scroll effects use scroll-driven timelines instead of scroll listeners (zero main-thread cost), and 31 of 32 demos ship zero JavaScript. (4) DROP-IN PORTABILITY — each demo themes from 3-6 CSS custom properties on the .tls-NN root so retuning takes one ruleset.
Vertical or horizontal timeline — which should I use?
Vertical is the default. It reads like prose, works on any width, and screen readers walk it in DOM order — 26 of the 32 demos in this collection ship vertical. Horizontal wins in three specific cases: (1) short repeatable cards where comparing adjacent items matters (conference agenda hour grid, flight itinerary segments, film festival strips, ticket stubs), (2) scrollytelling / panorama patterns where sideways travel IS the effect (Horizontal Panorama Timeline uses animation-timeline: scroll() to map vertical scroll to horizontal transform), (3) scroll-snap carousels where the user drives one item at a time (Cinematic Filmstrip, Ticker Tape Changelog). Avoid horizontal scroll on mobile unless the interaction feels intentional — the shipped horizontal demos all fall back to vertical stacks under 480px, or ship a documented scroll-snap fallback.
How do I ship a CI/CD deploy pipeline timeline for our dashboard?
Demo 02 in this collection is a production-ready CI/CD deploy pipeline: Build → Test → Deploy stages joined by connector lines that fill with state, a conic-gradient spinner on the running step, and success/pending/failed expressed with icon AND text so it reads correctly for color-blind engineers (WCAG 1.4.1). Wire it to your CI status: (1) each stage carries a data-state attribute (data-state='success' | 'running' | 'failed' | 'pending') that drives chip color, icon, and border via attribute selectors — no class swaps needed, just one attribute per stage; (2) the connector fill uses a single --progress custom property per stage that you can set inline (--progress:100% for done, --progress:60% for a partially-complete stage) so a small server-render helper is all it takes; (3) the running spinner is @property-registered so it degrades cleanly on older browsers; (4) prefers-reduced-motion turns the spinner into a static conic pattern. Pair this with a Terminal Log timeline (Demo 19) below the pipeline for the streaming log output, and you have the Vercel / Netlify / GitHub Actions dashboard pattern in ~200 lines of CSS.
How do I use CSS subgrid for a resume / work-experience timeline?
Demo 03 (Resume / Work Experience Timeline) is the reference implementation. The trick: the outer <ol> is a two-column grid (date-column | detail-column), and each <li> uses display: grid; grid-template-columns: subgrid; grid-column: 1 / -1 so its INNER date-cell and detail-cell align to the OUTER grid tracks. That means when one role has three bullet points and another has one, every date, every marker, and every description edge stays perfectly aligned across the entries — with zero manual width calculations. Subgrid is Baseline widely-available (Chrome, Safari, Firefox all shipped), and the @supports (grid-template-columns: subgrid) block wraps the alignment rules so older browsers get a graceful two-column flex fallback with slightly less-perfect alignment. This is the pattern LinkedIn, Read.cv, and every serious portfolio site ships for resume timelines — before subgrid, you needed JS to measure and align.
How do I ship a SaaS product roadmap timeline without linking to Notion or Trello?
Demo 25 (Product Roadmap Timeline — Quarter Columns) is the public-roadmap pattern SaaS companies converge on: four quarter columns of feature cards, each double-coded with a status chip (Shipped ✓ / In progress ◐ / Planned ○), and a 'today' line dropped precisely between quarters with a labeled flag. Status is a data-status attribute driving everything (chip color + glyph + card border) via attribute selectors — no class swaps, no conditional rendering, just one attribute per card that your CMS or Airtable can populate. The 'today' line position is one --today custom property (compute it server-side: (dayOfYear/365)*100%) so it stays honest without JavaScript. Ship this as a static /roadmap page and every 'when is X coming?' support ticket has a URL to point at — Linear, Height, Notion Product, and Superhuman all ship variants of this exact pattern. The mobile fallback stacks the four quarters into a horizontal scroll-snap strip so the roadmap still reads at 375px.
How do I ship an order tracking timeline (Amazon / DoorDash / Stripe style)?
Demo 04 is the modern order-tracking stepper: Placed → Packed → Shipped → Out for delivery → Delivered as a horizontal stepper whose connector fills to the current stage with one --step custom property (--step:3 means step 3 is active), an animated pulse on the live step, ETA copy, and a container-query flip to vertical layout under 480px containers. This is the exact pattern Amazon Prime, DoorDash, Uber Eats, Stripe Checkout, and Shopify Shop app converged on. Three ships you almost certainly need to add: (1) status polling — the demo is display-only; wire a small fetch on an interval to update --step and the ETA text, (2) accessibility announcement — when --step advances, add aria-live='polite' on a hidden status region so screen readers hear 'Step 3 of 5: Shipped', (3) error state — add a data-error attribute that changes the current step from amber-pulse to red-static with a 'Contact support' link. The demo ships the visual pattern; the polling + error handling is ~20 lines of JS added on your side.
Which timelines use scroll-driven animations (animation-timeline)?
Three demos in this collection ship animation-timeline: view() or scroll() as the primary motion driver. Demo 06 (Scroll-Driven Reveal Timeline) uses animation-timeline: view() so each milestone fades and rises exactly as it scrolls into the viewport — zero IntersectionObserver, zero JavaScript, and scrubbing back and forth plays the entrance forwards and backwards like a video, because the scrollbar IS the timeline. Demo 20 (Reading-Progress Timeline) uses animation-timeline: scroll() to fill a progress bar and light chapter dots as an article scrolls — the pure-CSS replacement for the scroll-listener progress bars every publisher ships in JavaScript. Demo 26 (Horizontal Panorama) maps vertical scroll to horizontal translateX across a 4-viewport-wide track — the sticky-viewport + scroll-driven-transform pattern behind award-site history pages. Every animation-timeline demo ships an @supports not fallback that delivers the identical content statically (or with a native scroll-snap carousel for the panorama) so unsupported browsers never see broken layouts. Scroll-driven animations are Baseline newly-available in Chromium and shipping stable in Safari and Firefox — check caniuse.com for the exact browser cut-off before relying on them for critical UI.
How do I keep timelines accessible for screen readers and keyboard users?
Every timeline in this collection is built on semantic <ol> or <ul> lists (never <div> stacks), so screen readers announce 'List, 5 items' before walking each event in order. Dates are real <time datetime> elements so assistive tech reads them consistently. Decorative rails, dots, and connectors live in aria-hidden pseudo-elements — never in real DOM that gets announced. Status is never color-alone — every chip carries text (Shipped / In progress / Planned) AND a glyph (✓ / ◐ / ○) alongside its color, which satisfies WCAG 1.4.1 for the ~8% of male users with red-green color blindness. Interactive timelines (Accordion Timeline uses <details name> for exclusive-open behavior, Tabbed Era uses radio inputs with :checked, Anchor-Nav uses :target for deep-linkable milestones) all work with Tab / Enter / Space and have visible focus rings via :focus-visible. Continuous animations (Terminal typing caret, EKG trace pulse, activity-feed fade-ins) all sit behind @media (prefers-reduced-motion: no-preference) with the reduced-motion path showing complete content statically.
How do I adapt these timelines to my design system?
Every demo themes from 3-6 CSS custom properties declared on the demo root (.tls-NN). Change --tl-accent for the primary color, --tl-ink for text, --tl-mut for muted text, --tl-line for rails and borders, --tl-bg for the wrapper background — the whole demo retunes without touching layout math. The class prefix .tls-NN is per-demo scoped so you can paste multiple demos into the same project without collision (paste Demo 02 + Demo 25 + Demo 08 side-by-side and they never fight). Typography swaps cleanly: the demos use Georgia / system-ui / ui-monospace by default, but you can drop Inter, Geist, or any custom stack in — the layouts are set with clamp() and ch/fr fluid units so they reflow to whatever font metrics you pick. For dark/light theming, override the tokens inside a [data-theme='dark'] block on the root or use light-dark() directly.