diff --git a/frontend/src/lib/components/AnnotationEditOverlay.svelte b/frontend/src/lib/components/AnnotationEditOverlay.svelte
new file mode 100644
index 00000000..45605f13
--- /dev/null
+++ b/frontend/src/lib/components/AnnotationEditOverlay.svelte
@@ -0,0 +1,259 @@
+
+
+
+ {m.annotation_edit_mode_active()}
+
+
+
+
+
+
diff --git a/frontend/src/lib/components/AnnotationEditOverlay.svelte.test.ts b/frontend/src/lib/components/AnnotationEditOverlay.svelte.test.ts
new file mode 100644
index 00000000..1afa47eb
--- /dev/null
+++ b/frontend/src/lib/components/AnnotationEditOverlay.svelte.test.ts
@@ -0,0 +1,50 @@
+import { describe, it, expect } from 'vitest';
+import { render } from 'vitest-browser-svelte';
+import AnnotationEditOverlay from './AnnotationEditOverlay.svelte';
+import type { Annotation } from '$lib/types';
+
+const annotation: Annotation = {
+ id: 'ann-1',
+ documentId: 'doc-1',
+ pageNumber: 1,
+ x: 0.1,
+ y: 0.2,
+ width: 0.3,
+ height: 0.4,
+ color: '#00c7b1',
+ createdAt: '2026-01-01T00:00:00Z'
+};
+
+describe('AnnotationEditOverlay', () => {
+ it('renders 8 handle elements', async () => {
+ render(AnnotationEditOverlay, { annotation });
+
+ const handles = document.querySelectorAll('[data-handle]');
+ expect(handles).toHaveLength(8);
+ });
+
+ it('each handle has a 44x44 hit area', async () => {
+ render(AnnotationEditOverlay, { annotation });
+
+ const hitAreas = document.querySelectorAll('[data-handle-hit]');
+ expect(hitAreas).toHaveLength(8);
+ hitAreas.forEach((el) => {
+ expect(el.getAttribute('width')).toBe('44');
+ expect(el.getAttribute('height')).toBe('44');
+ });
+ });
+
+ it('renders a move area covering the full box', async () => {
+ render(AnnotationEditOverlay, { annotation });
+
+ const moveArea = document.querySelector('[data-move-area]');
+ expect(moveArea).not.toBeNull();
+ });
+
+ it('renders an aria-live region for screen reader announcement', async () => {
+ render(AnnotationEditOverlay, { annotation });
+
+ const liveRegion = document.querySelector('[aria-live="polite"]');
+ expect(liveRegion).not.toBeNull();
+ });
+});