Content Type: Timeline
Changes to how this Type behaves are requested through a GitHub Issue and implemented, together with this reference page, through the repository's normal workflow. See /_docs/posts for how Content Types, Roles, and Rank work in general, and docs/Internal/type-doc-templates.md for the Content Type Doc template this file follows.
A Timeline groups an ordered chronology of Events under one optional Title. A Post may hold more than one independent Timeline, each addressed by its own Role — the primary use case is one Timeline containing several timed Events, but nothing stops a Post from having, say, a "Program History" Timeline and a separate "Key Milestones" Timeline side by side.
Fields
The Timeline container itself owns:
Title (optional) — distinguishes multiple Timelines on the same Post without misusing Role.
Precision (mandatory) — one of five options, applied to every Event's own timestamp in this Timeline:
Precision option Rendered form Year YYYYDate without year DD/MMTime HH:MMDate and time without year DD/MM HH:MMFull date and time DD/MM/YYYY HH:MMPrecision is a presentation parameter only — the stored timestamp always retains its full value regardless of which form is shown, and sorting always uses the stored timestamp, never the formatted string. The admin editor shows a live preview/example of the selected format.
Events — an ordered collection, at least one required. Each Event owns:
- Timestamp (mandatory) — a structured date-time.
- Label (key optional) — a short heading.
- Description (key optional) — full Markdown, rendered the same as any other Markdown content. It may reference/embed an eligible Link or Image Content Element from the same resolved Post Version through the existing
{{role}}/{{kind:role}}reference mechanism (see /_docs/markdown) — the referenced element stays a separate Content Element, never copied into the Timeline's own data. - Event rank — a stable, deterministic ordering tie-breaker (defaults to submission order), used whenever two Events share, or one is missing/invalid, a timestamp.
An Event is not its own Content Element — it has no Role/Rank/Reference of its own, and no independent public route or standalone page.
A missing or invalid Event timestamp is rejected outright by the admin's own Save validation (an actionable error naming which Event), rather than being accepted and silently misplaced later — but if one somehow reaches the database (e.g. a direct API call bypassing that check), public/Admin Preview rendering shows a safe fallback in its place and sorts it after every Event with a valid timestamp, never reordering the rest of the Timeline around it.
Semantic HTML and accessibility
A Timeline renders its optional Title, then its Events in deterministic order (stored timestamp ascending, event rank as the tie-breaker) — DOM/accessible reading order is always Title-then-Events regardless of viewport or visual layout. When the Timeline's own rendered width is wide enough, a short Timeline (at most six Events) lays its axis out horizontally, each Event an explicit, self-contained card below one shared line; a longer Timeline, or any Timeline whose own rendered column is narrower than that, always uses the vertical form. This switch reacts to the Timeline's own actual available width (a CSS container query), not the browser viewport — the Article reading column and the standalone full-page view both render narrower than the horizontal threshold in normal use, so the vertical form is what most visitors actually see; the horizontal form is reachable at a genuinely wide rendered width (e.g. a wide Admin Preview pane). The connecting line, per-Event marker, and horizontal-vs-vertical choice itself are purely decorative CSS and carry no unique meaning — none of them changes DOM order. Every Event's time and Label are always plain visible text, never hover-only. Print/PDF and no-JavaScript output always render the complete vertical form with every Event's full content (this app has no true SSR path — see docs/Internal/architecture.md — so this is guaranteed via plain server-agnostic markup/CSS, not a separate static-rendering pass). Reduced-motion preferences disable the Timeline's own non-essential hover transition.
Role, Rank, and Reference
Each Timeline Content Element carries the standard Role/Rank/Reference fields — see /_docs/posts. Role/Rank order a Post's Timelines relative to its other content, the same as any other Content Element; Reference addresses one specific Timeline for inline embedding, independent of Role. Event rank (above) is a separate, Timeline-local concern — it orders Events inside one Timeline, not Timelines against each other.
Editing
In the admin Post editor, the Timelines section holds one block per Timeline: Title, Precision (with a live rendered-format preview), Role, Rank, Reference, and a nested list of Events (each with its own Timestamp/Label/Description, addable/removable/reorderable). Every Post Type's editor shows the same Timelines section regardless of which Type is active. Editing any Event — including just reordering — publishes a genuinely new Timeline row: the container and all its Events version atomically, never independently.
An Event's Timestamp editing control is the browser's own date-time picker, which can only represent a 4-digit, non-negative year (0000-9999). A Timestamp already stored outside that range — e.g. a deep-past or far-future date entered via native-JSON import — shows in the editor as a read-only value instead of an editable control; everything else about that Event (Label, Description, its place in sort order) still edits normally, and public rendering, the standalone Content Element page, and JSON import/export all handle the full timestamp range regardless of this editor-only limitation.
Rendering contexts
Inline
{{role}}/{{kind:reference}} ({{timeline:role}} or {{timeline:reference}}) inserts the complete Timeline — optional Title, then every Event with its formatted time, Label, and fully rendered Markdown Description — at its authored position in the surrounding text.
Supplementary-region collection
Every eligible Timeline (one not already shown inline) appears in the shared Additional Information container (see /_docs/posts), each as its own visually distinct group — never merged, even when a Post holds more than one — showing only its optional Title and its Events as formatted time + Label, in Event order. Event Description, embedded Images, or any other rich Markdown content is never shown here; each group links to that Timeline's own full-page view instead.
End-of-Post listing
Not applicable in the old single-list sense — the shared Additional Information container above is Timeline's one grouped-listing context.
Post metadata-page traversal
Every Timeline Content Element the Post has ever held (current Version, or full history via /metadata/history) is enumerated with its Title, Precision, and full Events array as JSON-LD/native JSON — see /_docs/posts's Metadata page section. The native JSON export's own format version accepts both the current container shape and an older document's single-date shape on import, converting the latter into a one-Event container.
Standalone Content Element page
/_element/timeline/:id is a Timeline's own full-page view — the same complete Title+Events visual/responsive pattern as inline rendering, with the additional width a full page offers. Each Event's Description renders as plain Markdown; unlike the inline context, this standalone page has no owning Post in scope to resolve a {{...}} content reference against, so one appears as literal, unresolved text here (the same disclosed limitation Text's own standalone page already has). Gated exactly like the owning Post's own page. No route exists for an individual Event — only the Timeline as a whole.