From acf06c8d32e2782272cc361c8813e0bb7f14b684 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 18 Feb 2026 18:15:06 +0200 Subject: [PATCH] a11y: Tasks 7-12 - Dashboard, Settings, StatsView, BreakScreen, Celebration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dashboard: text-text-sec tokens, nav landmark, toast hover persistence, goal progressbar ARIA, pomodoro sr-only text - Settings: h3→h2 heading hierarchy, section aria-labelledby with ids, Working Hours heading added - StatsView: h3→h2, tablist/tab/tabpanel ARIA pattern, sr-only data tables for 30-day chart and heatmap, contrast tokens - BreakScreen: strict-mode focus safety span, breathing phase-only announcements, contrast tokens - Celebration: JS-controlled hover/focus persistence, dismiss buttons, Escape key, removed pointer-events:none - Titlebar: removed redundant role="banner" on
--- src/lib/components/BreakScreen.svelte | 52 +++++-- src/lib/components/Celebration.svelte | 171 +++++++++++++++++++-- src/lib/components/Dashboard.svelte | 204 ++++++++++++++------------ src/lib/components/Settings.svelte | 107 +++++++------- src/lib/components/StatsView.svelte | 124 ++++++++++------ src/lib/components/Titlebar.svelte | 1 - 6 files changed, 443 insertions(+), 216 deletions(-) diff --git a/src/lib/components/BreakScreen.svelte b/src/lib/components/BreakScreen.svelte index ccff681..6f420a0 100644 --- a/src/lib/components/BreakScreen.svelte +++ b/src/lib/components/BreakScreen.svelte @@ -74,6 +74,18 @@ let breathCountdown = $state(4); let breathScale = $state(0.6); + // Only announce phase name changes (not countdown ticks) to screen readers + let breathAnnouncement = $state(""); + let lastBreathPhase = $state(""); + $effect(() => { + // Extract just the phase name (e.g., "Inhale" from "Inhale 4") + const phaseName = breathPhase?.split(' ')[0] ?? ""; + if (phaseName && phaseName !== lastBreathPhase) { + lastBreathPhase = phaseName; + breathAnnouncement = phaseName; + } + }); + // Map raw 0.6–1.0 scale to 0.9–1.6 range for visible breathing text const textScale = $derived(0.9 + (breathScale - 0.6) * (0.7 / 0.4)); @@ -169,11 +181,11 @@ + {breathAnnouncement} {/if} @@ -185,16 +197,16 @@

{$timer.breakTitle}

-

+

{$timer.breakMessage}

{#if $config.show_break_activities}
-
+
{getCategoryLabel(currentActivity.category)}
-

+

{currentActivity.text}

@@ -205,7 +217,7 @@
@@ -302,11 +319,11 @@ class:text-[10px]={!isModal} class:text-[9px]={isModal} style="transform: scale({textScale}); color: {breathColor}; opacity: {0.5 + breathT * 0.5}; transition: transform 0.15s ease-out, opacity 0.15s ease-out, color 0.15s ease-out;" - aria-live="polite" aria-atomic="true" - aria-label="Breathing guide: {breathPhase} {breathCountdown > 0 ? breathCountdown + ' seconds' : ''}" + aria-hidden="true" > {breathPhase}{breathCountdown > 0 ? ` ${breathCountdown}s` : ""} + {breathAnnouncement} {/if} @@ -328,7 +345,7 @@

-

+
{getCategoryLabel(currentActivity.category)}
-

+

{currentActivity.text}

@@ -354,8 +371,8 @@
+
{#each confettiParticles as p (p.id)} @@ -51,12 +160,31 @@ {/if} {#if showGoal && !showMilestone} - {/each}
- + {$timer.pomodoroCyclePosition + 1}/{$timer.pomodoroTotalInCycle}
+ Pomodoro cycle: session {$timer.pomodoroCyclePosition + 1} of {$timer.pomodoroTotalInCycle} {/if} {#if $timer.microbreakEnabled && !$timer.microbreakActive && $timer.state === "running"} -
+
Goal met {:else} - Goal -
+ Goal +
- + {dailyGoalProgress}/{$config.daily_goal_breaks} {/if} @@ -253,7 +265,7 @@
{#if $timer.hasHadBreak} -

+

Last break {formatDurationAgo($timer.secondsSinceLastBreak)}

{:else} @@ -263,17 +275,24 @@ {#if showNaturalBreakToast} + {/if} @@ -292,94 +311,97 @@ {toggleBtnText} - -
- -
+ + +
- -
- -
+ + +
- -
- -
+ + +
+