fix(transcription): remove annotation canvas delete button that obscures text #722

Closed
opened 2026-06-03 22:32:01 +02:00 by marcel · 0 comments
Owner

Problem

Each annotation box on the document canvas renders a delete control (AnnotationShape.svelte): a 44×44 px circular red button pinned to top: 4px; right: 4px. On a single-line annotation the button is taller than the box itself, so it overlaps the box below and obscures the underlying document text. It appears on hover/active in transcribe mode (canDraw).

The 44 px size is the WCAG 2.2 touch-target minimum (transcribers work on tablets), so it cannot simply be shrunk without breaking touch accessibility — the control fundamentally fights the geometry of small annotation boxes.

Why removal is the right fix (not "improve in place")

The canvas delete button is redundant in 100% of normal user flows:

  • Drawing a box is atomic — createFromDraw POSTs once to /transcription-blocks and the backend creates the annotation and its block together (useTranscriptionBlocks.svelte.ts). Every user-created annotation always has a block.
  • The right-hand transcription panel renders one row per block, each with its own (non-overlapping, labelled) delete button (TranscriptionBlock.svelte), which cascades to remove the annotation.

The only thing the canvas button uniquely handles is orphan annotations (annotation with no block) — but those are not reachable through any normal UI flow; they can only arise from a backend partial-failure or data corruption. That is a data-integrity concern, not a transcriber-facing job. Losing the in-app cleanup path for that edge case is an accepted trade-off.

Decision

  • Remove the visible delete button from the annotation canvas.
  • Keep the hidden keyboard Delete shortcut (AnnotationShape.svelte keydown handler) — it does not obscure any content and is retained as a power-user path. The entire wiring chain (showDeleteonDeleteRequestdeleteAnnotation) stays in place to feed the shortcut.

Acceptance criteria

Given a document in transcribe mode with annotation boxes drawn
When I hover or activate an annotation box
Then no delete button is rendered over it and the underlying text is never obscured

Given an annotation box is focused/active in transcribe mode
When I press the Delete key
Then the existing confirm dialog appears and, on confirm, the block + annotation are deleted (unchanged behaviour)

Given the right-hand transcription panel
When I use a block's delete button
Then the block and its annotation are removed (unchanged behaviour)

Scope (files)

File Change
frontend/src/lib/document/annotation/AnnotationShape.svelte Remove the delete <button> block and the now-unused deleteVisible derived. Keep showDelete, onDeleteRequest, and the Delete-key branch.
frontend/src/lib/document/annotation/AnnotationShape.svelte.spec.ts Replace the 6 button-render/click tests with a single "never renders a delete button, even when hovered+active" contract test. Keep the keyboard-Delete tests.
frontend/src/lib/document/annotation/AnnotationLayer.svelte.spec.ts Consolidate the 2 (now-vacuous) delete-button-absence tests into one "never renders a delete button in annotate mode with an active annotation" contract test.
frontend/e2e/documents.spec.ts Repoint the admin can delete an annotation test from the (removed) canvas button to the kept path: focus an annotation → press Delete → confirm dialog ("Bestätigen"). The old matcher (/annotation löschen/i) never matched the button's Löschen aria-label anyway.

Unchanged (must stay — feeds the keyboard shortcut): AnnotationLayer.svelte (showDelete={canDraw}, onDeleteRequest), PdfViewer.svelte, DocumentViewer.svelte, documents/[id]/+page.svelte (handleAnnotationDeleteRequest), useTranscriptionBlocks.svelte.ts (deleteAnnotation).

Scope note: the AnnotationLayer spec and the e2e delete flow also referenced the removed button; both were folded into scope during implementation.

Non-functional notes

  • A11y: keeping a delete action with no visible affordance is a minor discoverability gap; the panel delete remains the primary discoverable path. A future help-page line documenting the Delete shortcut is out of scope here.
  • Verified locally: npm run check, Prettier, and ESLint are clean for all touched files. Browser/e2e specs run in CI.
## Problem Each annotation box on the document canvas renders a delete control (`AnnotationShape.svelte`): a 44×44 px circular red button pinned to `top: 4px; right: 4px`. On a single-line annotation the button is taller than the box itself, so it overlaps the box below and obscures the underlying document text. It appears on hover/active in transcribe mode (`canDraw`). The 44 px size is the WCAG 2.2 touch-target minimum (transcribers work on tablets), so it cannot simply be shrunk without breaking touch accessibility — the control fundamentally fights the geometry of small annotation boxes. ## Why removal is the right fix (not "improve in place") The canvas delete button is **redundant in 100% of normal user flows**: - Drawing a box is atomic — `createFromDraw` POSTs once to `/transcription-blocks` and the backend creates the annotation **and** its block together (`useTranscriptionBlocks.svelte.ts`). Every user-created annotation always has a block. - The right-hand transcription panel renders one row per block, each with its own (non-overlapping, labelled) delete button (`TranscriptionBlock.svelte`), which cascades to remove the annotation. The only thing the canvas button uniquely handles is **orphan annotations** (annotation with no block) — but those are not reachable through any normal UI flow; they can only arise from a backend partial-failure or data corruption. That is a data-integrity concern, not a transcriber-facing job. Losing the in-app cleanup path for that edge case is an accepted trade-off. ## Decision - **Remove the visible delete button** from the annotation canvas. - **Keep** the hidden keyboard `Delete` shortcut (`AnnotationShape.svelte` keydown handler) — it does not obscure any content and is retained as a power-user path. The entire wiring chain (`showDelete` → `onDeleteRequest` → `deleteAnnotation`) stays in place to feed the shortcut. ## Acceptance criteria ``` Given a document in transcribe mode with annotation boxes drawn When I hover or activate an annotation box Then no delete button is rendered over it and the underlying text is never obscured Given an annotation box is focused/active in transcribe mode When I press the Delete key Then the existing confirm dialog appears and, on confirm, the block + annotation are deleted (unchanged behaviour) Given the right-hand transcription panel When I use a block's delete button Then the block and its annotation are removed (unchanged behaviour) ``` ## Scope (files) | File | Change | |------|--------| | `frontend/src/lib/document/annotation/AnnotationShape.svelte` | Remove the delete `<button>` block and the now-unused `deleteVisible` derived. **Keep** `showDelete`, `onDeleteRequest`, and the `Delete`-key branch. | | `frontend/src/lib/document/annotation/AnnotationShape.svelte.spec.ts` | Replace the 6 button-render/click tests with a single "never renders a delete button, even when hovered+active" contract test. **Keep** the keyboard-`Delete` tests. | | `frontend/src/lib/document/annotation/AnnotationLayer.svelte.spec.ts` | Consolidate the 2 (now-vacuous) delete-button-absence tests into one "never renders a delete button in annotate mode with an active annotation" contract test. | | `frontend/e2e/documents.spec.ts` | Repoint the `admin can delete an annotation` test from the (removed) canvas button to the kept path: focus an annotation → press `Delete` → confirm dialog ("Bestätigen"). The old matcher (`/annotation löschen/i`) never matched the button's `Löschen` aria-label anyway. | **Unchanged (must stay — feeds the keyboard shortcut):** `AnnotationLayer.svelte` (`showDelete={canDraw}`, `onDeleteRequest`), `PdfViewer.svelte`, `DocumentViewer.svelte`, `documents/[id]/+page.svelte` (`handleAnnotationDeleteRequest`), `useTranscriptionBlocks.svelte.ts` (`deleteAnnotation`). > Scope note: the `AnnotationLayer` spec and the e2e delete flow also referenced the removed button; both were folded into scope during implementation. ## Non-functional notes - A11y: keeping a delete action with no visible affordance is a minor discoverability gap; the panel delete remains the primary discoverable path. A future help-page line documenting the `Delete` shortcut is out of scope here. - Verified locally: `npm run check`, Prettier, and ESLint are clean for all touched files. Browser/e2e specs run in CI.
marcel added the P2-mediumbugui labels 2026-06-03 22:32:05 +02:00
Sign in to join this conversation.
No Label P2-medium bug ui
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#722