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-listor its specialisation, without the axis. - Columns the user compares — that is
table. - A conversation — that is
ai-assistant, ordiscussion, which is not built.
Anatomy
.tlx›.tlx-bar(category filter chip, add-activity action) ›.tlx-body..tlx-bodyis the container query context (container: tlx / inline-size, threshold 640px) and the shared wrapper for every row.- Groups:
details.tl-sec(plusis-today) ›summary.tl-grpwithh3.l, a count badge and.gcvchevron. - Rows:
ul.il.il-md.il-embedded.tl-rail[role=list](plustl-todayfor the tinted block) withli.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.ngtarget glyph,.txand.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.nextby the row's date, andis-nowto 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-narrowfor the narrow layout; it is a filter-chip class. Narrow behaviour comes from the container width, nothing else. - Control
<details>without guardingonToggleagainst your own state — the native event fires when React setsopentoo, and the sections then flip themselves back on mount. - Add a row pattern for a new event type; the cells inside
.il-care 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.
Related
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