All examples

Timeline

Vertical and horizontal timelines: every status and tone, links through the URL policy, composed items, right to left and dark. Fictional data only.

Timeline

texuli-timeline · texuli/Timeline

texuli timeline
Props
  • hrefTrust
  • items
  • label
  • messages
  • orientation

Live example loads in an isolated same-origin preview frame.

Source (timeline/TimelineDemo.svelte)
<script lang="ts">
import { TIMELINE_TONES, Timeline, TimelineItem, type TimelineItemData } from 'texuli/timeline';

const project: TimelineItemData[] = [
  {
    id: 'kickoff',
    time: 'Mar 4',
    datetime: '2026-03-04',
    title: 'Kickoff',
    description: 'Scope and owners agreed with the fictional Orbit team.',
    status: 'complete',
    tone: 'success',
  },
  {
    id: 'review',
    time: 'Mar 18',
    datetime: '2026-03-18',
    title: 'Design review',
    description: 'Walkthrough of the onboarding flow.',
    status: 'current',
  },
  {
    id: 'beta',
    time: 'Apr 1',
    datetime: '2026-04-01',
    title: 'Private beta',
    href: 'https://example.com/beta',
    status: 'upcoming',
    tone: 'info',
  },
];

const roadmap: TimelineItemData[] = [
  { id: 'q1', time: 'Q1', title: 'Research', status: 'complete' },
  { id: 'q2', time: 'Q2', title: 'Prototype', status: 'complete' },
  { id: 'q3', time: 'Q3', title: 'Pilot', status: 'current', tone: 'warning' },
  { id: 'q4', time: 'Q4', title: 'General availability', status: 'upcoming' },
];

const tones: TimelineItemData[] = TIMELINE_TONES.map((tone) => ({
  id: tone,
  title: `Tone: ${tone}`,
  tone,
}));

const links: TimelineItemData[] = [
  { id: 'docs', title: 'Release notes', href: 'https://example.com/notes', time: 'Today' },
  // The URL policy rejects this one: it renders as plain text.
  { id: 'blocked', title: 'Blocked link', href: 'javascript:alert(1)', time: 'Today' },
];

const rtl: TimelineItemData[] = [
  { id: 'a', time: '٤ مارس', title: 'البداية', status: 'complete' },
  { id: 'b', time: '١٨ مارس', title: 'المراجعة', status: 'current' },
  { id: 'c', time: '١ أبريل', title: 'الإطلاق', status: 'upcoming' },
];
</script>

<section data-example="texuli-timeline" aria-labelledby="timeline-demo-title">
  <h2 id="timeline-demo-title">Timeline</h2>

  <h3>Vertical, with every status</h3>
  <Timeline items={project} label="Project milestones" />

  <h3>Horizontal</h3>
  <Timeline items={roadmap} orientation="horizontal" label="Roadmap" />

  <h3>Every tone</h3>
  <Timeline items={tones} orientation="horizontal" label="Tones" />

  <h3>Links through the URL policy</h3>
  <Timeline items={links} label="Links" />

  <h3>Composed items</h3>
  <Timeline label="Incident">
    <TimelineItem title="Alert fired" time="09:12" tone="destructive" status="complete">
      {#snippet icon()}<svg viewBox="0 0 12 12" width="10" height="10" focusable="false"><path d="M6 2v5M6 9.5v.5" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" /></svg>{/snippet}
      <p class="note">Paged the fictional on-call engineer.</p>
    </TimelineItem>
    <TimelineItem title="Mitigated" time="09:40" tone="success" status="current" />
  </Timeline>

  <h3>Right to left</h3>
  <div class="stack" dir="rtl" lang="ar">
    <Timeline items={rtl} label="الجدول الزمني" messages={{ complete: 'مكتمل', current: 'حالي', upcoming: 'قادم' }} />
    <Timeline items={rtl} orientation="horizontal" label="الجدول الزمني الأفقي" messages={{ complete: 'مكتمل', current: 'حالي', upcoming: 'قادم' }} />
  </div>

  <h3>Dark</h3>
  <div class="dark-panel" data-tx-mode="dark">
    <Timeline items={project} label="Project milestones (dark)" />
  </div>
</section>

<style>
  /* The example paints its own surface, so contrast is checked against what users see. */
  section {
    display: grid;
    gap: 1rem;
    padding: 1rem;
    max-inline-size: 48rem;
    background: Canvas;
    color: CanvasText;
  }

  .stack {
    display: grid;
    gap: 1.5rem;
  }

  .note {
    margin: 0;
    font-size: 0.875rem;
  }

  .dark-panel {
    color-scheme: dark;
    padding: 1rem;
    border-radius: var(--tx-radius, 0.5rem);
    background: Canvas;
    color: CanvasText;
    /* Keep token-driven children on the dark panel surface. */
    --tx-foreground: CanvasText;
    --tx-background: Canvas;
    --tx-muted-foreground: CanvasText;
    --tx-border: GrayText;
    --tx-link: LinkText;
    --tx-ring: Highlight;
  }
</style>