Skip to content

LayrzTimeline

Kenny Mochizuki Escalona edited this page Aug 28, 2026 · 1 revision

LayrzTimeline

A vertical spine of dated events, rendered one-sided or two-sided.

Metadata Predecessor: None — no ThemedTimeline exists in layrz_theme Phase: M6 (pulled forward as an M4/M5 prerequisite) Domain: Display Primitive: Hand-rolled (Column/Row/IntrinsicHeight, no Material timeline widget) Status: Merged · Review required.


Overview

LayrzTimeline renders a caller-owned, ordered List<LayrzTimelineEntry> as a vertical spine of markers connected by lines, each with a content card. Two layouts are available:

  • One-sided (LayrzTimelineOneSidedSurface): every card renders to the right of a single left-hand spine.
  • Two-sided (LayrzTimelineTwoSidedSurface): cards alternate (or are explicitly placed) on either side of a centered spine.
LayrzTimeline(
  entries: [
    LayrzTimelineEntry(
      labelText: 'Order placed',
      timestampText: 'Aug 26, 2026',
      icon: MdiIcons.cartCheck,
    ),
    LayrzTimelineEntry(
      labelText: 'Shipped',
      descriptionText: 'Left the warehouse in Miami, FL',
      timestampText: 'Aug 27, 2026',
      accentColor: tokens.colors.info.shade500,
    ),
  ],
)

Auto-collapse below the compact breakpoint — a default, not an opt-in

The two-sided layout auto-collapses to one-sided below context.isCompact (viewport < 960px), by default. This is deliberate, not a bug to work around: a two-sided timeline straddling a spine either wraps its card text into unreadable single-word stacks, or the two columns visually merge into the spine, on a phone-width viewport — and either way, the meaning the two-sidedness carried (grouping or contrasting entries by side) is silently lost.

twoSided remains fully overridable in either direction:

  • Pass twoSided: false to force one-sided at any width.
  • Pass isCompactOverride: false to force two-sided below the breakpoint. This override exists primarily so the auto-collapse is unit-testable without faking a real viewport resize, and for an unusual embedding (a fixed-width pane inside a wide viewport) — it does not widen a genuinely narrow viewport, it only skips the automatic derivation for this widget's own layout choice.

Reading order follows chronology, not left-right position

In the two-sided layout, alternating cards zig-zag left/right across the spine. Reading order always follows entries' list order (chronology), never the visual left-then-right placement — each row carries an explicit OrdinalSortKey in its Semantics node so a screen reader always walks entries in list order regardless of which side a card visually lands on.

Deliberately no shared code with LayrzStepper

LayrzTimeline shares no layout or marker code with LayrzStepper. At most, the connector-line painting approach (a plain colored line, via LayrzTimelineConnector) is similar to the stepper's own connector — and that similarity is duplicated code, not a shared import. A stepper's markers encode a linear completed/active/pending state machine; timeline entries are arbitrary dated events with no such vocabulary, and coupling the two modules would be the wrong direction of dependency.

Markers are purely decorative (ExcludeSemantics) — the entry's own Semantics node carries the full announcement (label, description, and timestamp concatenated). Per WCAG 1.4.1, an entry's accentColor is never the only thing distinguishing it: vary icon or text content too, or instead.

No built-in editing

This widget has no built-in editing of entries — entries is a plain, caller-owned list. Adding, removing, or reordering an entry is done by rebuilding the list passed in, the same way any other declarative list widget in this design system works.

API

LayrzTimeline

Parameter Type Notes
entries List<LayrzTimelineEntry> Required. Order is both visual top-to-bottom order and chronological/semantics reading order — the widget does not parse or sort by timestampText.
twoSided bool Defaults to true. Whether to render the two-sided layout when the viewport is not compact. Does not override the compact-breakpoint collapse (see above).
isCompactOverride bool? Overrides the context.isCompact derivation. See the auto-collapse section above for when to use this.

LayrzTimelineEntry

An immutable data class (@immutable, copyWith, ==/hashCode).

Field Type Notes
labelText String Required. Always rendered and always included in the semantics label.
descriptionText String? Optional longer supporting text below the label.
timestampText String? Optional caller-formatted date/time text. Opaque display text — not parsed or used for sorting.
icon IconData? Optional icon rendered inside the marker. A plain dot renders when null.
accentColor Color? Optional accent for the marker and connector. Never the sole distinguishing feature per WCAG 1.4.1 — see above.
side LayrzTimelineSide? Which side the content card renders on, in the two-sided layout only. When null, sides alternate automatically by position. Has no effect in the one-sided layout.
content Widget? Optional extra widget rendered below the description line (e.g. an attachment chip or action button).

LayrzTimelineSide (enum)

start · end — content-card placement hint for the two-sided layout only.

LayrzTimelineMarker

The circular per-entry marker both layouts render. Always excluded from semantics — its color and icon are supplementary, and the entry's own Semantics node already carries the full meaning.

LayrzTimelineConnector

A single vertical line segment between two adjacent markers on the spine. Has no "progressed"/"neutral" state (unlike a stepper connector) — a timeline has no active/completed step to progress through.

LayrzTimelineOneSidedSurface / LayrzTimelineTwoSidedSurface

The two layout surfaces LayrzTimeline selects between. Exported independently in case a caller needs one directly, though ordinary use goes through LayrzTimeline.

Design tokens used

  • Spacing: sp3 for the gap between rows and between the marker column and the card.
  • Colors: fg3 (default neutral marker when no accentColor is set), sf2 (card background), fg1/fg2/fg3 (label/description/timestamp text).
  • Typography: label (entry label, timestamp), body (entry description).

Related documents

Component Catalog, Milestone 6


Last updated: 2026-08-28 (first documentation of this widget)

Clone this wiki locally