fix(geschichte): use mode-aware journey tokens for interlude and badge
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m45s
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 4m8s
CI / fail2ban Regex (pull_request) Successful in 49s
CI / Semgrep Security Scan (pull_request) Successful in 21s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m6s

The reader interlude and the LESEREISE badge hardcoded orange-50/200/
400/700, leaving light text on a light background in dark mode. Switch
to the existing journey-tint/journey/journey-border tokens already used
by the list and editor rows.

Closes #801
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-10 22:42:26 +02:00
parent 9c524443cf
commit 2d38122833
5 changed files with 25 additions and 4 deletions

View File

@@ -631,7 +631,7 @@
<tr><td>Bedingte Logik</td><td>{#if geschichte.type === 'JOURNEY'} JourneyReader {:else} StoryReader {/if}</td><td>in +page.svelte von /geschichten/[id]</td></tr>
<tr><td>Artikel-Container</td><td>max-w-3xl mx-auto px-4 py-8</td><td>gleich wie StoryReader</td></tr>
<tr><td>Artikel-Sheet</td><td>rounded-sm border border-line bg-sheet shadow-sm px-5 py-6 sm:px-10 sm:py-10</td><td>Lesebogen-Panel zwischen Canvas und weißen Karten (Token --color-sheet), gleich wie Story (R-2); BackButton bleibt außerhalb</td></tr>
<tr><td>Journey-Badge</td><td>inline-flex px-2 py-px rounded-sm text-[10px] font-bold uppercase tracking-widest bg-orange-50 text-orange-700 border border-orange-200 mb-2</td><td>über dem Titel; nicht für STORY</td></tr>
<tr><td>Journey-Badge</td><td>inline-flex px-2 py-px rounded-sm text-[10px] font-bold uppercase tracking-widest bg-journey-tint text-journey border border-journey-border mb-2</td><td>über dem Titel; nicht für STORY</td></tr>
<tr><td>Titel</td><td>font-serif text-3xl text-ink leading-tight mb-4</td><td>gleich wie Story</td></tr>
<tr><td>Metabar</td><td>flex items-center gap-3 pb-4 border-b border-subtle mb-4</td><td>gleich wie Story</td></tr>
<tr><td>Bearbeiten/Löschen</td><td>nur BLOG_WRITE; auf Mobile im ··· BottomSheet</td><td>gleich wie Story</td></tr>
@@ -647,7 +647,7 @@
<tr><td>Annotation</td><td>mt-3 pl-3 border-l-2 border-brand-mint bg-muted rounded-r-sm py-1.5 pr-2</td><td>nur rendern wenn item.note vorhanden</td></tr>
<tr><td>Annotations-Text</td><td>text-base italic text-ink-2 leading-relaxed</td><td></td></tr>
<tr class="grp"><td colspan="3">Interlude-Item</td></tr>
<tr><td>Interlude-Block</td><td>pl-3 border-l-2 border-orange-400 bg-orange-50 rounded-r-sm py-2 pr-3 my-4</td><td>item.document === null</td></tr>
<tr><td>Interlude-Block</td><td>pl-3 border-l-2 border-journey-border bg-journey-tint rounded-r-sm py-2 pr-3 my-4</td><td>item.document === null</td></tr>
<tr><td>Interlude-Text</td><td>text-base italic text-ink leading-relaxed</td><td>item.note; plaintext</td></tr>
<tr class="grp"><td colspan="3">Mobile</td></tr>
<tr><td>··· Menü</td><td>ml-auto text-ink-3; öffnet BottomSheet mit Bearbeiten + Löschen</td><td>BLOG_WRITE; gleich wie Story</td></tr>

View File

@@ -11,7 +11,7 @@ let { note }: Props = $props();
<div
role="note"
aria-label={m.journey_interlude_aria_label()}
class="my-4 rounded-r-sm border-l-2 border-orange-400 bg-orange-50 py-2 pr-3 pl-3"
class="my-4 rounded-r-sm border-l-2 border-journey-border bg-journey-tint py-2 pr-3 pl-3"
>
<!-- plaintext — do NOT use {@html} here -->
<p class="text-base leading-relaxed text-ink italic">{note}</p>

View File

@@ -35,6 +35,15 @@ describe('JourneyInterlude', () => {
expect(el?.getAttribute('aria-label')).toBe(m.journey_interlude_aria_label());
});
it('uses mode-aware journey tokens, not raw orange utilities (#801)', async () => {
render(JourneyInterlude, { props: { note: 'Notiz' } });
const block = document.querySelector('[role="note"]');
expect(block!.className).toContain('bg-journey-tint');
expect(block!.className).toContain('border-journey-border');
expect(block!.className).not.toContain('bg-orange-50');
});
it('note text uses readable body size (text-base, #800)', async () => {
render(JourneyInterlude, { props: { note: 'Notiz' } });

View File

@@ -60,7 +60,7 @@ async function handleDelete() {
<header class="mb-6">
{#if isJourney}
<span
class="mb-2 inline-flex rounded-sm border border-orange-200 bg-orange-50 px-2 py-px text-[10px] font-bold tracking-widest text-orange-700 uppercase"
class="mb-2 inline-flex rounded-sm border border-journey-border bg-journey-tint px-2 py-px text-[10px] font-bold tracking-widest text-journey uppercase"
>
{m.journey_badge_detail()}
</span>

View File

@@ -84,6 +84,18 @@ describe('geschichten/[id] page', () => {
}
});
it('journey badge uses mode-aware journey tokens, not raw orange utilities (#801)', async () => {
render(GeschichtePage, {
context: new Map([[CONFIRM_KEY, createConfirmService()]]),
props: { data: baseData({ geschichte: baseGeschichte({ type: 'JOURNEY' }) }) }
});
const badge = document.querySelector('h1')!.parentElement!.querySelector('span');
expect(badge!.className).toContain('bg-journey-tint');
expect(badge!.className).toContain('text-journey');
expect(badge!.className).not.toContain('bg-orange-50');
});
it('renders the author full name from firstName + lastName', async () => {
render(GeschichtePage, {
context: new Map([[CONFIRM_KEY, createConfirmService()]]),