Topic 15.5
Contrast, Motion, Zoom and Sound
In one line
Accessibility is not only for screen readers. Check that marks have enough contrast in both themes, that the page reflows at high zoom, that motion respects the reader's setting, and consider sound as another way to hear a trend.
Think of it like this
A road sign. It is made for a driver at speed, in rain and at night, for people with weak eyes or tired ones. Big letters, high contrast, a plain shape, and no flashing. A chart is read in worse conditions than we picture: a bright office, a phone in sunlight, a wall screen from the back of the room.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- contrast ratio
- How different two colours are in lightness, from 1 to 1 (the same) up to 21 to 1 (black on white).
- relative luminance
- How bright a colour is to the eye, from 0 (black) to 1 (white), the number the ratio is made from.
- reflow
- The page rearranging into one column when the window is narrow or zoomed in.
- reduced motion
- A system setting a person turns on to avoid animation, read with the prefers-reduced-motion query.
- sonification
- Showing data as sound: for example, higher values as higher pitches.
- pitch
- How high or low a note sounds, measured in hertz (Hz).
- AudioContext
- The browser object that makes sound with Web Audio.
Step by step
01Measure the contrast
The first example turns each token's hex colour into a relative luminance, then into a ratio against the card. It prints pass or FAIL against 3 (marks) or 4.5 (text).
02Read the result honestly
Accent, alert, muted text and ink pass in both themes. The grid lines fail: they are faint on purpose. Whether that is acceptable depends on whether a person needs the grid to read the chart, and the axis labels carry the numbers. Say it as a decision, not a pass.
03Check reduced motion
The second example calls the dashboard's ms() with and without reduced motion. Every duration becomes 0 with it: the same code path, no movement.
04Plan the notes
The third example maps rides to a frequency between 220 and 880 Hz, handles a gap (a rest) and equal values (one steady note), and logs the plan. It never plays: sound waits for a click.
05Reflow at 320 pixels
The measured block shows the whole dashboard at a 320 pixel window: the page is exactly 320 wide, with no horizontal scrolling.
Try it yourself
- 1
Try a lighter teal
In the first example, change the light theme's accent to
#2a9d8f. The first light line becomeslight accent 3.32:1 needs 3 pass: still above 3, with a lot less to spare than the dashboard's#1f7a6fat 5.16. - 2
Change the pitch range
In the third example, change
high = 880tohigh = 440. Tuesday plays at 440 Hz instead of 880 Hz, and Wednesday at 389 Hz. The shape is the same, and the range is one octave instead of two.
Code & diagrams
The formula is the one WCAG gives for relative luminance and contrast ratio. The tokens are the ones from theme.css (Topic 10.4).
Console
(nothing was printed)Expected console output
light accent 5.16:1 needs 3 pass
light alert 5.18:1 needs 3 pass
light muted 5.50:1 needs 4.5 pass
light ink 14.47:1 needs 4.5 pass
light grid 1.29:1 needs 3 FAIL
dark accent 8.64:1 needs 3 pass
dark alert 6.97:1 needs 3 pass
dark muted 7.26:1 needs 4.5 pass
dark ink 13.68:1 needs 4.5 pass
dark grid 1.41:1 needs 3 FAIL
Orange against teal: 1.00:1. Same lightness, so a grey-scale view cannot tell Monday's bar from the rest: the words must say it too.The checker's page has not asked for reduced motion, so the setting reads false. The test passes true for the second line.
Console
(nothing was printed)Expected console output
prefers-reduced-motion is a setting the visitor chose, so the code asks for it: prefersReducedMotion() is boolean
reduced = false | line reveal: 600 ms | bar move: 450 ms | stagger per bar: 10 ms
reduced = true | line reveal: 0 ms | bar move: 0 ms | stagger per bar: 0 ms
The query exists: trueThe checker cannot hear, so this example only logs the planned frequencies. play() is the real code path: it runs when a person presses the button.
Console
(nothing was printed)Expected console output
Mon 2979 -> 220 Hz
Tue 3992 -> 880 Hz
Wed 3759 -> 728 Hz
Thu 3742 -> 717 Hz
Fri 3648 -> 656 Hz
Sat 3013 -> 242 Hz
Sun 3451 -> 528 Hz
with a gap: 220, rest, 880
all equal: 550, 550
Button added; play() is defined and has not been called.Measured with Playwright 1.63 in Chromium, viewport 320 by 800, the Phase 15 dashboard and the Phase 14 one.
document.documentElement.scrollWidth, clientWidth at 320 px:
Phase 14 dashboard: 320, 320
Phase 15 dashboard: 320, 320 (no horizontal scrolling)Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the all-equal case
In the third example, delete the line if (max === min) return (low + high) / 2; ... in pitch.
Myth vs fact
Myth
If the text passes 4.5 to 1, the chart is fine.
Fact
The bars, lines and focus rings have their own 3 to 1 rule, and colour cannot be the only signal.
Myth
Dark mode fixes contrast.
Fact
Dark mode changes the colours, not the check. Measure both themes: the grid failed in both.
Myth
Reduced motion means no animation at all, ever.
Fact
It means less motion where it is not essential. A fade or an instant change is fine, and one function that shortens every duration is the easy route.
Myth
Sonification replaces a description.
Fact
It is an extra way to feel a trend. Some people cannot hear it, and it does not say which day or how many.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Contrast tools compute against a flat colour. A bar over a gradient or an image needs a test at its worst point. Text over a chart (the note "App down") has a halo here (
paint-order: stroke) so it holds against whatever is behind it. - ▸
In Windows forced-colours mode the system replaces your colours. SVG that uses
currentColorand CSS variables follows it better than SVG with fixed hex values. Check that bars still show (system colours for fill) and that nothing disappears. - ▸
Web Audio needs a user gesture to start: a context made before a click starts suspended. Make a new
AudioContextinside the click handler and keep the notes short, with a quick fade so they do not click.
Remember this
- 1
The problem this solves: people who see still do not all see the same. Some have low vision, some have colour-vision differences, some zoom in to 200% or 400%, some feel sick from motion, some use a screen in sunlight. A chart that is only right for one pair of good eyes in a dim room is not accessible.
- 2
Contrast has two rules. Text needs a ratio of at least 4.5 to 1 against its background (WCAG 1.4.3 Contrast (Minimum), level AA; large text 3 to 1). Graphics that you need to understand the chart (bars, lines, axis ticks, focus rings) need 3 to 1 against what is next to them (1.4.11 Non-text Contrast, AA). The ratio comes from each colour's relative luminance:
(lighter + 0.05) / (darker + 0.05). The first example computes it for the dashboard's tokens in both themes. - 3
Colour must not be the only signal. WCAG 1.4.1 Use of Color (level A): if a bar is orange because it is the quiet day, something else must say so as well: a label, a note, a pattern, or the words in the description. The first example finds that the dashboard's orange and teal have almost the same lightness (1.00 to 1), so someone who sees only lightness cannot tell them apart. Phase 10 chose six colours that stay apart under four kinds of colour vision (Topic 10.2); lightness is a separate check.
- 4
Zoom and reflow. People zoom to 200% to read text (WCAG 1.4.4 Resize Text, AA) and to 400%, which is a 320 CSS pixel wide window (1.4.10 Reflow, AA). The page must reflow into one column with no sideways scrolling for the main content. The dashboard's charts are drawn at their box's width and redraw on resize (Topic 10.5), so they reflow. Measure it: the block below shows the page at 320 pixels.
- 5
Respect reduced motion. Some people get dizzy or distracted from motion, and set their system to reduce it. The CSS query
prefers-reduced-motion: reducereports it. The dashboard's one functionms()returns 0 for every duration then, so the line appears at once and the bars do not slide (Topic 9.5). WCAG 2.3.3 Animation from Interactions (level AAA) covers motion that a click triggers, and 2.2.2 Pause, Stop, Hide (level A) covers anything that moves or updates by itself for more than five seconds: Phase 16's live feed needs a pause button. - 6
Sound is another way to see a trend. Sonification turns values into pitch: high numbers are high notes. A person hears the shape of a week in two seconds. Browsers only start sound after a person's click, so the code path is a button that plays notes through Web Audio (
AudioContext, an oscillator and a gain for each note). The third example plans the notes and logs the frequencies. Sound helps some people and annoys others: it is an extra, never the only way. The Highcharts library ships an accessibility module and a sonification module, which is a useful reference for how a mature chart library packages these.
Explain it without notes
What are the two contrast rules for a chart and what ratios do they need?
Why can colour not be the only signal?
How does the dashboard respect reduced motion in one place?
Why does sonification wait for a click?
Practice
Compute the contrast of #767676 text on white. Is it enough for body text?
Write a function playOne(hz) that plays one half-second note and fades it out.
Trade-offs
- ↔
A strong focus ring and strong mark colours cost some elegance. They are the first thing a low-vision reader notices.
- ↔
Sonification costs code and attention for a feature some readers love and others ignore. Offer it as an option and keep it off by default.
Done when you can
I can compute a contrast ratio and know which rule needs 3 and which needs 4.5.
I never use colour alone to say something.
I have checked the page at 320 CSS pixels for sideways scrolling.
I send every animation duration through one reduced-motion check.
I know sound starts only after a click and is never the only route.