FairGarden Design

Timeline

Ordered milestones on a dashed spine, a dated agenda of sessions, or a date-block schedule.

import {
  Timeline,
  TimelineEntry,
  TimelineList,
  TimelineText,
  TimelineTitle,
} from '@fairgarden-private/design/components/Timeline'

The spine is always dashed: dashes mean path. Markers are semantic and decorative at once, so every state they show is also in words: a circle starts, an X marks a waypoint (past or planned, told apart by the date key), a triangle ends, and the solid dot means "now", defined by its dated label or a TimelineCaption. Entries never alternate sides. Use Index Rows for unordered or unrelated events.

Trail and date block

Trail and date block

trail is the default. The first entry draws the origin, the last the terminal, and the others waypoints; stage="current" draws the "now" dot and sets aria-current="step". ongoing keeps the last entry a waypoint and puts the terminal past it. Pass marker to override a shape. Dates sit above titles below 768 px of container, in a key column left of the spine from 768, and at 3 / gutter / 7 from 1024. date-block sets a TimelineDateStack (month, day, weekday) in a fixed key column with no spine. A TimelineTitleLink links a whole entry.

  1. The first 40 acres

    Neighbors pool their savings to buy the ravine.
  2. The meadow comes back

    Controlled burns return the prairie grasses.
  3. Headwaters campaign

    Raising funds to protect the upper creek.
  4. 2028 · Planned

    Trail to the ridge

● marks today, 22 Sep 2026.

  1. Night walk

    North kiosk, 7 p.m.
  2. Seed swap

    Barn, 10 a.m.
TimelineTrail.tsx
import {
  Timeline,
  TimelineCaption,
  TimelineDateStack,
  TimelineEntry,
  TimelineList,
  TimelineText,
  TimelineTitle,
  TimelineTitleLink,
} from '@fairgarden-private/design/components/Timeline'
import styles from './trail.module.css'

/** A trail timeline (origin, waypoints, "now", terminal past the end) and a date-block schedule. */
export function TimelineTrail() {
  return (
    <div className={styles.stack}>
      <Timeline ongoing>
        <TimelineList>
          <TimelineEntry stage="past" date={<time dateTime="1987">1987</time>}>
            <TimelineTitle>The first 40 acres</TimelineTitle>
            <TimelineText>Neighbors pool their savings to buy the ravine.</TimelineText>
          </TimelineEntry>
          <TimelineEntry stage="past" date={<time dateTime="2004">2004</time>}>
            <TimelineTitle>
              <TimelineTitleLink href="#trail">The meadow comes back</TimelineTitleLink>
            </TimelineTitle>
            <TimelineText>Controlled burns return the prairie grasses.</TimelineText>
          </TimelineEntry>
          <TimelineEntry stage="current" date={<time dateTime="2026-09-22">Today, 22 Sep 2026</time>}>
            <TimelineTitle>Headwaters campaign</TimelineTitle>
            <TimelineText>Raising funds to protect the upper creek.</TimelineText>
          </TimelineEntry>
          <TimelineEntry stage="planned" date="2028 · Planned">
            <TimelineTitle>Trail to the ridge</TimelineTitle>
          </TimelineEntry>
        </TimelineList>
        <TimelineCaption>● marks today, 22 Sep 2026.</TimelineCaption>
      </Timeline>

      <Timeline kind="date-block">
        <TimelineList>
          <TimelineEntry
            date={<TimelineDateStack dateTime="2026-10-03" month="Oct" day="3" weekday="Sat" />}
          >
            <TimelineTitle>Night walk</TimelineTitle>
            <TimelineText>North kiosk, 7 p.m.</TimelineText>
          </TimelineEntry>
          <TimelineEntry
            date={<TimelineDateStack dateTime="2026-10-11" month="Oct" day="11" weekday="Sun" />}
          >
            <TimelineTitle>Seed swap</TimelineTitle>
            <TimelineText>Barn, 10 a.m.</TimelineText>
          </TimelineEntry>
        </TimelineList>
      </Timeline>
    </div>
  )
}

Agenda

Agenda

agenda groups sessions under TimelineDays: a date head in the heading ink, a scope caption that states the time zone once, then sessions led by the » marker in the accent. TimelineSessionTitle joins its meta with the serif's pipe and a no-break space, so a pipe never starts a line. Say "Happening now" or "Past" in TimelineMeta.

Saturday, 3 October

All times are Eastern.

  1. Warblers of the ravine | Walk | 9–11 a.m.

    Happening now

    A slow loop with the bird count leaders.
  2. Why meadows burn | Talk | 1–2 p.m.

    Our stewardship director on fire as a tool.
TimelineAgenda.tsx
import {
  Timeline,
  TimelineDateHead,
  TimelineDay,
  TimelineDescription,
  TimelineList,
  TimelineMeta,
  TimelineScope,
  TimelineSession,
  TimelineSessionTitle,
} from '@fairgarden-private/design/components/Timeline'

/** Date heads in the heading ink, one scope caption, sessions led by the » marker. */
export function TimelineAgenda() {
  return (
    <Timeline kind="agenda">
      <TimelineDay>
        <TimelineDateHead>Saturday, 3 October</TimelineDateHead>
        <TimelineScope>All times are Eastern.</TimelineScope>
        <TimelineList>
          <TimelineSession>
            <TimelineSessionTitle meta={['Walk', '9–11 a.m.']}>Warblers of the ravine</TimelineSessionTitle>
            <TimelineMeta>Happening now</TimelineMeta>
            <TimelineDescription>A slow loop with the bird count leaders.</TimelineDescription>
          </TimelineSession>
          <TimelineSession>
            <TimelineSessionTitle meta={['Talk', '1–2 p.m.']}>Why meadows burn</TimelineSessionTitle>
            <TimelineDescription>Our stewardship director on fire as a tool.</TimelineDescription>
          </TimelineSession>
        </TimelineList>
      </TimelineDay>
    </Timeline>
  )
}

Grounds and print

Light grounds are the default home; on deep grounds the spine takes the ground's accent. On saturated grounds keep to six entries, all in the one ink. In print the spine is 0.75 pt with 3 / 2.5 pt dashes, every marker is kept, entries never split, and "Today" should read "As of".

API Reference

Timeline and TimelineList

The timeline module: a TimelineList of TimelineEntrys (trail and date-block), or TimelineDays holding a TimelineDateHead, a TimelineScope and a TimelineList of TimelineSessions (agenda). A TimelineCaption defines the current marker. Entries never alternate sides; the spine is always dashed [D174].

PropTypeDescription
kind
TimelineKind | undefined

trail (default): a straight dashed spine with the §4.7 markers (origin, waypoints, terminal, “now”). agenda: date heads, a scope caption and sessions led by the » marker. date-block: a stacked date block (month / day / weekday) in a fixed key column, no spine.

ongoing
boolean | undefined

The timeline continues: the last entry keeps a waypoint and the terminal marker sits past it. Default false.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: markers, date keys, titles and text. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: the spine, the agenda’s » markers (--role-accent), title-link underlines and the date heads on light grounds (--role-heading). Never defaulted.

The ordered entries (or an agenda day’s sessions). On an ongoing trail it closes with the terminal marker past the last entry.

TimelineEntry

One milestone: its date key, the spine with its marker, and the body (TimelineTitle, TimelineText). Entries never split across pages.

PropTypeDescription
date
React.ReactNode | undefined

The date key: a time element (type-data), or a TimelineDateStack in the date-block kind. Above the title at base; a column left of the spine from 768 px of container.

marker
TimelineMarker | undefined

Overrides the marker. By default the first entry draws the origin circle, the last the terminal triangle (unless the timeline is ongoing), the rest waypoint X’s, and the current entry the “now” dot.

stage
TimelineStage | undefined

planned (default): waypoint X, and say “Planned” in the date key. past: waypoint X; the date (or “Past”) carries it, never dimmed. current: the solid “now” dot, aria-current="step", and its dated label (“Today, 22 Sep 2026″) in words.

The entry title: type-itemhead in --primary12, within the reading measure. Renders <h3>.

A linked entry’s one link, inside TimelineTitle: a §9.3 title Link whose hit area stretches over the entry; the entry draws the ring.

PropTypeDescription
external
boolean | null | undefined

Adds the arrow-open mark and “(external site)” for assistive technology. Default false.

index
boolean | null | undefined

Adds the screen-only visited ✓ used in long indexes, such as reference and archive lists. Default false [D174, D175].

list
boolean | null | undefined

With kind="nav": a list link (nav-panel, footer, drawer and breadcrumb lists), whose hover is --role-link-hover color only, plus the --ds-stroke-1-5 --role-accent underline where that ink is --primary12. Without it, nav is bare navigation text (bar and utility items, page numbers, toolbar links), whose hover is the --border-size-2 --role-accent underline (§9.3) [D181]. Default false.

muted
boolean | null | undefined

Rests in --role-muted instead of --primary12, as a breadcrumb’s ancestors. Hover takes --role-link-hover only (the underline is added where that ink is --primary12); with kind="nav" it replaces the bar item’s bare-text underline (§9.3, §9.8) [D181]. Default false.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: the link text and focus ring. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: the accent underline. Never defaulted; omitted, it inherits the scope.

className
string | undefined

Extra class names, added after the module’s own.

The entry text: type-body-ui in --primary12. Put long entries in a Collapsible.

The legend line that defines the current marker, e.g. “● Today, 22 Sep 2026″ (§8.11): type-caption in --primary12. In print, write it as “As of 22 Sep 2026″.

TimelineDateStack

The date-block key (Sofar Sounds): MONTH / day / WEEKDAY, stacked. Pass dateTime.

PropTypeDescription
day
React.ReactNode | undefined

The day of the month: a large data numeral.

month
React.ReactNode | undefined

The month, e.g. “Jul”: type-label caps.

weekday
React.ReactNode | undefined

The weekday, e.g. “Sat”: type-label caps.

Agenda parts

One agenda day: TimelineDateHead, TimelineScope, then a TimelineList of sessions.

The agenda date head: type-itemhead in --role-heading (a group head). Renders <h3>.

The scope caption, “All times are Eastern.": the time zone, stated once.

One agenda session, led by the » block marker in --role-accent: TimelineSessionTitle, an optional TimelineMeta (“Happening now”, “Past”) and a TimelineDescription.

The session title, type-itemhead in --primary12, with its meta pipe-joined. Renders <h4>.

PropTypeDescription
meta
React.ReactNode[] | undefined

Fields joined into the title with the serif’s own pipe: “Title | Format | Time”. A no-break space precedes each pipe, so a pipe never starts a line; leave out empty fields.

A session’s meta line: type-caption in --primary12 (“Happening now”, “Past”).

A session’s description: type-body-ui in --primary12.

Additional types

timeline
TimelineCaptionProps

Props for TimelineCaption: paragraph props and render.

type TimelineCaptionProps = useRender.ComponentProps<'p'>
TimelineDateHeadProps

Props for TimelineDateHead: heading props and render (default <h3>).

type TimelineDateHeadProps = useRender.ComponentProps<'h3'>
TimelineDateStackProps

Props for TimelineDateStack: time props, render and the three lines.

type TimelineDateStackProps = {
  /** The month, e.g. "Jul": `type-label` caps. */
  month: React.ReactNode;
  /** The day of the month: a large data numeral. */
  day: React.ReactNode;
  /** The weekday, e.g. "Sat": `type-label` caps. */
  weekday?: React.ReactNode;
}
TimelineDayProps

Props for TimelineDay: section props and render.

type TimelineDayProps = useRender.ComponentProps<'section'>
TimelineDescriptionProps

Props for TimelineDescription: div props and render.

type TimelineDescriptionProps = useRender.ComponentProps<'div'>
timelineEntry

Entry state classes (unmanaged element): the stage of a milestone.

type timelineEntry = timelineEntry
TimelineEntryProps

Props for TimelineEntry: list-item props, render, the stage, the date key and a marker override.

type TimelineEntryProps = React.ClassAttributes<HTMLLIElement> &
  React.LiHTMLAttributes<HTMLLIElement> &
  __type & { stage?: TimelineStage; date?: React.ReactNode; marker?: TimelineMarker }
TimelineKind

The three timeline builds (§12.14).

type TimelineKind = 'trail' | 'agenda' | 'date-block'
TimelineListProps

Props for TimelineList: ol props and render.

type TimelineListProps = useRender.ComponentProps<'ol'>
TimelineMarker

The §4.7 marker set drawn on the spine.

type TimelineMarker = 'origin' | 'waypoint' | 'terminal' | 'current'
TimelineMetaProps

Props for TimelineMeta: paragraph props and render.

type TimelineMetaProps = useRender.ComponentProps<'p'>
TimelineProps

Props for Timeline: section props, render, the kind, ongoing and the color axes.

type TimelineProps = React.ClassAttributes<HTMLElement> &
  React.HTMLAttributes<HTMLElement> &
  __type & {
    kind?: TimelineKind;
    ongoing?: boolean;
    primary?:
      | 'ruby'
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | null;
    secondary?:
      | 'ruby'
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | 'amber'
      | 'blue'
      | 'cyan'
      | 'grass'
      | 'green'
      | 'jade'
      | 'lime'
      | 'mint'
      | 'orange'
      | 'sky'
      | 'teal'
      | 'yellow'
      | null;
  }
TimelineScopeProps

Props for TimelineScope: paragraph props and render.

type TimelineScopeProps = useRender.ComponentProps<'p'>
TimelineSessionProps

Props for TimelineSession: list-item props and render.

type TimelineSessionProps = useRender.ComponentProps<'li'>
TimelineSessionTitleProps

Props for TimelineSessionTitle: heading props, render and the pipe-joined meta.

type TimelineSessionTitleProps = React.ClassAttributes<HTMLHeadingElement> &
  React.HTMLAttributes<HTMLHeadingElement> &
  __type & { meta?: React.ReactNode[] }
TimelineStage

A milestone’s stage: past and planned keep the waypoint X (told apart by words); current is “now”.

type TimelineStage = 'past' | 'current' | 'planned'
TimelineTextProps

Props for TimelineText: div props and render.

type TimelineTextProps = useRender.ComponentProps<'div'>
TimelineTitleLinkProps

Props for TimelineTitleLink: Link props except kind, which is always title.

type TimelineTitleLinkProps = {
  /** Extra class names, added after the module's own. */
  className?: string;
  /**
   * Primary Radix scale: the link text and focus ring. Never defaulted;
   * omitted, it inherits the scope [D133].
   */
  primary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /**
   * Secondary Radix scale: the accent underline. Never defaulted; omitted,
   * it inherits the scope.
   */
  secondary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /**
   * With `kind="nav"`: a list link (nav-panel, footer, drawer and breadcrumb
   * lists), whose hover is `--role-link-hover` color only, plus the
   * `--ds-stroke-1-5` `--role-accent` underline where that ink is
   * `--primary12`. Without it, `nav` is bare navigation text (bar and
   * utility items, page numbers, toolbar links), whose hover is the
   * `--border-size-2` `--role-accent` underline (§9.3) [D181]. Default
   * `false`.
   */
  list?: boolean | null;
  /**
   * Adds the screen-only visited ✓ used in long indexes, such as reference
   * and archive lists. Default `false` [D174, D175].
   */
  index?: boolean | null;
  /**
   * Adds the arrow-open mark and "(external site)" for assistive technology.
   * Default `false`.
   */
  external?: boolean | null;
  /**
   * Rests in `--role-muted` instead of `--primary12`, as a breadcrumb's
   * ancestors. Hover takes `--role-link-hover` only (the underline is added
   * where that ink is `--primary12`); with `kind="nav"` it replaces the bar
   * item's bare-text underline (§9.3, §9.8) [D181]. Default `false`.
   */
  muted?: boolean | null;
}
TimelineTitleProps

Props for TimelineTitle: heading props and render (default <h3>).

type TimelineTitleProps = useRender.ComponentProps<'h3'>

Specification: DESIGN-SYSTEM.md §12.14 (timeline and agenda), §8.11 (the timeline method) and §5.10.2 (module reflow).