This commit is contained in:
2026-02-07 12:10:10 +02:00
parent 1811be3135
commit a48cbb1533
18 changed files with 459 additions and 226 deletions

View File

@@ -37,6 +37,16 @@
: "PAUSED",
);
// Track status changes for aria-live region (announce only on change, not every tick)
let lastAnnouncedStatus = $state("");
let statusAnnouncement = $state("");
$effect(() => {
if (statusText !== lastAnnouncedStatus) {
lastAnnouncedStatus = statusText;
statusAnnouncement = `Timer status: ${statusText}. ${formatTime($timer.timeRemaining)} remaining.`;
}
});
const toggleBtnText = $derived(
$timer.state === "running" ? "PAUSE" : "START",
);
@@ -87,6 +97,9 @@
<svelte:window bind:innerWidth={windowW} bind:innerHeight={windowH} />
<h1 class="sr-only" tabindex="-1">Dashboard</h1>
<div aria-live="polite" class="sr-only">{statusAnnouncement}</div>
<div class="relative flex h-full flex-col items-center justify-center">
<!-- Outer: responsive scaling (JS-driven), Inner: mount animation -->
<div style="transform: scale({ringScale}); transform-origin: center center; margin-bottom: {ringMargin}px;">
@@ -96,17 +109,20 @@
size={280}
strokeWidth={8}
accentColor={$config.accent_color}
label="Focus timer"
valueText="{formatTime($timer.timeRemaining)} remaining"
>
<!-- Counter-scale wrapper: text shrinks less than ring -->
<div style="transform: scale({textCounterScale}); transform-origin: center center;">
<!-- Eye icon -->
<svg
aria-hidden="true"
class="mx-auto mb-3 eye-blink"
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="#444"
stroke="#888"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
@@ -130,10 +146,7 @@
<!-- Status label -->
<span
class="block text-center text-[11px] font-medium tracking-[0.25em]"
class:text-[#444]={!$timer.prebreakWarning &&
$timer.state === "running"}
class:text-[#333]={!$timer.prebreakWarning &&
$timer.state === "paused"}
class:text-[#8a8a8a]={!$timer.prebreakWarning}
class:text-warning={$timer.prebreakWarning}
>
{statusText}
@@ -146,7 +159,7 @@
<!-- Last break info -->
<div use:fadeIn={{ delay: 0.4, y: 10 }}>
{#if $timer.hasHadBreak}
<p style="margin-bottom: {ringMargin}px;" class="text-[12px] text-[#2a2a2a]">
<p style="margin-bottom: {ringMargin}px;" class="text-[12px] text-[#8a8a8a]">
Last break {formatDurationAgo($timer.secondsSinceLastBreak)}
</p>
{:else}
@@ -157,12 +170,13 @@
<!-- Natural break notification toast -->
{#if showNaturalBreakToast}
<div
role="alert"
class="absolute top-6 left-1/2 -translate-x-1/2 rounded-2xl border px-5 py-3 text-center backdrop-blur-xl transition-all duration-500"
style="border-color: rgba(63, 185, 80, 0.3); background: rgba(63, 185, 80, 0.1);"
use:scaleIn={{ duration: 0.3, delay: 0 }}
>
<div class="flex items-center gap-2">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#3fb950" stroke-width="2">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#3fb950" stroke-width="2">
<path d="M20 6L9 17l-5-5"/>
</svg>
<span class="text-[13px] text-[#3fb950]">Natural break detected - timer reset</span>
@@ -190,12 +204,13 @@
aria-label="Start break now"
use:pressable
class="flex h-11 w-11 items-center justify-center rounded-full
border border-[#222] text-[#383838]
border border-[#222] text-[#8a8a8a]
transition-colors duration-200
hover:border-[#333] hover:text-[#666]"
hover:border-[#333] hover:text-[#aaa]"
onclick={startBreakNow}
>
<svg
aria-hidden="true"
width="18"
height="18"
viewBox="0 0 24 24"
@@ -216,15 +231,16 @@
aria-label="Statistics"
use:pressable
class="flex h-11 w-11 items-center justify-center rounded-full
border border-[#222] text-[#383838]
border border-[#222] text-[#8a8a8a]
transition-colors duration-200
hover:border-[#333] hover:text-[#666]"
hover:border-[#333] hover:text-[#aaa]"
onclick={() => {
invoke("set_view", { view: "stats" });
currentView.set("stats");
}}
>
<svg
aria-hidden="true"
width="17"
height="17"
viewBox="0 0 24 24"
@@ -247,12 +263,13 @@
aria-label="Settings"
use:pressable
class="flex h-11 w-11 items-center justify-center rounded-full
border border-[#222] text-[#383838]
border border-[#222] text-[#8a8a8a]
transition-colors duration-200
hover:border-[#333] hover:text-[#666]"
hover:border-[#333] hover:text-[#aaa]"
onclick={openSettings}
>
<svg
aria-hidden="true"
width="17"
height="17"
viewBox="0 0 24 24"