Decisions: Milestone
Append as you go, newest last. Record choices someone could reasonably question later, not every edit.
Its own family, not part of Timeline (2026-10-22)
- Decision:
MilestoneandMilestoneListinproposals/Milestone/. It started asTimelineMilestoneplus avariant='milestone'onTimeline(so the list had two modes) and was split out the same day.TimelineandTimeline Itemare as they were handed off. - Why: too many things called timeline; "timeline" now means the log of events and "milestone" the sequence of steps. The first reason for a separate item was anatomy: a card with a heading, a subtitle and a body, against a one-line row with a status icon and time, with almost no props in common. Splitting the list too keeps the ready-for-dev Timeline free of milestone-only props (
variant,subtitlePosition). Renaming the log (ActivityLog) was rejected: it touches 13 files, including handoffs. - Port impact: two new components. The shared
ol/liand spacing code is duplicated in a small list. Docs say "timeline of milestones" so a search for timeline still finds it.
Answers from Erika (2026-10-22)
Marker state: one boolean,
isActive(notdone/current/upcoming): the milestone in focus. Active has the accent fill and a ring andaria-current='step'; the rest are an empty circle in the line's color. An earlier version had three states and a neutral filled dot; both were replaced.Card: whether there is a card belongs to the list (
variant:cardorflat), and each milestone can override it, so it is not all or nothing. Reaches milestones through a React context. (AcardVariantprop was removed, see below.)Accent: the interactive link color, on the dot only. The subtitle is the default secondary color, not accent.
Heading:
headingLevelprop (default 3); the title ispds-heading-m.Subtitle, not date:
date/dateTimebecamesubtitle(a node), because it cannot always be a date.Subtitle at the start:
subtitlePosition='start'on the list puts it left of the line, baseline-aligned with the title, with a gutter (7xl). Below 480px (phone width) it falls back to under the title; it was 768px at first, which hid the layout in a narrow Storybook pane, so it was lowered (2026-10-22). On the list because the gutter belongs to it; the DOM order is the same either way.Dot placement: the dot sits inside the title (hidden from assistive tech), centered on its first line with
1lh, and is pulled onto the line by the card's border and padding and the list's inset.Active ring stays accent (2026-10-22): the grey ring was compared side by side and rejected. The comparison story and the
--milestone-active-ringvariable are removed.
Default Panel only (2026-10-22)
- Decision: removed
cardVariantfromMilestoneandMilestoneList(and theCardVariantsandMixedCardsstories). The card is the Panel with no variant (default), notraisedas before.hasCardstays (replaced byvariantbelow). - Why: Erika: use the default panel for now, other variants are not needed yet.
- Port impact: smaller API. A
cardVariantpass-through is easy to add back later through the context.
variant: 'card' | 'flat' instead of hasCard (2026-10-22)
- Decision:
hasCardis replaced byvariant(carddefault,flat) on both components; the list sets it and a milestone can override it. The modifier.pds-milestone--cardis unchanged. - Why: PDS uses string-union
variantprops for looks,hasCard={false}is a double negative, and other Panel variants can join the union later. - Port impact: none beyond the rename; nothing outside this proposal used
hasCard.
Open questions
- The inactive circle and the line are faint (same light grey); darken both through
--milestone-list-line-color? - Narrow phone widths and long subtitles are not checked. Dark mode: a11y panel run, no violations (2026-10-22).