Announcing View Changes in Data Apps

Permalink to "Announcing View Changes in Data Apps"

Data apps switch views constantly: table and chart, list and board, grouped and ungrouped, saved view A and saved view B, day and week. For sighted users the change is obvious — the whole area redraws. For screen reader users the switch is often silent beyond the control’s own state (“Board, tab, selected”) and they must explore to discover what the new view contains. The right announcement fills that gap without duplicating the control or stealing focus.

This guide belongs to tabs & view switchers for data views.

Spec reference

Permalink to "Spec reference"
  • SC 4.1.3 Status Messages (AA) — information about the result of an action that does not move focus — “42 rows, grouped by status” — is a status message.
  • SC 3.2.2 On Input (A) — switching views on selection is fine as long as it does not unexpectedly move focus or open new windows.
  • SC 2.4.2 Page Titled (A) — when a view switch changes the page’s purpose (a different report), update the title.
  • SC 4.1.2 Name, Role, Value (A) — the switcher’s own state is announced by the control; do not repeat it in a live region.
What to announce after a view switch Flow: the control announces its own state; the app adds only what changed in the content — counts, grouping, hidden columns — and updates the page title for major mode changes. What to announce after a view switchControl state"Board, tab, selected"announced by the control itselfContent summary"12 cards in 4 columns"status message, politeMajor mode change?different report or entityupdate document titleInitial load or restore?no user actionannounce nothing

When to use — and when not to

Permalink to "When to use — and when not to"

Announce when the view’s content differs in a way users cannot infer from the control: a different number of items, a different grouping, columns hidden or shown, a filter included in the view. Announce when the switch takes noticeable time (“Loading board…” then “Board loaded, 12 cards”).

Do not announce when the control’s state already says everything — switching a density radio from comfortable to compact needs no message. Do not announce when the app restores a view on page load; the page’s normal reading covers it.

The misapplication: a live region that repeats the tab name. Users hear “Board, tab, selected” and then “Board view” — twice the speech, no new information.

Echoing the control versus adding information Comparison: echo announcements repeat the control's name; useful announcements add counts, grouping and hidden columns. Echoing the control versus adding information✗ Echo"Switched to board view""Chart view""View changed"Repeated on page load✓ Information"12 cards in 4 columns: To do, Doing, Review,Done""Bar chart of 8 regions; table view alsoavailable""Grouped by owner, 6 groups, 3 columns hidden"Silent on load and restores

Annotated code example

Permalink to "Annotated code example"
const status = document.getElementById('view-status');   // role="status", present on load
let userInitiated = false;

switcher.addEventListener('pointerdown', () => { userInitiated = true; });
switcher.addEventListener('keydown', () => { userInitiated = true; });

async function onViewChange(view) {
  const announceAfter = userInitiated;
  userInitiated = false;
  if (view.slow && announceAfter) status.textContent = `Loading ${view.label}…`;
  await render(view);
  if (!announceAfter) return;                              // no announcements on load or restore
  status.textContent = summary(view);                      // only new information (SC 4.1.3)
  if (view.major) document.title = `${view.label} — Invoices — Acme`;  // SC 2.4.2
}

function summary(view) {
  switch (view.kind) {
    case 'board': return `${view.cardCount} cards in ${view.columns.length} columns: ${view.columns.join(', ')}.`;
    case 'chart': return `${view.chartType} of ${view.seriesCount} series. Table view also available.`;
    case 'table': return `${view.rowCount} rows${view.groupBy ? `, grouped by ${view.groupBy}` : ''}.`;
  }
}

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Event Expected announcement AT deviations
Select “Board” tab “Board, tab, selected, 2 of 3” then “12 cards in 4 columns: To do, Doing, Review, Done.” Status may be queued behind the tab announcement — expected
Select “Chart” “Chart, tab, selected” then “Bar chart of 8 regions…” —
Page load with saved view Normal page reading Nothing extra
Slow view “Loading board…” then summary Clear the loading message if the view loads under ~500 ms
Messages around one view switch Timeline: at 0 ms the tab announces itself; at 50 ms a loading message is skipped because the view is fast; at 300 ms the content summary is announced; the document title updates silently. Messages around one view switch0 msTab selected"Board, tab, selected, 2 of 3"50 msRender startsno loading message for fast views300 msRendered"12 cards in 4 columns…"300 msTitle"Board — Invoices — Acme", silent
One control announcement and one content summary.

Integration context

Permalink to "Integration context"

Tabs between table and chart are covered in the tabs pattern for switching table and chart views; saved views in saved views menus for data grids. Route-level changes in single-page apps follow managing route change focus in Angular and Vue. Keep summaries in the shared queue described in announcement queues for competing messages.

Gotchas

Permalink to "Gotchas"

Detecting user intent. Distinguish user-initiated switches from programmatic ones (URL restore, default view). The flag approach above is simple; a more robust one passes { source: 'user' } through your state store.

Summary length. Listing ten board columns is too long. Count them, and name them only if there are four or fewer.

Focus jumps. Some apps move focus to the first item of the new view. That is acceptable only when the user explicitly asked to “go to” the view; otherwise keep focus on the switcher.

Framework notes

Permalink to "Framework notes"

In React, put the announcer at the app root so a remount of the view does not recreate the live region; pass source in your view-change action. In Angular, view switches inside one route are usually component swaps — use the CDK LiveAnnouncer. In Vue, a watch on the view key with { flush: 'post' } runs after the DOM updates, which is when the summary is accurate.

// Vue: announce after DOM update, only for user switches
watch(() => store.view, (v, old) => { if (store.lastSource === 'user') announce(summary(v)); }, { flush: 'post' });

Worked example

Permalink to "Worked example"

A project tool offers List, Board and Timeline views. Switching to Board said “Board, tab, selected” and nothing else; a screen reader user had to explore to learn there were five columns and 38 cards, two of them blocked. The team added summaries computed from the rendered view — “38 cards in 5 columns. 2 blocked.” — and removed an older “View changed” message that had been firing on page load too. The Timeline view, which loads slowly, announces “Loading timeline…” first, but only when loading takes more than half a second.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Give every view a summary() function as part of its contract and let the view switcher component handle timing, intent detection and the live region. That keeps announcements consistent across views built by different teams.

FAQ

Permalink to "FAQ"
Is the tab or radio state not enough?

It says which view is selected, not what it contains. Users need the content summary to decide whether to explore.

Polite or assertive?

Polite. View changes are expected results of the user’s action.

Should loading states be announced?

Only when loading is slow enough to notice — more than about half a second. Otherwise the loading message and the result collide.

Should the URL change on view switch?

Usually yes, with replaceState for minor switches and pushState for major ones, so Back behaves sensibly.

What about views switched by keyboard shortcuts?

Treat them as user-initiated and announce the summary; the shortcut gives no visual cue at all.

How do I announce a change to a chart view without describing the whole chart?

Give the chart type, what it plots and the headline: “Line chart of daily orders over 30 days; highest on 14 September.” The chart’s own text alternative holds the detail.

Permalink to "Related"

← Back to Tabs & View Switchers for Data Views