Add WCAG 2.1 Level AA accessibility across all components
A break timer designed to prevent RSI should be usable by people who already live with disabilities. This overhaul adds comprehensive accessibility without changing the visual design. Changes across 17 source files: - Global focus-visible outlines, sr-only utility, forced-colors support - prefers-reduced-motion kills all CSS animations AND JS Web Animations - All text upgraded to 4.5:1+ contrast ratio (WCAG AA) - Keyboard navigation for ColorPicker, Stepper, TimeSpinner - Screen reader: aria-live status regions, progressbar roles, labeled controls, sr-only chart data table, focus management on view changes - Focus trap on break screen, aria-hidden on decorative elements - Descriptive labels on all 25+ toggle/stepper instances in Settings - README updated with accessibility section and WCAG badge
This commit is contained in:
@@ -109,7 +109,7 @@
|
||||
}
|
||||
|
||||
// Day label
|
||||
ctx.fillStyle = "#444";
|
||||
ctx.fillStyle = "#8a8a8a";
|
||||
ctx.font = "10px -apple-system, sans-serif";
|
||||
ctx.textAlign = "center";
|
||||
const label = day.date.slice(5); // "MM-DD"
|
||||
@@ -117,6 +117,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Accessible chart summary
|
||||
const chartAriaLabel = $derived(() => {
|
||||
if (history.length === 0) return "No break history data available";
|
||||
const total = history.reduce((sum, d) => sum + d.breaksCompleted, 0);
|
||||
const skipped = history.reduce((sum, d) => sum + d.breaksSkipped, 0);
|
||||
return `Weekly break chart: ${total} completed and ${skipped} skipped over ${history.length} days`;
|
||||
});
|
||||
|
||||
function roundedRect(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
@@ -149,10 +157,11 @@
|
||||
aria-label="Back to dashboard"
|
||||
use:pressable
|
||||
class="mr-3 flex h-8 w-8 items-center justify-center rounded-full
|
||||
text-[#444] transition-colors hover:text-white"
|
||||
text-[#8a8a8a] transition-colors hover:text-white"
|
||||
onclick={goBack}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -167,6 +176,7 @@
|
||||
</button>
|
||||
<h1
|
||||
data-tauri-drag-region
|
||||
tabindex="-1"
|
||||
class="flex-1 text-lg font-medium text-white"
|
||||
>
|
||||
Statistics
|
||||
@@ -179,7 +189,7 @@
|
||||
<!-- 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-[#666] uppercase"
|
||||
class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase"
|
||||
>
|
||||
Today
|
||||
</h3>
|
||||
@@ -189,7 +199,7 @@
|
||||
<div class="text-[28px] font-semibold text-white tabular-nums">
|
||||
{stats?.todayCompleted ?? 0}
|
||||
</div>
|
||||
<div class="text-[11px] text-[#777]">Breaks taken</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Breaks taken</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold tabular-nums"
|
||||
@@ -197,19 +207,19 @@
|
||||
>
|
||||
{compliancePercent}%
|
||||
</div>
|
||||
<div class="text-[11px] text-[#777]">Compliance</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Compliance</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-[28px] font-semibold text-white tabular-nums">
|
||||
{breakTimeFormatted()}
|
||||
</div>
|
||||
<div class="text-[11px] text-[#777]">Break time</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">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-[#777]">Skipped</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Skipped</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -217,7 +227,7 @@
|
||||
<!-- 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-[#666] uppercase"
|
||||
class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase"
|
||||
>
|
||||
Streak
|
||||
</h3>
|
||||
@@ -225,7 +235,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[13px] text-white">Current streak</div>
|
||||
<div class="text-[11px] text-[#777]">Consecutive days with breaks</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">Consecutive days with breaks</div>
|
||||
</div>
|
||||
<div class="text-[24px] font-semibold tabular-nums" style="color: {$config.accent_color}">
|
||||
{stats?.currentStreak ?? 0}
|
||||
@@ -237,7 +247,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[13px] text-white">Best streak</div>
|
||||
<div class="text-[11px] text-[#777]">All-time record</div>
|
||||
<div class="text-[11px] text-[#8a8a8a]">All-time record</div>
|
||||
</div>
|
||||
<div class="text-[24px] font-semibold text-white tabular-nums">
|
||||
{stats?.bestStreak ?? 0}
|
||||
@@ -248,17 +258,39 @@
|
||||
<!-- 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-[#666] uppercase"
|
||||
class="mb-4 text-[11px] font-medium tracking-[0.15em] text-[#8a8a8a] uppercase"
|
||||
>
|
||||
Last 7 Days
|
||||
</h3>
|
||||
|
||||
<!-- svelte-ignore a11y_no_interactive_element_to_noninteractive_role -->
|
||||
<canvas
|
||||
bind:this={chartCanvas}
|
||||
class="h-[140px] w-full"
|
||||
role="img"
|
||||
aria-label={chartAriaLabel()}
|
||||
></canvas>
|
||||
|
||||
<div class="mt-3 flex items-center justify-center gap-4 text-[10px] text-[#777]">
|
||||
<!-- Screen-reader accessible data table for the chart -->
|
||||
{#if history.length > 0}
|
||||
<table class="sr-only">
|
||||
<caption>Break history for the last {history.length} days</caption>
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Completed</th><th>Skipped</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each history 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-[#8a8a8a]">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="h-2 w-2 rounded-sm" style="background: {$config.accent_color}"></div>
|
||||
Completed
|
||||
|
||||
Reference in New Issue
Block a user