Display

Timeline

Timeline whose dot aligns itself to every title

Preview

  1. 01

    Milestone 1

    Milestone description.

  2. 02

    Milestone 2

    Milestone description.

  3. 03

    Milestone 3

    Milestone description.

size
layout
current

Code

import css from "./Timeline.module.css";

type Size = "sm" | "md";
type Layout = "stacked" | "axis";

interface ResponsiveSize {
  base: Size;
  mobile?: Size;
  tablet?: Size;
  desktop?: Size;
  wide?: Size;
}

interface ResponsiveLayout {
  base: Layout;
  mobile?: Layout;
  tablet?: Layout;
  desktop?: Layout;
  wide?: Layout;
}

interface TimelineProps {
  size: Size | ResponsiveSize;
  layout: Layout | ResponsiveLayout;
  children: React.ReactNode;
}

function getResponsiveClasses(
  value: Size | Layout | ResponsiveSize | ResponsiveLayout,
): string {
  if (typeof value === "string") {
    return css[value];
  }

  const classes = [css[`${value.base}_base`]];

  if (value.mobile) classes.push(css[`${value.mobile}_mobile`]);
  if (value.tablet) classes.push(css[`${value.tablet}_tablet`]);
  if (value.desktop) classes.push(css[`${value.desktop}_desktop`]);
  if (value.wide) classes.push(css[`${value.wide}_wide`]);

  return classes.join(" ");
}

export default function Timeline({
  size,
  layout,
  children,
}: TimelineProps) {
  return (
    <ol
      className={`
        ${css.timeline}
        ${getResponsiveClasses(size)}
        ${getResponsiveClasses(layout)}
      `}
      role="list"
    >
      {children}
    </ol>
  );
}

Props

PropTypeRequiredDescription
size"sm" | "md" | ResponsiveSizerequiredSize of the dot, the halo and the gaps between items. Fixed or responsive.
layout"stacked" | "axis" | ResponsiveLayoutrequiredStacked (year on top, rail on the left) or axis (rail down the middle, year on one side and content on the other). Fixed or responsive.
childrenReact.ReactNoderequiredThe items, one <TimelineItem> each.
TimelineItem.yearReact.ReactNoderequiredThe item's date or label; in axis it sits across the rail.
TimelineItem.titleReact.ReactNoderequiredThe item's title. The dot centres on it, whatever its size.
TimelineItem.currentboolean—Marks the current item: a filled dot with a halo (aria-current).
TimelineItem.childrenReact.ReactNoderequiredContent under the title.