The stops of one vehicle's trip, in order, on a rail in its line's colour: each stop's name, its time, the time real-time data expects there and how late the vehicle runs there, with the stops already passed set back and the next stop marked, as in a vehicle's detail panel. A cancelled call stays in the list, marked "Cancelled".
When to use
Use StopTimeline for the stops of one vehicle's trip, in order, with each stop's times and delay and the next stop marked, as in a vehicle's detail panel.
| Use instead | When |
|---|---|
| DepartureRow | The list is the departures from one stop, each its own line and destination |
- Svanemøllen PassedOn time
- Nordhavn Passed 1 min early
- ØsterportNext stop 2 min late
- Nørreport 3 min late
- Vesterport 3 min late
Props
| Name | Type | Default | Description |
|---|---|---|---|
stops* | StopTimelineStop[] | (required) | The trip's stops, first to last: each one's name, and optionally its timetabled time, its expected time, its delay in minutes and whether the call is cancelled. |
line | 'm1' | 'm2' | 'm3' | 'm4' | 'a' | 'b' | 'c' | 'e' | 'f' | 'h' | 'bus' | 'regional' | — | The line or mode whose colour the rail and the markers take, as StationIcon's line. Left out, the rail and markers are neutral. |
nextStopIndex | number | — | The index in stops of the vehicle's next stop. The stops before it are passed and the ones after it upcoming; an index past the last stop means the trip is over and every stop passed. Left out, the vehicle's place is unknown and no stop is passed or next. |
nextStopLabel | string | The locale's | Overrides "Next stop" |
passedLabel | string | The locale's | Overrides "Passed", heard after a passed stop's name |
cancelledLabel | string | The locale's | Overrides "Cancelled", seen under a cancelled stop's name and heard after it. |
expectedTimeLabel | string | The locale's | Overrides "Expected {time}", heard in place of a stop's "~" and expected time, "Expected 08:16"; {time} is the expected time |
minutesUnitLabel | string | The locale's | Overrides "min", the unit after each delay's figure; given to every Delay |
minutesLateLabel | string | The locale's | Overrides "min late", heard after the number when late; given to every Delay |
minutesEarlyLabel | string | The locale's | Overrides "min early", heard after the number when early; given to every Delay |
onTimeLabel | string | The locale's | Overrides "On time"; given to every Delay |
delayUnknownLabel | string | The locale's | Overrides "Delay unknown", heard in place of "—"; given to every Delay |
className | string | — | Merged after pn-StopTimeline on the root |
style | CSSProperties | — | Reaches the root |
ref | Ref<HTMLOListElement> | — | Reaches the root |
stops (required)StopTimelineStop[], default (required)- The trip's stops, first to last: each one's name, and optionally its timetabled time, its expected time, its delay in minutes and whether the call is cancelled.
line'm1' | 'm2' | 'm3' | 'm4' | 'a' | 'b' | 'c' | 'e' | 'f' | 'h' | 'bus' | 'regional', default —- The line or mode whose colour the rail and the markers take, as StationIcon's
line. Left out, the rail and markers are neutral. nextStopIndexnumber, default —- The index in
stopsof the vehicle's next stop. The stops before it are passed and the ones after it upcoming; an index past the last stop means the trip is over and every stop passed. Left out, the vehicle's place is unknown and no stop is passed or next. nextStopLabelstring, default The locale's- Overrides "Next stop"
passedLabelstring, default The locale's- Overrides "Passed", heard after a passed stop's name
cancelledLabelstring, default The locale's- Overrides "Cancelled", seen under a cancelled stop's name and heard after it.
expectedTimeLabelstring, default The locale's- Overrides "Expected {time}", heard in place of a stop's "~" and expected time, "Expected 08:16";
{time}is the expected time minutesUnitLabelstring, default The locale's- Overrides "min", the unit after each delay's figure; given to every Delay
minutesLateLabelstring, default The locale's- Overrides "min late", heard after the number when late; given to every Delay
minutesEarlyLabelstring, default The locale's- Overrides "min early", heard after the number when early; given to every Delay
onTimeLabelstring, default The locale's- Overrides "On time"; given to every Delay
delayUnknownLabelstring, default The locale's- Overrides "Delay unknown", heard in place of "—"; given to every Delay
classNamestring, default —- Merged after
pn-StopTimelineon the root styleCSSProperties, default —- Reaches the root
refRef<HTMLOListElement>, default —- Reaches the root
Use it
import { StopTimeline } from '@mylder/perron/patterns'Ask your agent
search_docs({ query: "StopTimeline" })Keyboard and ARIA
No Base UI part, so nothing from Base UI here: the timeline is read, not operated. It takes no focus, and no key does anything.
| Key | Does | From |
|---|---|---|
| None | The timeline is not focusable or operable; Tab passes it by | Perron |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | list, from the native <ol> | None of its own; the caller's aria-labelledby, such as the detail panel's heading, when it gives one | Its number of items, from the <ol> |
stop | listitem, from the native <li> | Its text, read in order: the name, "Passed" or "Next stop", "Cancelled", the time, "Expected" and the expected time, the delay's words | aria-current="step" on the next stop |
rail and marker | hidden, aria-hidden="true" | None: decorative; the words say what they show | None |
next | text | The owned string "Next stop" | None |
passed | text, visually hidden | The owned string "Passed" | None |
cancelled | text | The owned string "Cancelled"; the strike-through is not read, so the word says it | None |
time | time, from the native <time> | Its text, "08:14", with dateTime set to the same HH:MM | None |
expected | generic text, holding a time, from the native <time> | The owned string "Expected {time}" filled with the time, "Expected 08:16", visually hidden; the "~" and the <time>, its dateTime the same HH:MM, are aria-hidden, so the time is heard once | None |
delay | Delay's own: generic text | Delay's own words: "3 min late", "On time", "Delay unknown" | None |
A next stop reads "Nørreport, Next stop, 08:14, 3 min late", and a passed one "Forum, Passed, 08:10, On time". With an expected time it reads "Nørreport, Next stop, 08:14, Expected 08:17, 3 min late": the word "Expected" tells the ear what the "~" tells the eye, as the state's word does for DepartureRow's "~". A cancelled call reads "Vesterport, Cancelled, 08:16": "Cancelled", read before the time, tells the ear what the line through it tells the eye, as in DepartureRow's "5C, Husum Torv, Cancelled, 4 min, 08:14".
The next stop's list item carries aria-current="step" beside its "Next stop" words: it is the attribute ARIA gives for the current step in a sequence, so a screen reader names the stop as current when it lands on it from anywhere in the list, while the words say why.
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the filled marker and the "Next stop" words say which stop is next, heard as "Next stop" and
aria-current="step"; the set-back colour of a passed stop is heard as "Passed"; a cancelled call's danger colour and struck-through times are heard as "Cancelled", before the times (CR-14); the "~" before an expected time is heard as "Expected", with the time (CR-15); the rail and the line's colour are decoration, the line named in words by the panel around the timeline; each Delay says its delay in words; a stop with notimeordelayshows and says nothing for it; with nonextStopIndex, nothing is set back or marked, and nothing is said - Every landmark and named region has an accessible name: none here; the root is a list, not a region, and takes the caller's
aria-labelledbywhen the panel names it - Every default label can be overridden:
nextStopLabel,passedLabel,cancelledLabelandexpectedTimeLabel, and Delay's five,minutesUnitLabel,minutesLateLabel,minutesEarlyLabel,onTimeLabelanddelayUnknownLabel - Every shape the type accepts renders, in every variant: an empty
stopsrenders an empty list; one stop renders with no rail; each stop with and withouttime,expectedanddelay, anddelayas a number andnull; a stop with only anexpected, and an emptyexpected, which shows nothing;cancelledtrueandfalse, on a passed, a next and an upcoming stop, with and without anexpected; a trip with noexpectedkeeps no column for one;nextStopIndexleft out, at the first stop, in the middle, at the last stop and past it, negative or not a whole number (CR-2); everylinevalue and none; a long name wraps under itself - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; nothing animates or collapses. Therailandmarkerarearia-hiddenbecause they are decoration, not copies - Native semantics first:
<ol>and<li>for the ordered stops,<time>for each time and each expected time,aria-currentthe one attribute ARIA adds, andaria-hiddenon the decoration and the "~". A cancelled call's times are struck through by CSS, not wrapped in<s>or<del>; the word "Cancelled" carries it - Focus moves in reading order and returns on close: the timeline takes no focus
- Every target is at least
size.target-minsquare: no target; the stops are not pressed (CR-8) - Loops stop when the system asks for less motion: none here; nothing moves
Owned strings
The stop names, times, expected times and delays are the caller's data; the words of a stop's place in the trip, "Cancelled" and "Expected" are the pattern's own, and Delay's words are Delay's, overridable here through the props above. Each word is a text run of its own, so expectOwnedStrings reads it as a dictionary value, and the heard expected time is one template filled with the time, which expectOwnedStrings matches against the template.
| Key | English | Override | Seen or heard where |
|---|---|---|---|
nextStop | "Next stop" | nextStopLabel | next, seen under the next stop's name and read after it |
passed | "Passed" | passedLabel | passed, heard after each passed stop's name; never seen |
cancelled | "Cancelled" | cancelledLabel | cancelled, seen under a cancelled stop's name and read after it; DepartureRow's key, reused |
expectedTime | "Expected {time}" | expectedTimeLabel | expected, heard in place of each "~" and expected time, "Expected 08:16"; never seen; new |
minutesUnit | "min" | minutesUnitLabel | Each delay, as Delay's spec gives it; an existing key in packages/perron/src/locale/en.ts |
minutesLate | "min late" | minutesLateLabel | Each delay, as Delay's spec gives it; an existing key |
minutesEarly | "min early" | minutesEarlyLabel | Each delay, as Delay's spec gives it; an existing key |
onTime | "On time" | onTimeLabel | Each delay, as Delay's spec gives it; an existing key |
delayUnknown | "Delay unknown" | delayUnknownLabel | Each delay, as Delay's spec gives it; an existing key |
nextStop and passed are new, each with a JSDoc line like its neighbours in en.ts, and agents write their Danish, Swedish and Norwegian. Neither is a data-state, delay or disruption word, so neither joins content design's table.
cancelled is DepartureRow's key, "Cancelled", already in content design's table, whose JSDoc names a trip's stop as well as a departure: a cancelled call and a cancelled departure say the same word on every screen. expectedTime came with the expected time, "Expected {time}", with the Danish "Forventet {time}", the Swedish "Beräknad {time}" and the Norwegian "Forventet {time}": a template filled by interpolate, never the word set beside the time in code, as the contract's Words asks of a string that wraps the caller's words, so a locale can put the time where its grammar needs it. It is named for what it says, as DisruptionCard's fromTime and untilTime are. It is its own word, not the data state's "Estimated": "Estimated" says how a vehicle was placed, and an expected time can come from a live vehicle. Like passed it is heard and never seen, since the "~" says it to the eye, and it is not a data-state, delay or disruption word, so it does not join content design's table.
The next stop shows "Next stop" in words under its name, as well as its filled marker, and a passed stop says "Passed" to the ear only: StationIcon's spec asks that a filled stop's meaning be said in words beside it, and a sighted rider learns the filled marker from the words; a passed stop's set-back colour is plain to the eye in its place in the list, so "Passed" in every passed row would only crowd it, while the ear, which can't see the colour, hears it.
Do and don't
For a caller using the pattern, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Name the line in words around the timeline, such as in the panel's header with a LineBadge, because the rail's colour is decoration | Rely on the rail's colour to say which line runs the trip: colour is never alone (Accessibility) | review |
| DD-2 | must | Leave nextStopIndex out when no real-time data places the vehicle, so no stop claims to be next | Guess a next stop from the timetable and pass it: it claims more certainty than the data has (Content design) | review |
| DD-3 | must | Pass delay: null for a stop whose delay no real-time data says, and leave delay out where no delay belongs | Pass 0 for an unknown delay: "On time" claims a certainty the data doesn't have | review |
| DD-4 | must | Give the timetabled time and let Delay say how late the vehicle runs; give the expected time as expected where real-time data says it | Pass the expected time as time as well as a delay: the rider adds the delay twice | review |
| DD-5 | must | Keep a cancelled call in stops with cancelled: true, where the vehicle would have called | Drop it from the list: a rider waiting there reads its absence as a stop not yet shown | review |
| DD-6 | must | Leave expected out where no real-time data gives one | Copy the timetabled time into expected: the "~" claims an estimate the data doesn't have | review |
| DD-7 | must | Point the root's aria-labelledby at the panel heading that names the trip | Wrap the timeline in a live region: every update rereads the whole trip | review |
| DD-8 | should | Pass only the stops the screen needs, such as the next few, when the trip is long | Restyle the markers or the rail's width through className, which breaks the ring's and the rail's matching weight | review |