Components

Timeline

Render a chronological feed of events with UiTimeline.

UiTimeline renders an ordered list of events, each with a dot, icon, or image marker connected by a line. Use it for activity feeds, order/status history, or changelogs.

Basic timeline

Without icon or imageUrl, items render a plain dot marker.

<script setup lang="ts">
import type { ITimelineItem } from '@colorffy/ui'

const items: ITimelineItem[] = [
  { id: '1', title: 'Account created', text: 'Welcome to the platform', time: '3 days ago' },
  { id: '2', title: 'Profile verified', text: 'Documents approved', time: '2 days ago' },
  { id: '3', title: 'First project', text: 'Project Atlas started', time: '1 day ago' },
]
</script>

<template>
  <UiTimeline :items="items" />
</template>

Icon markers

Set icon (a Material Symbols HTML entity) and optionally variant to color the marker with the shared semantic palette.

<script setup lang="ts">
import type { ITimelineItem } from '@colorffy/ui'

const items: ITimelineItem[] = [
  { id: '1', title: 'New deployment', text: 'Project Atlas v2.4.0 released', time: '3 days ago', icon: '&#xe1b6;', variant: 'success' },
  { id: '2', title: 'Usage alert', text: 'API reached 80% of its limit', time: '1 day ago', icon: '&#xe002;', variant: 'warning' },
]
</script>

<template>
  <UiTimeline :items="items" />
</template>

Image markers

Set imageUrl (and imageAlt) to render an avatar-style marker instead of an icon. imageUrl takes precedence over icon.

<template>
  <UiTimeline
    :items="[
      { id: '1', title: 'Ana Morales', text: 'Approved the proposal', time: '4 hours ago', imageUrl: 'https://i.pravatar.cc/88?img=5', imageAlt: 'Photo of Ana Morales' },
    ]"
  />
</template>

Alternate alignment

Set align="alternate" to center the connector line and zig-zag content left/right (e.g. a company timeline or roadmap).

<template>
  <UiTimeline :items="items" align="alternate" />
</template>

Custom item body

Provide a custom body for a specific item via the #item-<id> named slot, or handle every item the same way via the scoped #item slot ({ item }). Either slot overrides the default title/text/time markup.

<template>
  <UiTimeline :items="items">
    <template #item-release="{ item }">
      <p class="subtitle-1 mb-1">{{ item.title }}</p>
      <UiBadge text="Production" variant="tonal tonal-success" size="sm" />
    </template>
  </UiTimeline>
</template>

Props

PropTypeDefaultDescription
itemsITimelineItem[][]Array of timeline items to render.
align'start' | 'alternate''start'Layout alignment. 'alternate' centers the connector line and alternates content left/right.
size'sm' | 'lg'—Tighter (sm) or larger (lg) layout; unset is the standard size.
customClassstring | string[] | Record<string, boolean> | null—Extra classes for the root element.

ITimelineItem

PropertyTypeDescription
idstringUnique identifier. Used as the render key and to target the #item-<id> slot.
titlestring | nullTitle text displayed prominently.
textstring | nullSupporting text.
timestring | nullTimestamp/label rendered above the title.
iconstring | nullMaterial Symbols icon code for the marker.
imageUrlstring | nullImage URL rendered in place of the icon/dot marker. Takes precedence over icon.
imageAltstring | nullAlt text for the image.
variantThemeColor | nullSemantic color applied to the dot/icon marker.

Slots: #item-<id> per-item custom body (takes precedence); #item scoped slot ({ item }) applied to every item as a fallback; both fall back to the default title/text/time markup when unused.

The marker's icon wrapper reuses the same footprint as UiListItem's icon wrapper, so a list and a timeline placed side by side stay visually aligned.

CSS variables

Set these on :root, or on any wrapper to restyle only the timelines inside it. The sm and lg sizes keep their own spacing, dot, icon and text sizes (and lg its image size), and colored markers keep their palette.

:root {
  --cffy-timeline-connector-color: var(--cffy-primary-a50);
  --cffy-timeline-dot-color: var(--cffy-primary-a10);
}
VariableDefaultControls
--cffy-timeline-gap--cffy-space-16Space between the marker and the content
--cffy-timeline-item-spacing--cffy-space-24Space between items
--cffy-timeline-connector-thickness / --cffy-timeline-connector-color2px / --cffy-outline-surfaceLine between markers
--cffy-timeline-dot-size / --cffy-timeline-dot-color0.75rem / --cffy-muted-a10Dot marker
--cffy-timeline-icon-bg-color / --cffy-timeline-icon-color--cffy-surface-container-lowest / --cffy-on-backgroundIcon marker
--cffy-timeline-icon-size / --cffy-timeline-icon-padding / --cffy-timeline-icon-radius--cffy-fs-lg / 0.625rem / --cffy-radius-mdIcon size, tile padding and corners
--cffy-timeline-image-size / --cffy-timeline-image-radius2.75rem / --cffy-radius-mdImage marker
--cffy-timeline-title-font-size / --cffy-timeline-title-font-weight--cffy-fs-2xs / --cffy-fw-600Title
--cffy-timeline-text-font-size / --cffy-timeline-text-color--cffy-fs-3xs / --cffy-muted-a10Body text
--cffy-timeline-time-color--cffy-muted-a10Timestamp caption
Copyright © 2026