Sound and haptics
Whether urgency may be carried by a buzz or a chime, the three-pattern haptic vocabulary, and why opsinjs specifies both channels but ships neither.
Overview
An iOS-idiom consumer health app has three alert channels available to it. They are visual, audible and tactile. The last two are the ones designers reach for when the first one has stopped working. That is exactly the wrong reason to reach for them, and it is how alarm fatigue is manufactured: the visual escalation became noise, so a buzz was added, and now the buzz is noise too and there is nothing left.
This page exists because opsinjs states flatly that urgency must never be conveyed by motion, and a rule about motion that says nothing about the other two non-textual channels invites the obvious workaround. So: the same rule applies, for the same reason, with one addition specific to these channels. They are the only ones the reader can be absent for.
opsinjs ships no sound and no haptic implementation. On the web this is partly a platform fact and partly a decision; both are set out under How it works. What this page provides now is the contract any implementation must satisfy, and the prohibitions that apply whether or not the implementation is ours.
How it works
The channel rule: reinforce, never carry. Sound and haptics may add emphasis to something that is already fully expressed visually and textually. They may never be the only expression of anything. The reason is not accessibility pedantry, it is arithmetic: a phone can be on silent, face down, in a bag, in another room, in a cinema, or held by somebody who cannot feel or hear it. A channel with that failure rate cannot be load-bearing for a health message.
The haptic vocabulary is three patterns and no more.
| Pattern | Fires when | Never fires for |
|---|---|---|
acknowledge | A user-initiated action committed, such as a reading saved or a consent given | Anything the user did not initiate |
warn | A user-initiated action produced a result the product has decided needs attention | A value merely arriving or updating |
reject | A user-initiated action was refused, such as a validation failure or an impossible entry | A slow network, a retryable error |
Three is not a starting point to be extended. A per-feature haptic vocabulary is unlearnable, and an unlearnable tactile signal is just a buzz.
No sound by default, at any level. A consumer health app that makes noise on its own has sent a notification, and notifications are governed by Notifications and off-screen alerts, not by this page. In-app sound is opt-in, per-product, and never the difference between a message being received and missed.
The escalation budget is shared, not additive. Haptics draw from the same
per-screen and per-session escalation budget as visual alerts, described on
Alarm fatigue. Adding a buzz to a screen that has
already spent its budget does not buy attention; it spends the same budget twice.
The warn pattern is subject to the same at-most-one rule as an urgent surface.
Why nothing is shipped. The Web Vibration API is not implemented in Safari on iOS, which is the primary platform for the products opsinjs is built for, and there is no web API for the richer tactile vocabulary the platform itself uses. So a web-only haptic implementation would work on some devices and silently do nothing on the ones that matter most. That is the worst possible shape for a signal whose whole justification is redundancy. The contract is published here so a product wrapping opsinjs in a native shell implements the same three patterns rather than inventing four.
Using it
Ask what happens when it does not fire. This is the whole test. If the answer is "the reader misses something", the design is wrong, not the channel. Rewrite until the answer is "the reader gets the same information a moment later, by looking".
Do
A single acknowledge tap when a blood-pressure reading is saved, alongside
the visible confirmation and the entry appearing in the log. Somebody with
the phone on silent loses nothing.
Don’t
A repeating buzz when a reading crosses a threshold, with no corresponding change on screen until the reader opens the app. Whoever has haptics disabled, or is asleep, or left the phone charging, is told nothing. Whoever does feel it has no way to know what it was about.
One haptic per action, and only for actions. Never on scroll, never on a value updating, never on a screen appearing, never repeated. A haptic that fires without a preceding user action is a notification with no text.
Let people turn it off inside the product. Relying on the operating-system setting alone is not enough: someone may want system haptics and not want them from a health app that reminds them of a diagnosis every time it taps. The preference is per-product and it is honoured immediately.
Never use warn to carry a threshold crossing the reader has not seen. That
is an escalation decision, and escalation belongs to
Emergency and escalation and the
product's clinical governance, not to a tactile pattern.
Tokens
The three patterns are the tokens: named, ordinal, and mapped by an implementation
to whatever the host platform provides. There are no durations or intensities in
tokens/, deliberately. A millisecond figure would be meaningless across the
platforms this has to run on, and publishing one would imply an implementation
that does not exist.
NOT IMPLEMENTED. This component does not exist in any released version of opsinjs. There is no package to install, no module to import and no props interface to generate code against. Everything on this page is a specification of intended behaviour and may change without notice. Do not write code against it.
This component is not built yet
PlannedRoadmapWhat “planned” means
Accessibility impact
This is the foundation with the widest spread between "helps enormously" and "harms actively", and both directions are real.
- For some readers this is the primary channel. A deafblind reader may receive a tactile signal and nothing else. That is an argument for implementing haptics well, and it is not an argument for making them exclusive. The same reader must also be able to read the message.
- For some readers it is harmful. Vibration can be distressing for people with sensory processing differences, can be genuinely painful with some pain conditions, and can trigger a startle response. A per-product off switch is therefore a requirement, not a courtesy.
prefers-reduced-motiondoes not cover this. There is no media query for tactile output, and reduced-motion is about vestibular effects rather than touch. Do not infer one preference from the other; ask.- Audio needs a text equivalent, always. Any sound that carries meaning has the same meaning available as text on screen at the same moment. A chime with no visible counterpart is inaccessible to every deaf reader and to every reader whose phone is on silent, which is most of them.
- Neither channel may be the only signal for a status change. The colour, the icon and the word rule from Principles applies unchanged; sound and haptics are a fourth and fifth channel on top, never a substitute for any of the three.
The conformance position and the testing route are on Cognitive accessibility and For testing.
Related
- Alarm fatigue has the shared escalation budget these channels draw from, and the at-most-one rule.
- Motion in health UI has the sibling rule for the third non-textual channel, and the reason this page exists.
- Notifications and off-screen alerts says what happens when the reader is not looking at the app at all.
Accessible charts
The table twin every chart ships with, the generated one-sentence summary, and the markup that stops a chart being an image full of numbers nobody can read.
Theming & tokens
The three tiers of the opsinjs token system, which tier you are allowed to change, and what the system refuses to let you change at all.