a11y: Tasks 7-12 - Dashboard, Settings, StatsView, BreakScreen, Celebration
- 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 <header>
This commit is contained in:
@@ -266,8 +266,8 @@
|
||||
<button
|
||||
aria-label="Back to dashboard"
|
||||
use:pressable
|
||||
class="mr-3 flex h-8 w-8 items-center justify-center rounded-full
|
||||
text-[#8a8a8a] transition-colors hover:text-white"
|
||||
class="mr-3 flex h-10 w-10 min-h-[44px] min-w-[44px] items-center justify-center rounded-full
|
||||
text-text-sec transition-colors hover:text-white"
|
||||
onclick={goBack}
|
||||
>
|
||||
<svg
|
||||
@@ -294,14 +294,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Tab navigation -->
|
||||
<div class="flex gap-1 px-5 mb-3" use:fadeIn={{ duration: 0.3, y: 6 }}>
|
||||
<div class="flex gap-1 px-5 mb-3" role="tablist" aria-label="Statistics time range" use:fadeIn={{ duration: 0.3, y: 6 }}>
|
||||
{#each [["today", "Today"], ["weekly", "Weekly"], ["monthly", "Monthly"]] as [tab, label]}
|
||||
<button
|
||||
use:pressable
|
||||
class="rounded-lg px-4 py-1.5 text-[11px] tracking-wider uppercase transition-all duration-200
|
||||
role="tab"
|
||||
id="tab-{tab}"
|
||||
aria-selected={activeTab === tab}
|
||||
aria-controls="tabpanel-{tab}"
|
||||
class="min-h-[44px] rounded-lg px-4 py-1.5 text-[11px] tracking-wider uppercase transition-all duration-200
|
||||
{activeTab === tab
|
||||
? 'bg-[#1a1a1a] text-white'
|
||||
: 'text-[#8a8a8a] hover:text-white'}"
|
||||
: 'text-text-sec hover:text-white'}"
|
||||
onclick={() => activeTab = tab as any}
|
||||
>
|
||||
{label}
|
||||
@@ -314,18 +318,19 @@
|
||||
<div class="space-y-3">
|
||||
|
||||
{#if activeTab === "today"}
|
||||
<div role="tabpanel" id="tabpanel-today" aria-labelledby="tab-today">
|
||||
<!-- Today's summary -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Today
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold text-white tabular-nums">
|
||||
{stats?.todayCompleted ?? 0}
|
||||
</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Breaks taken</div>
|
||||
<div class="text-[11px] text-text-sec">Breaks taken</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold tabular-nums"
|
||||
@@ -333,19 +338,19 @@
|
||||
>
|
||||
{compliancePercent}%
|
||||
</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Compliance</div>
|
||||
<div class="text-[11px] text-text-sec">Compliance</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold text-white tabular-nums">
|
||||
{breakTimeFormatted()}
|
||||
</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Break time</div>
|
||||
<div class="text-[11px] text-text-sec">Break time</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold text-white tabular-nums">
|
||||
{stats?.todaySkipped ?? 0}
|
||||
</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Skipped</div>
|
||||
<div class="text-[11px] text-text-sec">Skipped</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -353,9 +358,9 @@
|
||||
<!-- F10: Daily goal -->
|
||||
{#if $config.daily_goal_enabled}
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0.04 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Daily Goal
|
||||
</h3>
|
||||
</h2>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="relative w-16 h-16">
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" style="transform: rotate(-90deg);">
|
||||
@@ -375,7 +380,7 @@
|
||||
<div class="text-[14px] text-white font-medium">
|
||||
{stats?.dailyGoalProgress ?? 0} / {$config.daily_goal_breaks} breaks
|
||||
</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">
|
||||
<div class="text-[11px] text-text-sec">
|
||||
{stats?.dailyGoalMet ? "Goal reached!" : `${$config.daily_goal_breaks - (stats?.dailyGoalProgress ?? 0)} more to go`}
|
||||
</div>
|
||||
</div>
|
||||
@@ -385,26 +390,26 @@
|
||||
|
||||
<!-- Streak -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0.06 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Streak
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[13px] text-white">Current streak</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Consecutive days with breaks</div>
|
||||
<div class="text-[11px] text-text-sec">Consecutive days with breaks</div>
|
||||
</div>
|
||||
<div class="text-[24px] font-semibold tabular-nums" style="color: {$config.accent_color}">
|
||||
{stats?.currentStreak ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4 h-px bg-[#161616]"></div>
|
||||
<div class="my-4 h-px bg-border"></div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[13px] text-white">Best streak</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">All-time record</div>
|
||||
<div class="text-[11px] text-text-sec">All-time record</div>
|
||||
</div>
|
||||
<div class="text-[24px] font-semibold text-white tabular-nums">
|
||||
{stats?.bestStreak ?? 0}
|
||||
@@ -413,13 +418,13 @@
|
||||
|
||||
<!-- F10: Next milestone -->
|
||||
{#if nextMilestone()}
|
||||
<div class="my-4 h-px bg-[#161616]"></div>
|
||||
<div class="my-4 h-px bg-border"></div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[13px] text-white">Next milestone</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">{nextMilestone()} day streak</div>
|
||||
<div class="text-[11px] text-text-sec">{nextMilestone()} day streak</div>
|
||||
</div>
|
||||
<div class="text-[13px] text-[#8a8a8a] tabular-nums">
|
||||
<div class="text-[13px] text-text-sec tabular-nums">
|
||||
{nextMilestone()! - (stats?.currentStreak ?? 0)} days away
|
||||
</div>
|
||||
</div>
|
||||
@@ -428,9 +433,9 @@
|
||||
|
||||
<!-- Weekly chart -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0.12 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Last 7 Days
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<!-- svelte-ignore a11y_no_interactive_element_to_noninteractive_role -->
|
||||
<canvas
|
||||
@@ -458,7 +463,7 @@
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-4 text-[10px] text-[#8a8a8a]">
|
||||
<div class="mt-3 flex items-center justify-center gap-4 text-[10px] text-text-sec">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="h-2 w-2 rounded-sm" style="background: {$config.accent_color}"></div>
|
||||
Completed
|
||||
@@ -470,35 +475,37 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{:else if activeTab === "weekly"}
|
||||
<div role="tabpanel" id="tabpanel-weekly" aria-labelledby="tab-weekly">
|
||||
<!-- Weekly summaries -->
|
||||
{#each weeklySummaries as week, i}
|
||||
{@const prevWeek = weeklySummaries[i + 1]}
|
||||
{@const trend = prevWeek ? week.complianceRate - prevWeek.complianceRate : 0}
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: i * 0.06 }}>
|
||||
<h3 class="mb-3 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-3 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Week of {week.weekStart}
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-3 gap-3 mb-3">
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold text-white tabular-nums">{week.totalCompleted}</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Completed</div>
|
||||
<div class="text-[10px] text-text-sec">Completed</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold text-white tabular-nums">{week.totalSkipped}</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Skipped</div>
|
||||
<div class="text-[10px] text-text-sec">Skipped</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold tabular-nums" style="color: {$config.accent_color}">
|
||||
{Math.round(week.complianceRate * 100)}%
|
||||
</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Compliance</div>
|
||||
<div class="text-[10px] text-text-sec">Compliance</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between text-[11px]">
|
||||
<span class="text-[#8a8a8a]">Avg {week.avgDailyCompleted.toFixed(1)} breaks/day</span>
|
||||
<span class="text-text-sec">Avg {week.avgDailyCompleted.toFixed(1)} breaks/day</span>
|
||||
{#if prevWeek}
|
||||
<span class="flex items-center gap-1"
|
||||
style="color: {trend > 0 ? '#3fb950' : trend < 0 ? '#f85149' : '#8a8a8a'};"
|
||||
@@ -517,12 +524,14 @@
|
||||
</section>
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<div role="tabpanel" id="tabpanel-monthly" aria-labelledby="tab-monthly">
|
||||
<!-- Monthly: 30-day chart -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Last 30 Days
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<!-- svelte-ignore a11y_no_interactive_element_to_noninteractive_role -->
|
||||
<canvas
|
||||
@@ -532,7 +541,21 @@
|
||||
aria-label="30-day break history chart"
|
||||
></canvas>
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-4 text-[10px] text-[#8a8a8a]">
|
||||
{#if monthHistory.length > 0}
|
||||
<table class="sr-only">
|
||||
<caption>Break history for the last {monthHistory.length} days</caption>
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Completed</th><th>Skipped</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each monthHistory as day}
|
||||
<tr><td>{day.date}</td><td>{day.breaksCompleted}</td><td>{day.breaksSkipped}</td></tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-4 text-[10px] text-text-sec">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="h-2 w-2 rounded-sm" style="background: {$config.accent_color}"></div>
|
||||
Completed
|
||||
@@ -546,9 +569,9 @@
|
||||
|
||||
<!-- Heatmap -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0.06 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Activity Heatmap
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<!-- svelte-ignore a11y_no_interactive_element_to_noninteractive_role -->
|
||||
@@ -559,7 +582,21 @@
|
||||
></canvas>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-2 text-[10px] text-[#8a8a8a]">
|
||||
{#if monthHistory.length > 0}
|
||||
<table class="sr-only">
|
||||
<caption>Activity heatmap for the last {monthHistory.length} days</caption>
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Breaks completed</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each monthHistory as day}
|
||||
<tr><td>{day.date}</td><td>{day.breaksCompleted}</td></tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-2 text-[10px] text-text-sec">
|
||||
<span>Less</span>
|
||||
<div class="flex gap-1">
|
||||
<div class="w-3 h-3 rounded-sm" style="background: #161616;"></div>
|
||||
@@ -574,35 +611,36 @@
|
||||
|
||||
<!-- Monthly totals -->
|
||||
<section class="rounded-2xl p-5 backdrop-blur-xl" style="background: rgba(17,17,17,0.7);" use:inView={{ delay: 0.12 }}>
|
||||
<h3 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase">
|
||||
<h2 class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
|
||||
Monthly Summary
|
||||
</h3>
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold text-white tabular-nums">{monthTotalCompleted}</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Total breaks</div>
|
||||
<div class="text-[10px] text-text-sec">Total breaks</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold tabular-nums" style="color: {$config.accent_color}">
|
||||
{monthAvgCompliance()}%
|
||||
</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Avg compliance</div>
|
||||
<div class="text-[10px] text-text-sec">Avg compliance</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold text-white tabular-nums">
|
||||
{Math.floor(monthTotalTime / 60)} min
|
||||
</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Total break time</div>
|
||||
<div class="text-[10px] text-text-sec">Total break time</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[22px] font-semibold text-white tabular-nums">
|
||||
{(monthTotalCompleted / 30).toFixed(1)}
|
||||
</div>
|
||||
<div class="text-[10px] text-[#8a8a8a]">Avg daily breaks</div>
|
||||
<div class="text-[10px] text-text-sec">Avg daily breaks</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user