feat(person-mention): discoverability hint for the @ trigger in the editor #370

Closed
opened 2026-04-29 01:26:59 +02:00 by marcel · 2 comments
Owner

Context

PR #369 (issue #362 / PR-B1) shipped the @-mention typeahead for transcription blocks but did not add a visible hint that the @ trigger exists. A first-time transcriber has no way to discover the feature without being told or stumbling on it.

This was raised by Leonie in #369 cycle-2 review as the only remaining concern. Explicitly not a B1 blocker — Leonie's recommendation was "ship and validate with the existing 2 transcribers first, then iterate."

Proposal

Add a subtle hint near the textarea (mirroring the existing transcription_block_quote_hint pattern) when the textarea is focused and empty. Suggested copy:

  • de: Tipp: @ tippen um eine Person zu verlinken
  • en: Tip: type @ to link a person
  • es: Sugerencia: escribe @ para vincular a una persona

Hint appears as a one-line muted string below the textarea, only when:

  • The textarea has focus
  • The textarea is empty (or contains no @)
  • The popup is not currently open

Acceptance criteria

Given a first-time transcriber focuses an empty transcription block textarea
When the focus lands
Then a one-line hint "Tipp: @ tippen um eine Person zu verlinken" appears below the textarea

Given the transcriber types any character (including @)
When the textarea is no longer empty
Then the hint disappears

Given the transcriber blurs the textarea
When focus moves away
Then the hint disappears

Out of scope

  • A persistent affordance / button. Leonie's stronger suggestion was a small "@-Person verlinken" button next to existing label affordances; that's a separate visual-design call and would warrant its own discussion.
  • Onboarding tutorial / tour flow.

Effort

≈ 4 lines of Svelte + 3 Paraglide keys. Single small PR.

## Context PR #369 (issue #362 / PR-B1) shipped the @-mention typeahead for transcription blocks but did not add a visible hint that the `@` trigger exists. A first-time transcriber has no way to discover the feature without being told or stumbling on it. This was raised by Leonie in [#369 cycle-2 review](http://heim-nas:3005/marcel/familienarchiv/pulls/369#issuecomment-5525) as the only remaining concern. Explicitly not a B1 blocker — Leonie's recommendation was "ship and validate with the existing 2 transcribers first, then iterate." ## Proposal Add a subtle hint near the textarea (mirroring the existing `transcription_block_quote_hint` pattern) when the textarea is focused and empty. Suggested copy: - de: `Tipp: @ tippen um eine Person zu verlinken` - en: `Tip: type @ to link a person` - es: `Sugerencia: escribe @ para vincular a una persona` Hint appears as a one-line muted string below the textarea, only when: - The textarea has focus - The textarea is empty (or contains no `@`) - The popup is not currently open ## Acceptance criteria **Given** a first-time transcriber focuses an empty transcription block textarea **When** the focus lands **Then** a one-line hint "Tipp: @ tippen um eine Person zu verlinken" appears below the textarea **Given** the transcriber types any character (including `@`) **When** the textarea is no longer empty **Then** the hint disappears **Given** the transcriber blurs the textarea **When** focus moves away **Then** the hint disappears ## Out of scope - A persistent affordance / button. Leonie's stronger suggestion was a small "@-Person verlinken" button next to existing label affordances; that's a separate visual-design call and would warrant its own discussion. - Onboarding tutorial / tour flow. ## Effort ≈ 4 lines of Svelte + 3 Paraglide keys. Single small PR.
Author
Owner

🎨 Leonie Voss — UI/UX Design Lead

Walked through the discoverability hint design with Marcel. The original "muted line below the textarea" framing turned out to be a worse fit than expected — the existing transcription_block_quote_hint is a contextual feedback hint (after-selection), not a discoverability hint, so "mirroring the pattern" would put the new hint in a low-attention zone between the editor and the footer. We pivoted to a stronger placement.

Decisions

  • Placement: inline as part of the editor placeholder. The hint sits where the cursor blinks, not below the fold. No new DOM slot, no layout shift, no {#if} block. The existing <p class="mt-1 text-xs text-ink-3"> slot stays reserved for the quote hint only.

  • Show-condition: whenever the editor is empty (regardless of focus) — broader than the original spec's "focus + empty". Tiptap's natural placeholder behavior. Visible at a glance even before clicking.

  • Copy — show the syntax inline + describe the result, not just the trigger:

    • DE: Text eingeben — mit @Name eine Person aus dem Archiv verknüpfen
    • EN: Type text — use @name to link a person from the archive
    • ES: Escriba el texto — use @nombre para vincular a una persona del archivo

    Three deliberate choices: (a) @Name shows the syntax inline so no separate explanation is needed; (b) aus dem Archiv grounds the result — users understand this connects to existing data, not free text; (c) verknüpfen over verlinken matches the family-archive tone and describes the relationship being formed.

  • Layout shift: N/A — placeholder lives inside the fixed editor box.

  • Visual styling: Flat muted text via Tiptap's default .is-editor-empty::before rendering. No <kbd> pill for @Name — would require a custom Tiptap plugin/overlay, not worth it for B1. The @ symbol is recognizable on its own.

  • Accessibility: No extra aria-describedby. Best-effort placeholder behavior is fine for a hint; the mention typeahead itself remains keyboard-operable regardless of whether the placeholder is announced. Adding aria-describedby for every focus event would get noisy fast.

  • Dismissal persistence: Solved for free by placeholder semantics. Empty shows, typed hides, backspace shows again. No localStorage flag, no "seen" state.

Mobile/touch — accepted as known limitation for B1

On a German tablet keyboard, @ is a long-press on Q (iPad) or AltGr+Q (laptop). The text "type @" tells the user what to do but not how to physically produce the character. Accepted for B1: most transcribers are on laptops; iPad use is secondary. If validation with the existing 2 transcribers shows iPad friction on the @ key, we reopen the @-insert-button discussion as a separate issue. Not now.

Implementation impact

Smaller than the original estimate. The issue proposed ~4 lines of Svelte + 3 new Paraglide keys for a separate hint element. With this approach we modify the 3 existing transcription_block_placeholder strings — no new keys, no new component logic, no new DOM. Effectively a 3-line i18n change plus visual verification.

My read

This is a textbook case for "discoverability via placeholder, not via afterthought-paragraph". The hint lives where the user's eyes already are, disappears the moment it's no longer needed, and reinforces the result (Person aus dem Archiv verknüpfen) — not just the trigger. Ship it.

## 🎨 Leonie Voss — UI/UX Design Lead Walked through the discoverability hint design with Marcel. The original "muted line below the textarea" framing turned out to be a worse fit than expected — the existing `transcription_block_quote_hint` is a *contextual feedback* hint (after-selection), not a *discoverability* hint, so "mirroring the pattern" would put the new hint in a low-attention zone between the editor and the footer. We pivoted to a stronger placement. ### Decisions - **Placement: inline as part of the editor placeholder.** The hint sits where the cursor blinks, not below the fold. No new DOM slot, no layout shift, no `{#if}` block. The existing `<p class="mt-1 text-xs text-ink-3">` slot stays reserved for the quote hint only. - **Show-condition: whenever the editor is empty** (regardless of focus) — broader than the original spec's "focus + empty". Tiptap's natural placeholder behavior. Visible at a glance even before clicking. - **Copy — show the syntax inline + describe the result, not just the trigger:** - **DE**: `Text eingeben — mit @Name eine Person aus dem Archiv verknüpfen` - **EN**: `Type text — use @name to link a person from the archive` - **ES**: `Escriba el texto — use @nombre para vincular a una persona del archivo` Three deliberate choices: (a) `@Name` shows the syntax inline so no separate explanation is needed; (b) `aus dem Archiv` grounds the result — users understand this connects to existing data, not free text; (c) `verknüpfen` over `verlinken` matches the family-archive tone and describes the relationship being formed. - **Layout shift:** N/A — placeholder lives inside the fixed editor box. - **Visual styling:** Flat muted text via Tiptap's default `.is-editor-empty::before` rendering. No `<kbd>` pill for `@Name` — would require a custom Tiptap plugin/overlay, not worth it for B1. The `@` symbol is recognizable on its own. - **Accessibility:** No extra `aria-describedby`. Best-effort placeholder behavior is fine for a hint; the mention typeahead itself remains keyboard-operable regardless of whether the placeholder is announced. Adding `aria-describedby` for every focus event would get noisy fast. - **Dismissal persistence:** Solved for free by placeholder semantics. Empty shows, typed hides, backspace shows again. No `localStorage` flag, no "seen" state. ### Mobile/touch — accepted as known limitation for B1 On a German tablet keyboard, `@` is a long-press on `Q` (iPad) or AltGr+Q (laptop). The text "type @" tells the user *what* to do but not *how* to physically produce the character. Accepted for B1: most transcribers are on laptops; iPad use is secondary. If validation with the existing 2 transcribers shows iPad friction on the `@` key, we reopen the `@`-insert-button discussion as a separate issue. Not now. ### Implementation impact **Smaller** than the original estimate. The issue proposed ~4 lines of Svelte + 3 *new* Paraglide keys for a separate hint element. With this approach we **modify the 3 existing `transcription_block_placeholder` strings** — no new keys, no new component logic, no new DOM. Effectively a 3-line i18n change plus visual verification. ### My read This is a textbook case for "discoverability via placeholder, not via afterthought-paragraph". The hint lives where the user's eyes already are, disappears the moment it's no longer needed, and reinforces the result (`Person aus dem Archiv verknüpfen`) — not just the trigger. Ship it.
Author
Owner

Implementation complete

Branch: feat/issue-370-at-mention-placeholder-hint
PR: #379

What was done

Implemented Leonie's design decision from the review comment: update the 3 existing transcription_block_placeholder i18n strings to embed the @Name trigger inline. No new DOM, no new keys, no new component logic.

Commit: 54b4b964

Changes

File Change
frontend/messages/de.json "Text eingeben — mit @Name eine Person aus dem Archiv verknüpfen"
frontend/messages/en.json "Type text — use @name to link a person from the archive"
frontend/messages/es.json "Escriba el texto — use @nombre para vincular a una persona del archivo"
PersonMentionEditor.svelte.spec.ts New TDD test: transcription_block_placeholder contains @ mention trigger for discoverability

Tests

All 22 PersonMentionEditor tests green including the new one. Pre-existing failures in layout.svelte.spec.ts and hilfe/transkription/page.svelte.spec.ts are unrelated to this change.

## Implementation complete Branch: `feat/issue-370-at-mention-placeholder-hint` PR: #379 ### What was done Implemented Leonie's design decision from the review comment: update the 3 existing `transcription_block_placeholder` i18n strings to embed the `@Name` trigger inline. No new DOM, no new keys, no new component logic. **Commit:** `54b4b964` ### Changes | File | Change | |---|---| | `frontend/messages/de.json` | `"Text eingeben — mit @Name eine Person aus dem Archiv verknüpfen"` | | `frontend/messages/en.json` | `"Type text — use @name to link a person from the archive"` | | `frontend/messages/es.json` | `"Escriba el texto — use @nombre para vincular a una persona del archivo"` | | `PersonMentionEditor.svelte.spec.ts` | New TDD test: `transcription_block_placeholder contains @ mention trigger for discoverability` | ### Tests All 22 PersonMentionEditor tests green including the new one. Pre-existing failures in `layout.svelte.spec.ts` and `hilfe/transkription/page.svelte.spec.ts` are unrelated to this change.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#370