Svelte 5 component · fractalsvelte-ui · Blocks
Timeline
A vertical chronology stream linking sequence events along a continuous spine track. Features semantic marker nodes for operational status and supports left-aligned or alternating milestone layouts.
Install
Timeline is a Svelte 5 timeline component in @fractaldesign/fractalsvelte-ui. It works in SvelteKit and in any Svelte 5 app.
pnpm add @fractaldesign/fractalsvelte-uiImport
import { Timeline } from '@fractaldesign/fractalsvelte-ui';Usage
<Timeline items={[]} align={'left'}>Click me</Timeline>Props
Also accepts every attribute of HTMLAttributes, spread onto
the root element.
| Name | Type | Default |
|---|---|---|
items | TimelineItem[] | [] |
align | 'left' | 'alternate' | 'left' |
class | string | '' |
Snippets
| Name | Type | Default |
|---|---|---|
children | Snippet | — |
Source
Show the Timeline.svelte source
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { Intent } from '../../types';
export interface TimelineItem {
id?: string;
title: string;
time?: string;
description?: string;
intent?: Intent;
active?: boolean;
}
interface Props extends HTMLAttributes<HTMLDivElement> {
items?: TimelineItem[];
align?: 'left' | 'alternate';
class?: string;
children?: Snippet;
}
let {
items = [],
align = 'left',
class: className = '',
children,
...restProps
}: Props = $props();
</script>
<div
class="f-timeline {className}"
data-align={align}
{...restProps}
>
{#if items.length > 0}
<div class="f-timeline-track">
{#each items as item, index}
<div
class="f-timeline-entry"
data-intent={item.intent}
data-active={item.active ? 'true' : undefined}
data-side={align === 'alternate' ? (index % 2 === 0 ? 'left' : 'right') : 'right'}
>
<div class="f-timeline-marker">
<span class="f-marker-dot"></span>
{#if item.active}
<span class="f-marker-pulse"></span>
{/if}
</div>
<div class="f-timeline-card">
<header class="f-card-header">
<h4 class="f-entry-title">{item.title}</h4>
{#if item.time}
<time class="f-entry-time">{item.time}</time>
{/if}
</header>
{#if item.description}
<p class="f-entry-desc">{item.description}</p>
{/if}
</div>
</div>
{/each}
</div>
{/if}
{#if children}
<div class="f-timeline-custom">
{@render children()}
</div>
{/if}
</div>
<style>
@layer fractalsvelte-ui.components {
.f-timeline {
position: relative;
width: 100%;
font-family: var(--f-font-sans);
box-sizing: border-box;
}
.f-timeline-track {
position: relative;
display: flex;
flex-direction: column;
gap: var(--f-space-xl);
}
/* Spine line */
.f-timeline[data-align='left'] .f-timeline-track::before {
content: '';
position: absolute;
top: 0.5rem;
bottom: 0.5rem;
left: 0.75rem;
width: 2px;
background: var(--f-border);
transform: translateX(-50%);
}
.f-timeline[data-align='alternate'] .f-timeline-track::before {
content: '';
position: absolute;
top: 0.5rem;
bottom: 0.5rem;
left: 50%;
width: 2px;
background: var(--f-border);
transform: translateX(-50%);
}
.f-timeline-entry {
position: relative;
display: flex;
align-items: flex-start;
gap: var(--f-space-lg);
}
.f-timeline[data-align='left'] .f-timeline-entry {
padding-left: 2rem;
}
.f-timeline[data-align='alternate'] .f-timeline-entry[data-side='left'] {
width: calc(50% - 1.5rem);
align-self: flex-start;
flex-direction: row-reverse;
text-align: right;
}
.f-timeline[data-align='alternate'] .f-timeline-entry[data-side='right'] {
width: calc(50% - 1.5rem);
align-self: flex-end;
margin-left: auto;
text-align: left;
}
/* Marker */
.f-timeline-marker {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 1rem;
height: 1rem;
border-radius: var(--f-radius-full);
background: var(--f-surface);
z-index: 2;
}
.f-timeline[data-align='left'] .f-timeline-marker {
left: 0.75rem;
top: 0.35rem;
transform: translateX(-50%);
}
.f-timeline[data-align='alternate'] .f-timeline-entry[data-side='left'] .f-timeline-marker {
right: -2rem;
top: 0.35rem;
transform: translateX(50%);
}
.f-timeline[data-align='alternate'] .f-timeline-entry[data-side='right'] .f-timeline-marker {
left: -2rem;
top: 0.35rem;
transform: translateX(-50%);
}
.f-marker-dot {
width: 0.625rem;
height: 0.625rem;
border-radius: var(--f-radius-full);
background: var(--f-primary);
transition: background-color var(--f-duration-fast) var(--f-ease);
}
.f-timeline-entry[data-intent='info'] .f-marker-dot {
background: var(--f-info);
}
.f-timeline-entry[data-intent='success'] .f-marker-dot {
background: var(--f-success);
}
.f-timeline-entry[data-intent='warning'] .f-marker-dot {
background: var(--f-warning);
}
.f-timeline-entry[data-intent='danger'] .f-marker-dot {
background: var(--f-danger);
}
.f-marker-pulse {
position: absolute;
inset: -3px;
border-radius: var(--f-radius-full);
background: var(--f-primary);
opacity: 0.4;
animation: f-marker-ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes f-marker-ping {
75%, 100% {
transform: scale(2.2);
opacity: 0;
}
}
/* Card */
.f-timeline-card {
flex: 1 1 auto;
display: flex;
flex-direction: column;
gap: 0.25rem;
background: var(--f-surface);
border: 1px solid var(--f-border);
border-radius: var(--f-radius-md);
padding: var(--f-space-md) var(--f-space-lg);
box-shadow: var(--f-shadow-xs);
}
.f-card-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--f-space-sm);
}
.f-entry-title {
margin: 0;
font-size: var(--f-text-sm);
font-weight: 600;
color: var(--f-text);
}
.f-entry-time {
font-size: var(--f-text-xs);
color: var(--f-text-muted);
white-space: nowrap;
}
.f-entry-desc {
margin: 0;
font-size: var(--f-text-sm);
color: var(--f-text-muted);
line-height: 1.5;
}
.f-timeline-custom {
margin-top: var(--f-space-lg);
}
}
</style>Related components
WorkflowNode A graph node card designed for visual pipeline editors and flowchart interfaces.BeforeAfter An interactive visual comparison slider that reveals before and after views through a draggable central…ChatBubble A conversation message bubble with dedicated user, assistant, and system roles.FeatureGrid A responsive grid showcase organizing product highlights into structured cards with icon containers, titles,…FlowCanvas An infinite interactive graph workspace featuring dot matrix, line, and cross background grids.HeroBlock A prominent landing section pairing an announcement badge, large headline, description, and action button…