Skip to content

Timeline ​

The Timeline component is used to display a chronological sequence of events or activities. It provides a visual representation of the order in which events occurred, allowing users to easily track and understand the progression of a process.

Basic usage ​

A basic timeline showing a sequence of events.

  1. Quantity changed from 2032.00 to 2500.00.

    NJ
    Nick Jack
  2. Quantity changed from 1840.00 to 2000.00.

    NJ
    Nick Jack
  3. Unit of Measure changed from Gram to Unit

    NJ
    Nick Jack
vue
<script setup lang="ts">
  import { ref } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Timeline>
    <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
      <div class="tw-max-w-[246px]">
        <h3 class="tw-mb-3 tw-text-base tw-text-ice-900">
          {{ changeLogEvent.title }}
        </h3>

        <div class="tw-flex tw-items-center tw-gap-[6px]">
          <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
          <span class="tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
        </div>
      </div>
    </TimelineItem>
  </Timeline>
</template>

Side ​

The timeline component can receive a side prop to display the content on the start | end side of the timeline.

Start side ​

  1. Quantity changed from 2032.00 to 2500.00.

    NJ
    Nick Jack
  2. Quantity changed from 1840.00 to 2000.00.

    NJ
    Nick Jack
  3. Unit of Measure changed from Gram to Unit

    NJ
    Nick Jack
vue
<script setup lang="ts">
  import { ref } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Timeline side="start">
    <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
      <div class="tw-max-w-[246px]">
        <h3 class="tw-mb-3 tw-text-right tw-text-base tw-text-ice-900">
          {{ changeLogEvent.title }}
        </h3>

        <div class="tw-flex tw-items-center tw-justify-end tw-gap-[6px]">
          <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
          <span class="tw-order-first tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
        </div>
      </div>
    </TimelineItem>
  </Timeline>
</template>

Opposite content ​

The timeline component exposes the opposite slot to add content to the opposite side of the main content.

  1. Quantity changed from 2032.00 to 2500.00.

    NJ
    Nick Jack
    edited
    Feb 28, 20244:00 AM
  2. Quantity changed from 1840.00 to 2000.00.

    NJ
    Nick Jack
    edited
    Dec 19, 20233:06 AM
  3. Unit of Measure changed from Gram to Unit

    NJ
    Nick Jack
    edited
    Nov 14, 202311:50 AM
vue
<script setup lang="ts">
  import { format } from 'date-fns';
  import { ref } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Chip from '../../../src/components/Chip/Chip.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Timeline>
    <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
      <template #opposite>
        <div class="tw-text-right">
          <Chip class="tw-mb-[6px] tw-py-1.5 tw-font-bold">
            {{ changeLogEvent.status.label }}
          </Chip>

          <div class="tw-text-xs tw-text-ice-700">
            <span class="tw-block">{{ format(changeLogEvent.date, 'PP') }}</span>
            <span class="tw-block">{{ format(changeLogEvent.date, 'p') }}</span>
          </div>
        </div>
      </template>

      <div class="tw-max-w-[246px]">
        <h3 class="tw-mb-3 tw-text-base tw-text-ice-900">
          {{ changeLogEvent.title }}
        </h3>

        <div class="tw-flex tw-items-center tw-gap-[6px]">
          <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
          <span class="tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
        </div>
      </div>
    </TimelineItem>
  </Timeline>
</template>

Content density ​

The Timeline component can receive a density prop to set the spacing around the content.

Compact ​

  1. Quantity changed from 2032.00 to 2500.00.

    NJ
    Nick Jack
  2. Quantity changed from 1840.00 to 2000.00.

    NJ
    Nick Jack
  3. Unit of Measure changed from Gram to Unit

    NJ
    Nick Jack
vue
<script setup lang="ts">
  import { ref } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Timeline density="compact">
    <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
      <h3 class="tw-mb-3 tw-text-base tw-text-ice-900">
        {{ changeLogEvent.title }}
      </h3>

      <div class="tw-flex tw-items-center tw-gap-[6px]">
        <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
        <span class="tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
      </div>
    </TimelineItem>
  </Timeline>
</template>

Comfortable ​

  1. Quantity changed from 2032.00 to 2500.00.

    NJ
    Nick Jack
  2. Quantity changed from 1840.00 to 2000.00.

    NJ
    Nick Jack
  3. Unit of Measure changed from Gram to Unit

    NJ
    Nick Jack
vue
<script setup lang="ts">
  import { ref } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Timeline density="comfortable">
    <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
      <h3 class="tw-mb-3 tw-text-base tw-text-ice-900">
        {{ changeLogEvent.title }}
      </h3>

      <div class="tw-flex tw-items-center tw-gap-[6px]">
        <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
        <span class="tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
      </div>
    </TimelineItem>
  </Timeline>
</template>

Within a Drawer ​

An example using a timeline inside a drawer Modal.

vue
<script setup lang="ts">
  import { format } from 'date-fns';
  import { ref, useCssModule } from 'vue';

  import Avatar from '../../../src/components/Avatar/Avatar.vue';
  import Button from '../../../src/components/Button/Button.vue';
  import Chip from '../../../src/components/Chip/Chip.vue';
  import Modal from '../../../src/components/Modal/Modal.vue';
  import Timeline from '../../../src/components/Timeline/Timeline.vue';
  import TimelineItem from '../../../src/components/TimelineItem/TimelineItem.vue';

  const classes = useCssModule();
  const emit = defineEmits(['dismiss']);

  export interface ChangeLogEvent {
    date: Date;
    title: string;
    status: {
      label: string;
      colorScheme: string;
    };
    avatar: {
      name: string;
    };
  }

  const changeLogEvents = ref<ChangeLogEvent[]>([
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2024, 1, 29, 4, 14, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 47, 0),
      title: 'Unit of Measure changed from Unit to Gram',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2024, 1, 29, 4, 14, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 47, 0),
      title: 'Unit of Measure changed from Unit to Gram',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2024, 1, 28, 4, 0, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2024, 1, 29, 4, 14, 0),
      title: 'Quantity changed from 2032.00 to 2500.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 11, 19, 3, 6, 0),
      title: 'Quantity changed from 1840.00 to 2000.00.',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 50, 0),
      title: 'Unit of Measure changed from Gram to Unit',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
    {
      date: new Date(2023, 10, 14, 11, 47, 0),
      title: 'Unit of Measure changed from Unit to Gram',
      status: {
        label: 'edited',
        colorScheme: 'blue',
      },
      avatar: {
        name: 'Nick Jack',
      },
    },
  ]);
</script>

<template>
  <Modal title="Product Change Log" position="right" size="narrow" @dismiss="emit('dismiss')">
    <Timeline>
      <TimelineItem v-for="(changeLogEvent, index) in changeLogEvents" :key="index">
        <template #opposite>
          <div class="tw-text-right">
            <Chip class="tw-mb-[6px] tw-py-1.5 tw-font-bold">
              {{ changeLogEvent.status.label }}
            </Chip>

            <div class="tw-text-xs tw-text-ice-700">
              <span class="tw-block">{{ format(changeLogEvent.date, 'PP') }}</span>
              <span class="tw-block">{{ format(changeLogEvent.date, 'p') }}</span>
            </div>
          </div>
        </template>

        <div class="tw-max-w-[246px]">
          <h3 class="tw-mb-3 tw-text-base tw-text-ice-900">
            {{ changeLogEvent.title }}
          </h3>

          <div class="tw-flex tw-items-center tw-gap-[6px]">
            <Avatar :name="changeLogEvent.avatar.name" bg-color="purple-500" text-color="white" />
            <span class="tw-text-sm tw-text-ice-700">{{ changeLogEvent.avatar.name }}</span>
          </div>
        </div>
      </TimelineItem>
    </Timeline>

    <template #footer>
      <Button :class="classes['change-log__button']" secondary class="tw-w-full" @click="emit('dismiss')">
        Close
      </Button>
    </template>
  </Modal>
</template>

<style module>
  footer:has(.change-log__button) {
    border-top: 1px solid theme('colors.ice.500');
  }
</style>