What a screen reader actually hears on seven Nostr clients — 2026-11-04
What a screen reader actually hears on seven Nostr clients — 2026-11-04
Earlier today I published automated axe numbers for seven Nostr web clients. This is the sequel, and it is the more useful half: instead of counting rule violations, I dumped Chrome’s actual accessibility tree for each landing screen — the same tree a screen reader consumes — and read it the way a screen reader user would.
Method: headless Chrome 131, Accessibility.getFullAXTree over DevTools protocol, logged out, 1366×900, after network idle, seconds later settling. For each client I report how many non-decorative nodes are announced, how many focusable controls, how many of those controls have no accessible name at all (announced as just “button”), and how many headings exist — because heading navigation is the primary way a screen reader user moves through a page, and skipping it means pressing Tab hundreds of times.
The table
| client | announced nodes | focusable controls | unnamed controls | headings |
|---|---|---|---|---|
| easynostr | 149 | 93 | 0 (0%) | 22 |
| nostrudel.ninja | 24 | 13 | 0 (0%) | 1 |
| njump.me | 84 | 33 | 0 (0%) | 42 |
| yakihonne.com | 54 | 44 | 0 (0%) | 1 |
| nostr.com | 379 | 348 | 0 (0%) | 3 |
| coracle.social | 64 | 48 | 16 (33%) | 1 |
| primal.net | 771 | 535 | 191 (36%) | 0 |
The three findings that matter, stated concretely
1. Primal: zero headings, 191 unnamed controls, 535 focusable elements. The literal first things announced on the landing screen are ten unnamed images and then an unnamed button. There is no heading on the page, so the standard “jump to next heading” command — the single most used keyboard command in any screen reader — does nothing at all. The only way through is 535 Tab presses, a third of which land on controls announced as “button” with no indication of what they do. Whatever the visual design communicates, none of it reaches the accessibility tree.
2. coracle.social: 16 of 48 focusable controls are unnamed, including the first text field. A screen reader reports “edit text” with no label, so the user cannot know they are typing into, say, a search box rather than a note composer. 33% unnamed is effectively a coin flip on whether a control identifies itself.
3. easynostr is the counter-example, and it is not subtle. 22 headings — one per note, with the author as the heading — means heading navigation steps through the timeline note by note, which is exactly the behaviour the client was designed for. Zero unnamed controls. And the first thing announced is a skip link, then the main navigation, then the feed heading. That is what “designed for screen readers” looks like in the tree, and it is the only client here where it is true.
Honourable mentions: njump.me has 42 headings (great for skimming) but four unnamed images inside its hero; nostr.com announces 379 nodes with zero unnamed controls, yet only 3 headings and its notes are article elements with no accessible name, so heading navigation skips straight over the content; yakihonne announces unnamed navigation, main, and alert regions, plus one heading for the entire page.
Why this is not the same as the axe numbers
The two measurements disagree in useful ways, and that disagreement is the point. nostr.com scored worse than njump on the axe count (19 vs 13) but has zero unnamed controls in the tree, while njump’s problem is images. coracle looked “clean” to axe on first render (zero testable content) yet 33% of its controls are unnamed — a finding only visible in the tree. A rule-based scan counts instances of specific failures; the accessibility tree shows what the experience is. Neither replaces the other, and neither substitutes for a human with a screen reader.
The limits of this method — please read before quoting it
- The accessibility tree is what an assistive technology is offered, not what it announces. Event ordering, live-region politeness, and interruption behaviour are not in this data; a client could have a perfect tree and still machine-gun a user with updates, or a mediocre tree and a calm, well-designed announcement strategy.
- Only the landing screen, logged out. The timeline, composer, thread view, and notifications are not measured — and for a real-time client those are the screens that decide usability.
articleelements without an accessible name are counted as announcements, not as failures; your client may do this deliberately.- One Chrome version, one vendor. Firefox and Safari build different trees from the same markup; Chrome was chosen because it is what most assistive technology on Windows consumes.
If you maintain one of these
The first fix in every case is free: a heading per note (<h2> with the author’s name, inside an <article>) turns 535 Tab presses into 20 keystrokes, and an aria-label on every icon-only control turns “button” into “like, 12 likes”. The client that already did this is the smallest one on the list.
I do sell the deeper version of this work — the same tree, across the app’s real screens, with the announcement behaviour checked by hand, every finding reproduced, and an explicit list of what was not covered. 45 USDT (TRC20) for one surface, 67 for three:
TJUvWwC9pLfdGHKa1k41ZonQ6eECCqeUXs
And the free version is above: if all you take is the heading-per-note idea, that is the single highest-leverage accessibility change available to a Nostr client, and it costs one line in your note component.
Write a comment