Skip to content

Seven accessibility gaps the E2E journeys uncovered #51

Description

@rsml

Writing the Phase 6 end-to-end journey suite turned up seven accessibility gaps in the client. They surfaced because that suite forbids data-testid and addresses the UI the way a screen reader does, so a control a locator cannot name is a control a screen reader cannot name either.

None was patched, because Phase 6 is a pure test addition and changes no production file. Every journey works around its gap with a legitimate text or role locator, so fixing these will not break the suite. Several locators get simpler.

Ordered by severity.

  1. A book card cannot be reached by keyboard at all. client/features/library/components/BookCard.tsx renders the card as a div with onClick and onContextMenu. No role, no accessible name, no tab stop, so a keyboard user cannot open a book. Wants a button, or role="button" with tabIndex={0} and a key handler, named from the book title. Journeys reach it with getByText(title, { exact: true }).first().

  2. Two controls share the accessible name "Next section". client/features/reader/components/ChapterRail.tsx and client/features/reader/components/SectionTapZones.tsx both render a button with that name, wired to the same goNext callback. Assistive technology announces the same control twice, and Playwright's strict mode rejects it outright until a journey disambiguates. One of the two wants a distinct name or none at all.

  3. The book context menu has no menu semantics. client/features/library/dialogs/BookContextMenu.tsx is a plain div of button elements. No menu role, no menuitem roles, no arrow-key navigation, so assistive technology sees a pile of buttons rather than a menu.

  4. The feedback form's textareas are not label-associated. FeedbackForm.tsx puts each label as a plain sibling of its textarea, with no for and id pair and no aria-label, so neither field has an accessible name. Journeys locate them by placeholder.

  5. The Rename dialog's labels are not wired to their inputs. Title and Subtitle both lack htmlFor with a matching id, so getByLabel finds neither and a screen reader announces two unlabelled text fields. The journey selects them positionally inside the dialog instead.

  6. The Delete dialog's confirmation input has no label. The field where a reader types the confirmation word has no label element and no aria-label.

  7. The library toolbar's icon-only controls have no accessible name. The search, grid, and list toggles in LibraryToolbar carry a title attribute, which is a tooltip rather than a name. They want aria-label. The search journey uses the keyboard shortcut instead of the button because of this.

Separately, and not an accessibility issue: the packaged Electron app logs two Content Security Policy violations on boot, an inline script blocked by script-src 'self' and a data-URI font blocked by font-src. The app still renders. Worth its own look.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions