Conversation timeline — accessibility

Roles & names

  • The cursor .ma-ov is role="slider", tabindex="0", named by labels.cursor. aria-valuemin 0, aria-valuemax the duration in seconds, aria-valuenow the cursor in seconds, aria-describedby points at the hidden table.
  • aria-valuetext is built from the cursor time, the phase, who speaks (labels.speaking), the mood phrase (labels.moodText, nearest point within 90 s, same as the bubble) and the kind of the nearest moment (within 60 s); empty parts are skipped, e.g. "12:30, Demo, mluví Jana, nálada: spíš pozitivní (+0,4), Bolest".
  • A moment chip is a <button> named "kind + time" (e.g. "Bolest 06:40"); the description is in title. In narrow charts the visible label is hidden, the name stays.
  • The moment card is role="group"; the open chip points to it with aria-describedby. Close is a <button> with an accessible name from labels; ▷ time is a <button> only when the host can seek.
  • The SVG, phases, lanes, axis, legend row, guides and the bubble are aria-hidden. Lane labels (name and talk share) stay ordinary text.
  • The full text alternative is the visually hidden table .acrm-sr (caption labels.summary; columns time, phase, speaker, moment with quote), sorted by time.
  • Mood is derived from the content of the conversation only, never from voice; the words describe the talk, not a person's emotion.

Keyboard

  • Tab reaches every moment chip first, then the cursor.
  • On the cursor: Arrow Left/Down and Right/Up move 10 s, Shift+arrow 60 s, PageUp/PageDown 5 min, Home/End to start/end. Enter or Space calls onSeek with the cursor time.
  • Focus on a chip opens its moment card; Tab moves into the card (▷ time, Close when pinned), Escape closes the card and returns focus to the chip, focus leaving the row closes it.
  • Focus places the cursor (at 0 if untouched) and shows the line and the bubble; blur hides them. A chip activates with Enter or Space (native button).
  • Focus ring: --ring, --line-thick, offset --sp-1 on the cursor (:focus-visible); chips use the foundation button ring.

Screen reader

  • On focus the slider name, value and aria-valuetext are read, then the table description on request. Every key step updates aria-valuenow and aria-valuetext.
  • The table is reachable in browse mode: phases first by time, moments with speaker and quote. In demo mode the counterpart quote is absent by design.
  • Sentiment is spoken as a word from moodWord plus the signed value, never as a colour.
  • Hover and the bubble have no announcement of their own; the bubble repeats what aria-valuetext says (plus the quote).

Contrast & motion

  • --sentiment-pos #12a594 / --sentiment-neg #c2620a in light, #12a594 / #d9760a in dark; WCAG 1.4.11 needs at least 3:1 on the surface, pinned by apps/web/src/ds/__tests__/sentiment-tokens.contract.test.ts.
  • The two hues differ (teal against orange), not in lightness. The sign never rests on colour alone: above the zero line is positive, below negative, the legend and the bubble say it in words.
  • Chart text uses --fg, --fg-secondary and --fg-muted; the axis ticks share the muted-label limit of the system.
  • All motion (curtain, lane growth, chip pop, cursor and bubble fade) lives inside @media (prefers-reduced-motion: no-preference); under reduced motion is-anim is never added and the cursor and bubble just appear. The animate prop turns the entrance off in print and snapshots (contract test reduced-motion.contract.test.ts).
  • Touch: the first tap on a chip opens and pins the moment card (no seek), Close and ▷ have 44 px targets (measured on dev 390: 44 px high). Known limits: cursor hover has no touch equivalent (a tap moves the cursor, shows the bubble and calls onSeek); the SVG scales with its container, so on a phone the shapes shrink while the values remain in the table.

Source: docs/meeting-analysis/conversation-timeline.a11y.md · GitLab