Skip to the content
DelaysAffected lines
  • F

Signal fault at Danshøj

Trains between Flintholm and Ny Ellebjerg run slower than planned.

Variants and states

RestHoverPressedFocus
Default
DelaysAffected lines
  • F

Signal fault at Danshøj

Trains between Flintholm and Ny Ellebjerg run slower than planned.
DelaysAffected lines
  • F

Signal fault at Danshøj

Trains between Flintholm and Ny Ellebjerg run slower than planned.
DelaysAffected lines
  • F

Signal fault at Danshøj

Trains between Flintholm and Ny Ellebjerg run slower than planned.
DelaysAffected lines
  • F

Signal fault at Danshøj

Trains between Flintholm and Ny Ellebjerg run slower than planned.
Default
  • Rest
    DelaysAffected lines
    • F

    Signal fault at Danshøj

    Trains between Flintholm and Ny Ellebjerg run slower than planned.
  • Hover
    DelaysAffected lines
    • F

    Signal fault at Danshøj

    Trains between Flintholm and Ny Ellebjerg run slower than planned.
  • Pressed
    DelaysAffected lines
    • F

    Signal fault at Danshøj

    Trains between Flintholm and Ny Ellebjerg run slower than planned.
  • Focus
    DelaysAffected lines
    • F

    Signal fault at Danshøj

    Trains between Flintholm and Ny Ellebjerg run slower than planned.

Props

NameTypeDefaultDescription
severity*'delay' | 'data' | 'diversion' | 'planned' | 'notice'(required)The kind of disruption, shown by the SeverityLabel in the card's header: delay for vehicles running late, diversion for a changed route, planned for planned works, data for a problem with the real-time data feed, notice for a service notice whose message does not say which of the others it is.
linesreadonly DisruptionCardLine[]—The lines the disruption touches, in the order to show them, each as a LineBadge: line gives its colours and name the short name riders know, "M2", "5C". Leave it out, or pass an empty array, for a disruption across the network, such as a data feed problem.
title*ReactNode(required)What is happening, in a few words: "Signal fault at Kongens Nytorv". Required. Replaces the HTML title attribute.
childrenReactNode—The sentence under the title: where, why and for how long, and how sure we are. "M2 and M3 trains between Kongens Nytorv and Christianshavn run slower than planned." Left out, or a value that renders nothing (null, a boolean, '' or an array of only such values), it renders no description
stopsDisruptionCardStretch | readonly string[]—The stops the disruption affects, under the sentence: a list of stop names, in the order to show them, "Affects Nørreport, Kongens Nytorv and 3 more", or a stretch by its two ends, "Between Hellerup and Klampenborg". A list names three stops at most: past three it names the first two and counts the rest. Leave it out, or pass an empty array, to show no stops.
timeDisruptionCardTime—When the disruption runs, under the stops: from alone reads "From 08:03", to alone "Until 22:00", and both "24 to 25 October", each value written as the caller formats it. Leave it out, or give neither, to show no time.
actionReactNode—One link or button at the foot of the card, such as a LinkButton "Show on map". Leave it out when the card is itself a link through render: links and buttons can't nest.
affectedLinesLabelstringThe locale'sOverrides "Affected lines", the words screen readers hear just before the list of line badges
affectsStopsLabelstringThe locale'sOverrides "Affects {stops}", the words around the stops; {stops} is the names
stopListEndLabelstringThe locale'sOverrides "{stops} and {stop}", which joins the last stop name to those before it
moreStopsLabelstringThe locale'sOverrides "{stops} and {count} more", the list cut short past three stops; {count} is how many it leaves out, 2 or more
betweenStopsLabelstringThe locale'sOverrides "Between {from} and {to}", the words around a stretch's two ends
fromTimeLabelstringThe locale'sOverrides "From {time}", the time with a start and no end
untilTimeLabelstringThe locale'sOverrides "Until {time}", the time with an end and no start
timeRangeLabelstringThe locale'sOverrides "{from} to {to}", the time with a start and an end
classNamestring—Reaches the root; className is merged after pn-DisruptionCard
styleCSSProperties—Reaches the root; className is merged after pn-DisruptionCard
refRef<HTMLElement>—Reaches the root element, whichever element render makes it
slotProps{ header?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> | undefined; severity?: Omit<SeverityLabelProps, "severity"> | undefined; lines?: DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement> | undefined; title?: Omit<TextProps, "children" | "variant"> | undefined; description?: Omit<TextProps, "children" | "variant"> | undefined; stops?: Omit<TextProps, "children" | "variant"> | undefined; time?: Omit<TextProps, "children" | "variant"> | undefined; action?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> | undefined; }—Props for each part: header, severity, lines, title, description, stops, time, action.
severity (required)'delay' | 'data' | 'diversion' | 'planned' | 'notice', default (required)
The kind of disruption, shown by the SeverityLabel in the card's header: delay for vehicles running late, diversion for a changed route, planned for planned works, data for a problem with the real-time data feed, notice for a service notice whose message does not say which of the others it is.
linesreadonly DisruptionCardLine[], default —
The lines the disruption touches, in the order to show them, each as a LineBadge: line gives its colours and name the short name riders know, "M2", "5C". Leave it out, or pass an empty array, for a disruption across the network, such as a data feed problem.
title (required)ReactNode, default (required)
What is happening, in a few words: "Signal fault at Kongens Nytorv". Required. Replaces the HTML title attribute.
childrenReactNode, default —
The sentence under the title: where, why and for how long, and how sure we are. "M2 and M3 trains between Kongens Nytorv and Christianshavn run slower than planned." Left out, or a value that renders nothing (null, a boolean, '' or an array of only such values), it renders no description
stopsDisruptionCardStretch | readonly string[], default —
The stops the disruption affects, under the sentence: a list of stop names, in the order to show them, "Affects Nørreport, Kongens Nytorv and 3 more", or a stretch by its two ends, "Between Hellerup and Klampenborg". A list names three stops at most: past three it names the first two and counts the rest. Leave it out, or pass an empty array, to show no stops.
timeDisruptionCardTime, default —
When the disruption runs, under the stops: from alone reads "From 08:03", to alone "Until 22:00", and both "24 to 25 October", each value written as the caller formats it. Leave it out, or give neither, to show no time.
actionReactNode, default —
One link or button at the foot of the card, such as a LinkButton "Show on map". Leave it out when the card is itself a link through render: links and buttons can't nest.
affectedLinesLabelstring, default The locale's
Overrides "Affected lines", the words screen readers hear just before the list of line badges
affectsStopsLabelstring, default The locale's
Overrides "Affects {stops}", the words around the stops; {stops} is the names
stopListEndLabelstring, default The locale's
Overrides "{stops} and {stop}", which joins the last stop name to those before it
moreStopsLabelstring, default The locale's
Overrides "{stops} and {count} more", the list cut short past three stops; {count} is how many it leaves out, 2 or more
betweenStopsLabelstring, default The locale's
Overrides "Between {from} and {to}", the words around a stretch's two ends
fromTimeLabelstring, default The locale's
Overrides "From {time}", the time with a start and no end
untilTimeLabelstring, default The locale's
Overrides "Until {time}", the time with an end and no start
timeRangeLabelstring, default The locale's
Overrides "{from} to {to}", the time with a start and an end
classNamestring, default —
Reaches the root; className is merged after pn-DisruptionCard
styleCSSProperties, default —
Reaches the root; className is merged after pn-DisruptionCard
refRef<HTMLElement>, default —
Reaches the root element, whichever element render makes it
slotProps{ header?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> | undefined; severity?: Omit<SeverityLabelProps, "severity"> | undefined; lines?: DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement> | undefined; title?: Omit<TextProps, "children" | "variant"> | undefined; description?: Omit<TextProps, "children" | "variant"> | undefined; stops?: Omit<TextProps, "children" | "variant"> | undefined; time?: Omit<TextProps, "children" | "variant"> | undefined; action?: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> | undefined; }, default —
Props for each part: header, severity, lines, title, description, stops, time, action.

Use it

import { DisruptionCard } from '@mylder/perron/patterns'

Ask your agent

search_docs({ query: "DisruptionCard" })

Keyboard and ARIA

At rest DisruptionCard is text: it takes no focus and no key does anything. Made a link through render={<a href="…" />} (CR-8) it is one tab stop, with the browser's own keys. Its action is the caller's link or button, one tab stop with its own keys; the card around it stays unfocusable.

KeyDoesFrom
noneAt rest the card is not focusable or operablePerron
TabMoves focus to the card, when render makes it a linkThe browser, on <a href>
EnterFollows the link, when render makes the card a linkThe browser, on <a href>
TabMoves focus to the action's link or button, when givenThe caller's component
ElementRoleAccessible name fromStates announced
rootnone: a generic <div>; listitem, article or link through renderNot named at rest; as a link, its whole text content: the kind, "Affected lines", each line's short name, the title, the sentence, the stops and the timenone
severitynone: SeverityLabel's <span>, its icon aria-hiddenIts text, the kind's owned name: "Delays", "Diversions", "Planned works", "Data feed problems", "Service notice"none
lineslistNot named: the owned string "Affected lines", overridden by affectedLinesLabel, is read just before it as VisuallyHidden textnone
linelistitemIts text, the line's short name from linesnone
titleParagraph, or a heading when slotProps.title.render makes it oneIts text, from titlenone
descriptionnone: a generic <div>Its text, from childrennone
stopsParagraphIts text: the owned words with the names from stops, "Affects Nørreport, Kongens Nytorv and 3 more"none
timeParagraphIts text: the owned words with the ends from time, "From 08:03"none
actionnone: a generic <div>; the caller's link or button insideThe caller's link or button, named by its own text, "Show on map"none

The line badges are an unnamed list after a new owned string, "Affected lines", in a VisuallyHidden span inside the header, so a screen reader hears "Affected lines, list, 2 items, M2, M3" rather than two bare names after the kind: the eye reads the badges' place in the card as "these lines are affected", and the ear needs the same in words, as the contract's first checklist line asks. The words are text, not the list's aria-label, because a name replaces the content it names: a card made a link takes its name from its text, and an aria-label on the list would put "Affected lines" in that name in place of "M2" and "M3".

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 kind is SeverityLabel's word as well as its icon and colour; the badges' place in the card is said by "Affected lines", read just before the list, and each badge's colour by its short name, so a card made a link is named with the kind, "Affected lines", every line's short name and the title; with no lines, neither the words nor the list renders and nothing is announced for them. A list of stops cut short says "and 3 more" to the eye and the ear alike, and a stretch says both its ends.
  • Every landmark and named region has an accessible name: the card is no landmark by default; a caller who renders it as <article> or <section> names it from its title, through aria-labelledby and slotProps.title.id. The lines list is no region and has no name: its owned string is read just before it.
  • Every default label can be overridden: affectedLinesLabel, affectsStopsLabel, stopListEndLabel, moreStopsLabel, betweenStopsLabel, fromTimeLabel, untilTimeLabel and timeRangeLabel, and SeverityLabel's delaysLabel, diversionsLabel, plannedWorksLabel, dataFeedProblemsLabel and serviceNoticeLabel through slotProps.severity.
  • Every shape the type accepts renders, in every variant: title and children render a string, an element and a list alike, the title as a <p> holds phrasing content and the sentence as a <div> holds block content too; lines renders none, one and many, wrapping onto new rows; stops renders none, one, two, three and more than three names, and a stretch; time renders a start, an end and both, each a string or an element such as <time>; action renders the caller's element; every severity has its story.
  • A hidden copy kept for animation is aria-hidden, and a collapsed region is inert: none here; the card does not animate. SeverityLabel's icon is aria-hidden because its word speaks for it.
  • Native semantics first: a <ul> of <li> for the lines, a <p> for the title made a native h2 to h6 through render, a <div> for the sentence, and a native <a href> when the caller makes the card a link; no ARIA attribute, "Affected lines" being VisuallyHidden text.
  • Focus moves in reading order and returns on close: at rest the card takes no focus, and its action, last in the card, is the one tab stop inside it; as a link it is one tab stop in the document's order, and it opens or closes nothing.
  • Every target is at least size.target-min square: at rest none but the action, whose size is the caller's component's; as a link the whole card is the target, far larger than size.target-min.
  • Loops stop when the system asks for less motion: none here; the card has no motion.

Owned strings

The card shows SeverityLabel's name for its kind, which SeverityLabel owns and reads itself, and adds strings of its own: the words said before the list of lines, and the words around the stops and the time. Every other word comes from the caller, through title, children, lines, the names in stops, the ends of time and action.

The words around the stops and the time are owned strings with {name} placeholders, filled by interpolate (the contract's Words), so each locale puts the names and the times where its grammar needs them. The names a list shows are joined by ", ", as all four languages join a list, and the last by stopListEnd; a list past three names the first two and moreStops counts the rest, always 2 or more, so no locale needs a singular "1 more".

KeyEnglishOverrideSeen or heard where
affectedLines"Affected lines"affectedLinesLabelheader, VisuallyHidden text just before the lines list, "Affected lines, list, 2 items"; never seen; new
delays"Delays"slotProps.severity.delaysLabelseverity when severity='delay', seen and read; SeverityLabel's, the key en.ts has
diversions"Diversions"slotProps.severity.diversionsLabelseverity when severity='diversion'; SeverityLabel's, the key en.ts has
plannedWorks"Planned works"slotProps.severity.plannedWorksLabelseverity when severity='planned'; SeverityLabel's, the key en.ts has
dataFeedProblems"Data feed problems"slotProps.severity.dataFeedProblemsLabelseverity when severity='data'; SeverityLabel's, the key en.ts has
serviceNotice"Service notice"slotProps.severity.serviceNoticeLabelseverity when severity='notice'; SeverityLabel's, new with it
affectsStops"Affects {stops}"affectsStopsLabelstops when stops is a list, around the names; new
stopListEnd"{stops} and {stop}"stopListEndLabelstops, joining the last of two or three names to those before it; new
moreStops"{stops} and {count} more"moreStopsLabelstops, a list of four or more cut to its first two names and the count of the rest; new
betweenStops"Between {from} and {to}"betweenStopsLabelstops when stops is a stretch; new
fromTime"From {time}"fromTimeLabeltime when only time.from is given; new
untilTime"Until {time}"untilTimeLabeltime when only time.to is given; new
timeRange"{from} to {to}"timeRangeLabeltime when both ends are given; new

affectedLines joins packages/perron/src/locale/en.ts with the JSDoc "A disruption: said just before the list of lines it affects, heard by screen readers only.", and its Danish, Swedish and Norwegian in da.ts, sv.ts and nb.ts: "Berørte linjer", "Berörda linjer" and "Berørte linjer".

The stops' and the time's strings join them with a JSDoc each that names its placeholders, in Danish, Swedish and Norwegian:

KeyDanishSwedishNorwegian Bokmål
affectsStops"Berører {stops}""Berör {stops}""Berører {stops}"
stopListEnd"{stops} og {stop}""{stops} och {stop}""{stops} og {stop}"
moreStops"{stops} og {count} andre""{stops} och {count} till""{stops} og {count} andre"
betweenStops"Mellem {from} og {to}""Mellan {from} och {to}""Mellom {from} og {to}"
fromTime"Fra {time}""Från {time}""Fra {time}"
untilTime"Indtil {time}""Fram till {time}""Fram til {time}"
timeRange"{from} til {to}""{from} till {to}""{from} til {to}"

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.

RuleLevelDoDon'tChecked by
DD-1mustName the lines in words in the title or the sentence, "M2 and M3 trains…", as well as in the badges.Let the badges be the only mention of a line: five official badge pairs miss 4.5:1, and LineBadge asks for the name in words beside them.review
DD-2shouldSay what is happening, then where, how long and how sure we are, in plain sentences: "Signal fault at Kongens Nytorv" over its sentence.Repeat the kind in the title ("Delays on M2"): the SeverityLabel already says it, in the content-design word every screen shares.review
DD-3mustSet a list of disruptions as a <ul> of cards, each rendered as <li>, or make each card a link with render={<a href="…" />} to open the line.Make the card a <button>, or put a link or button inside a card that is itself a link: the card's blocks are invalid in a button, and links can't nest.review
DD-4mustMake the title a heading with slotProps.title.render at the level the panel around it needs.Pass a heading element as title: the title part is a <p> until render swaps it, and a heading inside a <p> is invalid.review
DD-5shouldLeave lines out for a disruption across the whole network, such as a data feed problem.Pass every line on the network, or a placeholder badge, to fill the header.review
DD-6shouldGive the stops and the time as data, stops and time, from the message's own fields.Write "Affects …" or "From …" into the sentence yourself: the words then differ from every other card's, and from the other languages'.review
DD-7mustGive severity='notice' to a message whose fields don't say which kind it is.Show an unread kind as planned works, or guess one from the message's words.review
DD-8mustGive one action, a LinkButton or Button, such as "Show on map", when the card is not itself a link.Give an action to a card made a link, or put several actions in it: links can't nest, and one action keeps the card one decision.review
DD-9shouldPut DisruptionCards inside a panel, which supplies the glass or opaque surface they sit on.Tint or stripe the card in the kind's colour with className: the colour belongs to the SeverityLabel and never carries meaning alone.review