v2.23.0 — the accessibility product that ignored your accessibility settings¶
Released 2026-08-16. Upgrade:
YazSes exists so that people who cannot comfortably type — or cannot use a pointer — can still put words on a screen. This release is two places where it did not hold up, found by reading its own code against its own documentation rather than by a bug report.
The site respected your settings; the app did not¶
The voice-activity overlay draws rings that expand outward from near the pointer, sixty times a second, for as long as you hold the key. Expanding motion in the periphery is the specific pattern people with vestibular disorders and motion sensitivity ask software to stop doing, and every desktop carries a switch where they have said so: GNOME's Reduce Animation, macOS's Reduce motion, Windows's Animation effects.
YazSes read none of them. Meanwhile docs/assets/extra.css — the stylesheet for this very documentation site — has honoured prefers-reduced-motion for months. The site advertising the accessibility tool respected the preference. The tool did not.
[overlay] reduced_motion is now auto (the default), on or off. auto reads the desktop once at startup, never on the render tick.
Reduced motion removes motion, not information. The overlay answers one question — am I being heard, and how loudly — and someone asking for less animation has not asked to stop being told. So the reduced form keeps the ring and drops the travel: one steady circle while you speak, brightness in four discrete bands.
The bands matter. A brightness recomputed every frame from a live microphone level shimmers, and a shimmer at 60 Hz is its own accessibility problem rather than a fix for one. It also shows the ring during silence, which the animated form does not — tolerable when rings are about to appear, and not when none ever will.
A desktop YazSes cannot read resolves to full motion, exactly as before, so nobody's overlay changes on the strength of a failed probe. KDE, Xfce and bare window managers have no such setting to read; they set on and are believed.
A question only a pointer could answer¶
The mic-change guard is the one part of the pipeline that asks rather than tells. When capture moves to another device, or a run of clips comes back silent, it pops a toast offering Re-calibrate, Pin this mic and Ignore.
Those were buttons and only buttons. So the daemon interrupted you with a question about your microphone that could only be answered with a mouse — and the person seeing it is the one whose dictation has just stopped working, which is the worst possible moment to be sent to the pointer.
With [audio] voice_answer on, hold your dictation key and say it instead:
| Say | Does |
|---|---|
| “re-calibrate” / “calibrate the mic” | the same as clicking Re-calibrate |
| “pin this mic” / “pin the microphone” | the same as clicking Pin this mic |
| “ignore” / “dismiss” / “never mind” | dismisses the toast |
Almost all the work here is in not firing. “ignore” and “pin” are ordinary English, so the whole utterance must be the answer — “please ignore the second paragraph” is prose and gets typed. And the words only count within 45 seconds of the toast; an unbounded window would arm “ignore” as a control word for the rest of your session, and once it closes the word types normally rather than being silently swallowed. No action and no text is the worst outcome available.
It runs after the command-safety and check-digit gates and before staged dictation, so a held rm -rf keeps first claim on the utterance and the staged buffer cannot swallow the answer.
Both are findable, which is half the point¶
An opt-in accessibility capability that appears nowhere but a documentation page is one nobody switches on. Both are listed by yazses features:
yazses features enable mic-voice-answer
yazses features enable overlay-reduced-motion
yazses restart
Disabling overlay-reduced-motion restores auto rather than writing off — overriding someone whose desktop asked for less motion is a different opinion, not an off switch.
Both ship off by default, and that is worth stating plainly rather than dressing up: the measured baseline of daemon-initiated states with no voice-only exit stays at 2 until you turn the first one on.
An example config must say what was observed¶
examples/config.<app>.toml files are copied verbatim by newcomers, so a wrong one costs more than none at all. Three checks already guarded them — valid TOML, every key resolving to a real field, an opening comment — and all three pass just as happily on a profile nobody ran. Valid TOML, real keys and a header can all be produced without the application ever being opened.
The fourteen profiles in the tree already record what was actually seen, several with the dictated and arrived text side by side. Nothing enforced it. scripts/check-app-profile.py and the test suite now do.
The check lives in the script rather than only in the suite, and the placement is the point: a pull request from a first-time contributor arrives with every real workflow held at waiting for approval, so the suite cannot see the file until a maintainer clears it. The script runs on the contributor's own machine, while they can still fix it.
A marker word inside an unresolved TODO does not count — a starter file headed “TODO(you): replace this block with what you actually observed” would otherwise satisfy the check by quoting the instruction to fill it in.
Also¶
paper/benchmark/, the reproducibility code the benchmarks page points readers at, sits outside the lint gate and had drifted. Fixed — and the fix was the configuration, not the code: ruff had no way to know_commonis the helper sitting beside those scripts, so its autofix wanted to reorder nine files' imports around a grouping their author chose.