Timeline

The vertical activity feed on a record: what happened, what is planned, and one nudge when nothing is. React in apps/web/src/components/timeline/ (EntityTimeline.tsx, TimelineRow.tsx, TodayGroup.tsx), CSS ds/timeline.css layered on ds/item-list.css.

It is not a list pattern of its own. Every row is an ItemList row — .il-row with lead, title, byline, content and meta — and timeline.css adds only what an axis needs: the vertical line (drawn with ::before/::after on each row, held together by .tl-rail), the day groups, the tinted today block, the task cards and the change grid. The sheet must load after item-list.css and task-list.css, which it overrides.

When to use

  • The Activity tab of a record, and the shortened axis on a record overview or in a quick preview.
  • Anywhere the question is "what has happened here, and what happens next".

When not to use

  • A plain list of one kind of item (tasks, e-mails, notes) — that is item-list or its specialisation, without the axis.
  • Columns the user compares — that is table.
  • A conversation — that is ai-assistant, or discussion, which is not built.

Anatomy

  • .tlx › .tlx-bar (category filter chip, add-activity action) › .tlx-body.
  • .tlx-body is the container query context (container: tlx / inline-size, threshold 640px) and the shared wrapper for every row.
  • Groups: details.tl-sec (plus is-today) › summary.tl-grp with h3.l, a count badge and .gcv chevron.
  • Rows: ul.il.il-md.il-embedded.tl-rail[role=list] (plus tl-today for the tinted block) with li.il-li › .il-row.
  • Event-specific cells inside .il-c: .tr (a single field transition), dl.chg › .chg-r (two or more fields), .tcard (a task, plus .done: priority glyph .tc-prio · state .tl-state · name · assignee · due — the same lead as the task list, except that priority is a bare glyph with no 24px box, so the card stays 46px), .tstack (several cards), .prods (offered products).
  • .il-nudge — the "no next step" prompt, with .ng target glyph, .tx and .acts.
  • Row flags: is-now (the single nearest row on the whole axis, decided by the server), is-taskev, is-taskcell, .il-meta.next (a time that has not happened yet).

Measured: rows 50px; the axis is a 1px line from ::before/::after; the today section tints head and rail alike.

Best practices

Do

  • Let the count decide the shape: one changed field goes inline as a transition, two or more open into the grid. Same for offered products. The source of the event never decides.
  • Title the event, not its subject: a task row is titled Úkol splněn, and the task's own name lives in the card.
  • Give future times .il-meta.next by the row's date, and is-now to exactly one row on the axis. They are different facts and a group cannot tell you either.
  • Keep sections as native <details> so they fold from the keyboard.

Don't

  • Draw a header on the aggregated task cell — it repeated the section title and only cost width (P282).
  • Use .tlx-narrow for the narrow layout; it is a filter-chip class. Narrow behaviour comes from the container width, nothing else.
  • Control <details> without guarding onToggle against your own state — the native event fires when React sets open too, and the sections then flip themselves back on mount.
  • Add a row pattern for a new event type; the cells inside .il-c are the extension point.

Content

  • Title = what happened, from i18n by event type — never the server's string, which is Czech regardless of locale.
  • Byline = who; meta = when, in the year convention (26. 8., 15. 10.).
  • Group labels are fixed: Dnes, Nejbližší akce, Naplánováno, Dříve.
  • item-list — the row this is built on.
  • task-list, note-card — the neighbours that fill its cells.
  • section-block — the frame around it on a record overview.
  • Stories: components--timeline--default, --variants, --sizes, --states, --with-icon-and-kbd, --composition.

Source: docs/timeline/timeline.usage.md · GitLab