-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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,
),
],
)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: falseto force one-sided at any width. - Pass
isCompactOverride: falseto 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.
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.
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.
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.
| 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. |
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). |
start · end — content-card placement hint for the two-sided layout only.
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.
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.
The two layout surfaces LayrzTimeline selects between. Exported independently in case a caller
needs one directly, though ordinary use goes through LayrzTimeline.
-
Spacing:
sp3for the gap between rows and between the marker column and the card. -
Colors:
fg3(default neutral marker when noaccentColoris set),sf2(card background),fg1/fg2/fg3(label/description/timestamp text). -
Typography:
label(entry label, timestamp),body(entry description).
Component Catalog,
Milestone 6
Last updated: 2026-08-28 (first documentation of this widget)
Made with ❤️ by Golden M, Inc.
- LayrzAnchoredPanel
- LayrzBottomSheet
- LayrzDialog
- LayrzDropdownMenu
- LayrzResponsiveModal
- LayrzPageTransition
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput
- LayrzSlider
- LayrzStepper