feat(a11y): soft length hint on @mention search input (>=90 chars) #639

Open
opened 2026-05-20 00:20:03 +02:00 by marcel · 0 comments
Owner

Context

PR #629 introduced MAX_QUERY_LENGTH = 100 (CWE-400 layered cap, Nora #1) in the @mention search input. The cap is silent — pasted long strings are truncated with no user signal, and a long visible historical name (e.g. "Seine Hochwohlgeboren Friedrich Wilhelm Heinrich Karl Ludwig von Hohenzollern-Sigmaringen…") loses its tail without explanation. WCAG 3.3.1 Error Identification calls for an honest signal when the system is silently transforming input.

Reviewer rationale: Leonie #11068 (S-2) on PR #629 round 3.

Required

  • When searchQuery.length >= 90, render a small inline hint below the input:
    • de: "Suche auf 100 Zeichen begrenzt"
    • en: "Search limited to 100 characters"
    • es: "Búsqueda limitada a 100 caracteres"
  • Styling: text-xs text-ink-3 px-3 pb-2 font-sans (consistent with the dropdown's secondary copy).
  • Wire the hint via aria-describedby on the <input> so screen readers announce it when focus is on the input.
  • The hint is visible only when length crosses 90; doesn't take layout space when below.
  • Threshold (90) lives in mentionConstants.ts next to MAX_QUERY_LENGTH (e.g. MAX_QUERY_LENGTH_HINT_THRESHOLD = 90).
    • Rationale: Threshold = MAX_QUERY_LENGTH - 10 (i.e. 90) leaves a 10-char leading-edge buffer so the hint appears before the user can paste over the cap. Picking 99 would only warn after the silent truncation already happened; picking 50 would warn far too early for typical names.

Acceptance

  • Hint visible at length 90+; gone at 89-.
  • aria-describedby wired and validated via axe-core (or manual NVDA spot-check).
  • i18n in de/en/es.
  • Unit test covering both visibility states (length === 89 → hint absent; length === 95 → hint present + correct text).
  • No layout shift when the hint appears (verified manually or via Playwright screenshot).

Depends on / interacts with

  • #636 (aria-controls / aria-activedescendant) — the aria-describedby should be a third ID on the input alongside whatever #636 wires.
  • Layered with the existing MAX_QUERY_LENGTH = 100 clip (Nora #1 / PR #629).

Out of scope

  • Changing the cap value or the dual-path clipping (those are PR #629 / #633).
  • Hard validation / blocking input — the hint stays soft, the input still accepts up to 100 chars per maxlength.

Reviewer rationale: Leonie #11068 (S-2) on PR #629 round 3; threshold rationale per Elicit/Sara polish on PR #629 round 4.

## Context PR #629 introduced `MAX_QUERY_LENGTH = 100` (CWE-400 layered cap, Nora #1) in the @mention search input. The cap is silent — pasted long strings are truncated with no user signal, and a long visible historical name (e.g. "Seine Hochwohlgeboren Friedrich Wilhelm Heinrich Karl Ludwig von Hohenzollern-Sigmaringen…") loses its tail without explanation. WCAG 3.3.1 Error Identification calls for an honest signal when the system is silently transforming input. Reviewer rationale: Leonie #11068 (S-2) on PR #629 round 3. ## Required - When `searchQuery.length >= 90`, render a small inline hint below the input: - de: "Suche auf 100 Zeichen begrenzt" - en: "Search limited to 100 characters" - es: "Búsqueda limitada a 100 caracteres" - Styling: `text-xs text-ink-3 px-3 pb-2 font-sans` (consistent with the dropdown's secondary copy). - Wire the hint via `aria-describedby` on the `<input>` so screen readers announce it when focus is on the input. - The hint is visible only when length crosses 90; doesn't take layout space when below. - Threshold (90) lives in `mentionConstants.ts` next to `MAX_QUERY_LENGTH` (e.g. `MAX_QUERY_LENGTH_HINT_THRESHOLD = 90`). - **Rationale**: Threshold = `MAX_QUERY_LENGTH - 10` (i.e. 90) leaves a 10-char leading-edge buffer so the hint appears before the user can paste over the cap. Picking 99 would only warn after the silent truncation already happened; picking 50 would warn far too early for typical names. ## Acceptance - [ ] Hint visible at length 90+; gone at 89-. - [ ] `aria-describedby` wired and validated via axe-core (or manual NVDA spot-check). - [ ] i18n in de/en/es. - [ ] Unit test covering both visibility states (`length === 89` → hint absent; `length === 95` → hint present + correct text). - [ ] No layout shift when the hint appears (verified manually or via Playwright screenshot). ## Depends on / interacts with - #636 (`aria-controls` / `aria-activedescendant`) — the `aria-describedby` should be a third ID on the input alongside whatever #636 wires. - Layered with the existing `MAX_QUERY_LENGTH = 100` clip (Nora #1 / PR #629). ## Out of scope - Changing the cap value or the dual-path clipping (those are PR #629 / #633). - Hard validation / blocking input — the hint stays soft, the input still accepts up to 100 chars per `maxlength`. Reviewer rationale: Leonie #11068 (S-2) on PR #629 round 3; threshold rationale per Elicit/Sara polish on PR #629 round 4.
marcel added the P3-laterfeatureui labels 2026-05-20 00:20:08 +02:00
Sign in to join this conversation.
No Label P3-later feature ui
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#639