Recalculation Announcements for Dependent Cells
Permalink to "Recalculation Announcements for Dependent Cells"In a spreadsheet, changing one number can change a hundred others. A sighted user sees the totals flicker and the chart redraw. A screen reader user hears the edit committed and nothing else — unless they navigate to every dependent cell to check. For financial and planning tools, that silence is risky: the user does not learn that their change pushed the budget over its limit.
This guide shows how to summarise recalculation without flooding the user. It belongs to spreadsheet-style editing grids.
Spec reference
Permalink to "Spec reference"- SC 4.1.3 Status Messages (AA) — changes in content that are not given focus must be announced if they are status information. A recalculated total is exactly that.
- SC 3.3.4 Error Prevention (Legal, Financial, Data) (AA) — for financial submissions, users must be able to review changes; a “what changed” list supports that review.
- SC 1.4.1 Use of Color (A) — a flash of colour on changed cells is not enough on its own.
- SC 2.2.2 Pause, Stop, Hide (A) — flashing highlights must stop within five seconds and must not flash more than three times a second (SC 2.3.1).
When to use — and when not to
Permalink to "When to use — and when not to"Announce recalculation when an edit changes cells other than the one edited — totals, derived columns, cross-sheet references. Mention by name only the cells that matter: designated totals, cells with conditional thresholds (“over budget”), and dependents visible in the viewport.
Do not announce every changed cell. “Cell F4 changed to 3,220. Cell F5 changed to …” for forty cells takes minutes to hear and nobody waits for it. Do not announce recalculation at all for edits with no dependents.
The misapplication: putting every recalculated cell into a polite live region as it updates. Screen readers queue some, drop some, and the user hears a random subset in a random order.
Annotated code example
Permalink to "Annotated code example"// After commit: sheet.recalculate returns [{ ref, label, before, after, isTotal, visible, threshold }]
function announceRecalc(editRef, changes) {
const others = changes.filter(c => c.ref !== editRef && c.before !== c.after);
if (!others.length) return;
const ranked = others.sort((a, b) =>
(b.threshold - a.threshold) || (b.isTotal - a.isTotal) || (b.visible - a.visible));
const named = ranked.slice(0, 2).map(c =>
`${c.label} ${c.after}, was ${c.before}${c.threshold ? `, ${c.threshold}` : ''}`);
const rest = others.length - named.length;
const msg = `Recalculated: ${named.join('; ')}` + (rest > 0 ? `; and ${rest} other cells.` : '.');
// After the commit confirmation, not in the same tick (SC 4.1.3)
setTimeout(() => { status.textContent = msg; }, 150);
lastChanges = others; // for "what changed"
flash(others.filter(c => c.visible)); // visual cue, under 5 s (SC 2.2.2)
}
/* Changed cells: outline plus a small marker — not colour alone (SC 1.4.1) */
.cell.recalc { outline: 2px dashed #155c38; }
.cell.recalc::after { content: "↻"; margin-inline-start: .25em; }
@media (prefers-reduced-motion: reduce) { .cell.recalc { transition: none; } }
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Event | Expected announcement | AT deviations |
|---|---|---|
| Commit B4 | “B4 is now 1,500” then “Recalculated: Total 18,400, was 18,150, over budget; Subtotal North 3,220, was 2,970; and 42 other cells.” | JAWS may truncate long messages; keep under ~200 characters |
| Ctrl+Alt+C (“what changed”) | Opens a dialog or list of 44 changes | — |
| Navigate to F20 later | Cell value read normally | — |
Integration context
Permalink to "Integration context"The edit confirmation that comes first is part of accessible formula bar for spreadsheet grids and entering and exiting cell edit mode. Ordering two messages reliably uses the techniques in announcement queues for competing messages. Numeric changes are easier to understand with direction words, as covered in announcing numeric changes with direction and magnitude.
Gotchas
Permalink to "Gotchas"Async recalculation. Large sheets recalculate in a worker; the summary must wait for completion, not fire after the first batch. If it takes more than a second, announce “Recalculating…” first.
Undo. Undoing the edit recalculates again. Announce the reverse summary (“Total back to 18,150”) so users know the undo reached the dependents.
Charts bound to the sheet. If a chart updates, mention it once (“Chart updated”) rather than describing the new chart; users can open its table view.
Framework notes
Permalink to "Framework notes"Calculation engines usually report dependents. HyperFormula (used by Handsontable) returns an array of ExportedChange objects from setCellContents, each with address and new value — capture old values before the call to build “was” text. SheetJS does not recalculate; if you use it with your own engine, record changes in your engine. In React, keep the announcer outside the grid component so a re-render of the sheet does not remount the live region.
// HyperFormula: changes returned by the engine
const before = snapshotFormulaCells(hf);
const changes = hf.setCellContents({ sheet: 0, col: 1, row: 3 }, [[1500]]);
announceRecalc('B4', changes.map(ch => toChange(ch, before)));
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Let sheet authors mark cells as “key results” in the model — totals, KPIs, limits — so the announcer knows what to name without heuristics. Document the summary format in the component guidelines and expose the ranking function so products with special priorities can adjust it.
FAQ
Permalink to "FAQ"Should recalculation be announced assertively?
No, except when a threshold the user must act on is crossed and the product treats it as an error. Routine recalculation is polite.
How many cells should the summary name?
Two or three at most. Beyond that, count them and offer the full list on request.
What if nothing visible changed but off-screen totals did?
Still announce the key totals. Off-screen is exactly where screen reader users cannot see the effect of their change.
Is a "what changed" dialog a good idea, or should it be a panel?
Either works. A non-modal panel that stays open while editing suits frequent use; a dialog suits occasional review. Make it a list or table with old and new values and a way to jump to each cell.
What about collaborative sheets where other people's edits recalculate cells?
Treat remote edits differently from your own. Do not announce every remote recalculation; offer a quiet indicator and an on-demand summary (“3 cells changed by others since you last checked”), and announce only when a remote change affects the cell the user is editing.
Related
Permalink to "Related"- Formula bar — where edits are committed
- Announcing totals and summary rows — totals in ordinary tables
- Announcing numeric changes — wording for up and down