diff --git a/docs/specs/lesereisen-reader-spec.html b/docs/specs/lesereisen-reader-spec.html index 68be7649..2ae036e3 100644 --- a/docs/specs/lesereisen-reader-spec.html +++ b/docs/specs/lesereisen-reader-spec.html @@ -636,19 +636,19 @@ Metabarflex items-center gap-3 pb-4 border-b border-subtle mb-4gleich wie Story Bearbeiten/Löschennur BLOG_WRITE; auf Mobile im ··· BottomSheetgleich wie Story Intro-Absatz - Intro (body)font-serif text-sm text-ink-2 italic leading-relaxed mb-6 pb-4 border-b border-dashed border-subtlenur rendern wenn body nicht leer; kein HTML-Rendering — plaintext + Intro (body)font-serif text-lg text-ink-2 italic leading-relaxed mb-6 pb-4 border-b border-dashed border-subtlenur rendern wenn body nicht leer; kein HTML-Rendering — plaintext Dokument-Item Item-Zeilemb-3kein flex nötig — Karte ist full-width Dokumentkartebg-surface border border-line rounded-sm p-3 - Brieftitelfont-serif text-sm text-ink leading-snug mb-0.5document.title - Briefmetatext-xs text-ink-3 mb-2formatDate(document.documentDate) · "von X an Y" - Brief öffnen Linkinline-flex items-center gap-1 text-xs font-semibold text-ink hover:text-primaryhref="/documents/{item.document.id}" + Brieftitelfont-serif text-base text-ink leading-snug mb-0.5document.title + Briefmetatext-sm text-ink-3 mb-2formatDate(document.documentDate) · "von X an Y" + Brief öffnen Linkinline-flex items-center gap-1 text-sm font-semibold text-ink hover:text-primaryhref="/documents/{item.document.id}" Kuratoren-Annotation Annotationmt-3 pl-3 border-l-2 border-brand-mint bg-muted rounded-r-sm py-1.5 pr-2nur rendern wenn item.note vorhanden - Annotations-Texttext-xs italic text-ink-2 leading-relaxed + Annotations-Texttext-base italic text-ink-2 leading-relaxed Interlude-Item Interlude-Blockpl-3 border-l-2 border-orange-400 bg-orange-50 rounded-r-sm py-2 pr-3 my-4item.document === null - Interlude-Texttext-xs italic text-ink leading-relaxeditem.note; plaintext + Interlude-Texttext-base italic text-ink leading-relaxeditem.note; plaintext Mobile ··· Menüml-auto text-ink-3; öffnet BottomSheet mit Bearbeiten + LöschenBLOG_WRITE; gleich wie Story Touch Target (Brief öffnen)min-h-[44px] durch padding auf der KarteWCAG 2.2 AA diff --git a/frontend/src/lib/geschichte/JourneyInterlude.svelte b/frontend/src/lib/geschichte/JourneyInterlude.svelte index 9b9a180a..cd66fd4b 100644 --- a/frontend/src/lib/geschichte/JourneyInterlude.svelte +++ b/frontend/src/lib/geschichte/JourneyInterlude.svelte @@ -14,5 +14,5 @@ let { note }: Props = $props(); class="my-4 rounded-r-sm border-l-2 border-orange-400 bg-orange-50 py-2 pr-3 pl-3" > -

{note}

+

{note}

diff --git a/frontend/src/lib/geschichte/JourneyInterlude.svelte.spec.ts b/frontend/src/lib/geschichte/JourneyInterlude.svelte.spec.ts index ee97b3c7..3764542c 100644 --- a/frontend/src/lib/geschichte/JourneyInterlude.svelte.spec.ts +++ b/frontend/src/lib/geschichte/JourneyInterlude.svelte.spec.ts @@ -35,6 +35,14 @@ describe('JourneyInterlude', () => { expect(el?.getAttribute('aria-label')).toBe(m.journey_interlude_aria_label()); }); + it('note text uses readable body size (text-base, #800)', async () => { + render(JourneyInterlude, { props: { note: 'Notiz' } }); + + const text = document.querySelector('[role="note"] p'); + expect(text!.className).toContain('text-base'); + expect(text!.className).not.toContain('text-xs'); + }); + it('XSS: note is rendered as plaintext — injected payload does not execute', async () => { // Interlude uses Svelte text interpolation ({note}), NOT {@html}. render(JourneyInterlude, { diff --git a/frontend/src/lib/geschichte/JourneyItemCard.svelte b/frontend/src/lib/geschichte/JourneyItemCard.svelte index e2275afd..e54d44bc 100644 --- a/frontend/src/lib/geschichte/JourneyItemCard.svelte +++ b/frontend/src/lib/geschichte/JourneyItemCard.svelte @@ -27,14 +27,14 @@ const hasNote = $derived(item.note != null && item.note.trim().length > 0);
-

{doc.title}

+

{doc.title}

{#if metaLine} -

{metaLine}

+

{metaLine}

{/if} @@ -59,7 +59,7 @@ const hasNote = $derived(item.note != null && item.note.trim().length > 0); {#if hasNote}
-

{item.note}

+

{item.note}

{/if}
diff --git a/frontend/src/lib/geschichte/JourneyItemCard.svelte.spec.ts b/frontend/src/lib/geschichte/JourneyItemCard.svelte.spec.ts index 6b1ca318..19161248 100644 --- a/frontend/src/lib/geschichte/JourneyItemCard.svelte.spec.ts +++ b/frontend/src/lib/geschichte/JourneyItemCard.svelte.spec.ts @@ -134,6 +134,21 @@ describe('JourneyItemCard', () => { expect(note!.className).toContain('bg-muted'); }); + it('reading text sizes meet the accessibility floor (#800)', async () => { + render(JourneyItemCard, { props: { item: baseItem({ note: 'Ein wichtiger Brief' }) } }); + + const title = page.getByText('Brief an Helene'); + expect((await title.element()).className).toContain('text-base'); + + const link = await page.getByRole('link', { name: /öffnen/i }).element(); + expect(link.className).toContain('text-sm'); + expect(link.className).not.toContain('text-xs'); + + const noteText = document.querySelector('[class*="border-l-2"] p'); + expect(noteText!.className).toContain('text-base'); + expect(noteText!.className).not.toContain('text-xs'); + }); + it('omits annotation block when note is blank or whitespace', async () => { render(JourneyItemCard, { props: { item: baseItem({ note: ' ' }) } }); diff --git a/frontend/src/lib/geschichte/JourneyReader.svelte b/frontend/src/lib/geschichte/JourneyReader.svelte index 18f93342..96603322 100644 --- a/frontend/src/lib/geschichte/JourneyReader.svelte +++ b/frontend/src/lib/geschichte/JourneyReader.svelte @@ -28,7 +28,7 @@ const validItems = $derived( {#if introText}

{introText}

diff --git a/frontend/src/lib/geschichte/JourneyReader.svelte.spec.ts b/frontend/src/lib/geschichte/JourneyReader.svelte.spec.ts index 47e5d749..a80993f4 100644 --- a/frontend/src/lib/geschichte/JourneyReader.svelte.spec.ts +++ b/frontend/src/lib/geschichte/JourneyReader.svelte.spec.ts @@ -62,6 +62,17 @@ describe('JourneyReader', () => { await expect.element(page.getByText('Eine Reise durch die Geschichte.')).toBeVisible(); }); + it('intro paragraph uses readable body size (text-lg, #800)', async () => { + render(JourneyReader, { + context: ctx(), + props: { geschichte: baseGeschichte({ body: 'Eine Reise durch die Geschichte.' }) } + }); + + const intro = document.querySelector('p'); + expect(intro!.className).toContain('text-lg'); + expect(intro!.className).not.toContain('text-sm'); + }); + it('omits intro paragraph when body is null', async () => { render(JourneyReader, { context: ctx(),