njump.me — a dated testing record, and a defect that matters specifically to a gateway (2026-11-04)
njump.me — a dated testing record, and a defect that matters specifically to a gateway (2026-11-04)
njump is the page most Nostr links land on. Its audience is not people who use Nostr — it is people who don’t, arriving from a link in a chat or a post elsewhere. So the thing worth testing is not the landing page. It is the detail pages, and the two descriptions of each page that exist simultaneously: the one a machine reads (OpenGraph, which decides what the link preview says) and the one a screen reader is offered.
What was tested
- 2026-11-04, 18:47–19:05 UTC.
- headless Chrome 131, viewport 1366×900, logged out.
- axe-core 4.14.0, tags
wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa. - Chrome’s accessibility tree via
Accessibility.getFullAXTree. - Pages: a profile (
npub…for a real account), a single short-text note, and that note as rendered inside a thread (a reply, with a parent reference).
Results
| page | axe failures | largest | focusable | visible focus ring | html lang |
|---|---|---|---|---|---|
| profile | 4 | color-contrast on 67 elements |
24 | 24 / 24 | missing |
| note | 3 | color-contrast on 11 |
23 | 23 / 23 | missing |
| note-in-thread | — | (not scored; see finding 2) | — | — | missing |
Accessibility tree on the profile: 54 announced nodes, 49 focusable, 0 with an empty accessible name, 2 headings (display name, then “Last Notes”). That is a better tree than most clients I measured today.
Findings
1. html lang is missing on every page. On a gateway whose entire job is to receive links from anywhere, this is the one that scales worst: a screen reader has to guess the language, and it guesses wrong on every non-English note, display name and relay name. One attribute in the root template.
2. The two descriptions of a page disagree, and both are wrong in opposite directions.
Measured on the same note:
og:title= “Short Text Note by a05 (reply)” — a type name, thrown away by every client that renders a link preview. The human-readable title the browser gets is different and better: “Filled in the whole document I sell…”.og:description= absent — so a link preview has no body text at all.<title>= the note’s text, truncated with an ellipsis, and for the profile page it is empty.<h1>= “a05 on Nostr: <note text…>” — a label (“on Nostr”), a colon, and a truncated sentence. That is the first and most important thing a screen reader announces when a page loads, and it is neither the author’s name nor a meaningful heading. The page has no heading that says what the page is.- The in-thread version is worse: the visible text reads “in reply to nostr:naddr1qq…3c7v” — a truncated identifier — and the accessibility tree for the note content starts with
article:with no accessible name, then two unnamed images. A screen reader entering the thread is told there is an article and then, essentially, nothing.
The summary: the machine-readable description says less than the page contains, and the human-readable description says less than the machine’s. Both are generated from the same event, and neither carries the author, the note text, or a stable heading.
3. color-contrast, 67 elements on the profile, 11 on the note. Nearly all of it is a single colour pair used for timestamps, relay names and metadata, repeated across every row. Fixing the pair fixes all 67. This is the cheapest large win on the page.
4. image-alt, critical, one node, on both page types. One image with no alt attribute at all. If decorative, alt="" clears it; if it identifies something, it needs a name. The profile’s tree starts with image:UNNAMED, image:UNNAMED — the first two things announced on a profile are anonymous pictures.
What was NOT tested
- Any signed-in or interactive behaviour: replies, reactions, zaps, the media viewer, the search.
- Any actual screen reader. I read Chrome’s accessibility tree; I did not listen to NVDA, JAWS or VoiceOver. The announcement order and the interruption behaviour of a gateway page — which matters if a user arrives and the page re-renders as relays respond — are not in this document.
- Other browsers, other viewports, mobile.
- The OpenGraph image rendering (
/image/nevent…), i.e. whether the preview picture is legible or meaningful. - Long-form articles, media notes, and reply chains beyond one level (the
naddrreference from the thread did not resolve to a readable page in this session, so that route is untested rather than measured). - Anything about content quality: whether existing alt text is meaningful rather than merely present.
Reproduction
node audit.mjs 'https://njump.me/<npub|note|nevent|naddr>'
node axdump.mjs 'https://njump.me/<…>'
node ogcheck.mjs 'https://njump.me/<…>' # og:* vs <title> vs <h1> vs lang
Automated testing covers a fraction of accessibility requirements — Section508.gov puts automated scanning at 30–35% and states its output must not be presented as compliance results. This is the evidence layer underneath an assessment, not a substitute for one, and it is not a certification.
Prepared 2026-11-04 19:06Z. Every claim above is reproducible with the commands shown; if you get different numbers, the timestamps say which of us is looking at a stale page.
Write a comment