- Upgrade accessibility from WCAG 2.1 AA to WCAG 2.2 AAA conformance - 42 accessibility fixes across 18 frontend components - Enhanced contrast ratios (7:1 body text, 4.5:1 large text, 3:1 non-text) - 44px minimum touch/click targets on all interactive elements - Full WAI-ARIA 1.2: tablist, radiogroup, alertdialog, progressbar, switch - Screen-reader-only data tables behind chart, dynamic page titles - Skip navigation link, semantic heading hierarchy (h1 > h2) - Celebration popups persist on hover/focus with keyboard dismiss - Fix ToggleSwitch visual height (44px hit area, 28px visual track) - Update README with detailed WCAG 2.2 AAA accessibility section - Include WCAG design doc and implementation plan in docs/plans/
27 KiB
WCAG 2.2 AAA Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Achieve WCAG 2.2 AAA conformance across all frontend components while preserving the existing dark-theme visual identity.
Architecture: All changes are CSS + Svelte template only — no Rust backend changes. Theme tokens in app.css propagate through Tailwind's @theme system. Components use Svelte 5 runes ($state, $derived, $effect, $props). Accessibility patterns follow WAI-ARIA 1.2 (tablist, radiogroup, alertdialog, progressbar).
Tech Stack: Svelte 5, Tailwind CSS v4 (@theme tokens in CSS, no config file), TypeScript, Web Animations API (motion library)
Note: This project has no frontend test suite. Verification is done via npm run build (Vite build) and manual inspection. Each task ends with a build check and a commit.
Task 1: Theme Tokens & Global Styles (app.css)
Files:
- Modify:
break-timer/src/app.css(entire file)
This is the foundation — all subsequent tasks depend on these token changes.
Step 1: Update theme tokens
In app.css, inside the @theme { } block (lines 3–20):
- Change
--color-text-sec: #8a8a8a→--color-text-sec: #a8a8a8(7.28:1 on black) - Change
--color-text-dim: #3a3a3a→--color-text-dim: #5c5c5c(3.5:1 decorative) - Change
--color-border: #222222→--color-border: #3a3a3a(2.63:1 non-text) - Change
--color-danger: #f85149→--color-danger: #ff6b6b(7.41:1) - Add new token:
--color-input-border: #444444; - Add new token:
--color-surface-lt: #1e1e1e;
Step 2: Update body styles
In the html, body block (lines 22–35):
- Change
user-select: noneto only apply on drag regions:- REMOVE
user-select: none;and-webkit-user-select: none;from body - ADD a new rule for drag regions only:
[data-tauri-drag-region], [data-tauri-drag-region] * { user-select: none; -webkit-user-select: none; }
- REMOVE
- Add
line-height: 1.625;(leading-relaxed) to body for AAA 1.4.8
Step 3: Enhance focus indicators
Replace the :focus-visible block (lines 73–76) with:
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.3);
}
The box-shadow provides a white fallback when the accent color has low contrast against dark backgrounds.
Step 4: Add skip link styles
After the .sr-only block, add:
.skip-link {
position: absolute;
top: -100%;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
padding: 8px 16px;
background: var(--color-accent);
color: #000;
font-size: 13px;
font-weight: 600;
border-radius: 0 0 8px 8px;
text-decoration: none;
transition: top 0.15s ease;
}
.skip-link:focus {
top: 0;
}
Step 5: Verify build
Run: cd break-timer && npm run build
Expected: Build succeeds with no errors.
Step 6: Commit
git add break-timer/src/app.css
git commit -m "a11y: update theme tokens and global styles for WCAG AAA
- Bump --color-text-sec to #a8a8a8 (7.28:1 contrast)
- Bump --color-text-dim to #5c5c5c (3.5:1 decorative)
- Bump --color-border to #3a3a3a (2.63:1 non-text)
- Bump --color-danger to #ff6b6b (7.41:1)
- Add --color-input-border and --color-surface-lt tokens
- Add white shadow fallback on :focus-visible
- Add leading-relaxed default line-height
- Scope user-select:none to drag regions only
- Add skip-link focus styles"
Task 2: App Shell (App.svelte)
Files:
- Modify:
break-timer/src/App.svelte
Step 1: Add skip link
Inside the <main> element (line 87), add skip link as first child and an id target:
<main class="relative h-full bg-black">
<a href="#main-content" class="skip-link">Skip to content</a>
...
Then on the zoom container <div> (line 92), add id="main-content":
<div
id="main-content"
class="relative h-full overflow-hidden origin-top-left"
Step 2: Add document title effect
After the existing focus management $effect (after line 76), add:
// WCAG 2.4.2: Document title reflects current view
$effect(() => {
const viewNames: Record<string, string> = {
dashboard: "Dashboard",
breakScreen: "Break",
settings: "Settings",
stats: "Statistics",
};
document.title = `Core Cooldown — ${viewNames[effectiveView] ?? "Dashboard"}`;
});
Step 3: Verify build
Run: cd break-timer && npm run build
Step 4: Commit
git add break-timer/src/App.svelte
git commit -m "a11y: add skip link and dynamic document title for WCAG AAA"
Task 3: Titlebar (Titlebar.svelte)
Files:
- Modify:
break-timer/src/lib/components/Titlebar.svelte
Step 1: Wrap in header landmark
Change the outer <div> (line 8) to <header role="banner">.
Step 2: Enlarge traffic lights to meet 44px target size
Change each traffic light button from h-[15px] w-[15px] to h-[20px] w-[20px] visual size with min-h-[44px] min-w-[44px] hit area via padding. The trick: keep the visual circle at 20px but wrap in a 44px invisible tap area.
Replace each button's class. For example the Maximize button (line 27):
<button
aria-label="Maximize"
class="traffic-btn group/btn relative flex h-[44px] w-[44px] items-center justify-center"
onclick={() => appWindow.toggleMaximize()}
>
<span class="flex h-[20px] w-[20px] items-center justify-center rounded-full bg-[#27C93F] transition-all duration-150 group-hover/btn:brightness-110">
<svg ...>
</span>
</button>
Apply the same pattern to Minimize and Close buttons. The gap between buttons changes from gap-[8px] to gap-0 since the 44px buttons provide their own spacing.
Step 3: Verify build
Run: cd break-timer && npm run build
Step 4: Commit
git add break-timer/src/lib/components/Titlebar.svelte
git commit -m "a11y: add header landmark and enlarge traffic lights to 44px hit areas"
Task 4: ToggleSwitch (ToggleSwitch.svelte)
Files:
- Modify:
break-timer/src/lib/components/ToggleSwitch.svelte
Step 1: Enlarge to 52x28 and fix knob contrast
Change button dimensions from h-[24px] w-[48px] to h-[28px] w-[52px] with min-h-[44px] padding for hit area.
Change the knob span from h-[19px] w-[19px] to h-[22px] w-[22px].
Change translate-x for ON state from translate-x-[26px] to translate-x-[27px].
Change mt from mt-[2.5px] to mt-[3px].
Change OFF knob color from bg-[#444] to bg-[#666] (better contrast).
The button should have min-h-[44px] via a wrapper approach or padding.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/ToggleSwitch.svelte
git commit -m "a11y: enlarge toggle switch and improve OFF knob contrast"
Task 5: Stepper (Stepper.svelte)
Files:
- Modify:
break-timer/src/lib/components/Stepper.svelte
Step 1: Enlarge buttons and fix contrast
Change both +/- button dimensions from h-7 w-7 (28px) to h-9 w-9 (36px) with min-h-[44px] min-w-[44px] padding.
Change bg-[#141414] to bg-[#1a1a1a] border border-[#3a3a3a] for better non-text contrast.
Change text-[#8a8a8a] to text-text-sec (uses updated theme token).
Step 2: Add keyboard hold-to-repeat
Add onkeydown handlers to both buttons that trigger the same startHold/stopHold logic for Enter, Space, ArrowUp, ArrowDown keys:
function handleKeydown(fn: () => void, e: KeyboardEvent) {
if (["Enter", " ", "ArrowUp", "ArrowDown"].includes(e.key)) {
e.preventDefault();
startHold(e.key === "ArrowDown" || e.key === "ArrowUp" ? fn : fn);
}
}
function handleKeyup(e: KeyboardEvent) {
if (["Enter", " ", "ArrowUp", "ArrowDown"].includes(e.key)) {
stopHold();
}
}
Add onkeydown and onkeyup to both buttons. The decrease button uses ArrowDown for decrement, the increase button uses ArrowUp for increment.
Step 3: Verify build
Run: cd break-timer && npm run build
Step 4: Commit
git add break-timer/src/lib/components/Stepper.svelte
git commit -m "a11y: enlarge stepper buttons and add keyboard hold-to-repeat"
Task 6: Animation Actions (animate.ts)
Files:
- Modify:
break-timer/src/lib/utils/animate.ts
Step 1: Add keyboard support to pressable
In the pressable function (line 117), after the mousedown/mouseup/mouseleave listeners (lines 143–145), add keydown/keyup for Enter and Space:
function onKeyDown(e: KeyboardEvent) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onDown();
}
}
function onKeyUp(e: KeyboardEvent) {
if (e.key === "Enter" || e.key === " ") {
onUp();
}
}
node.addEventListener("keydown", onKeyDown);
node.addEventListener("keyup", onKeyUp);
Update destroy() to remove these listeners too.
Step 2: Add focus support to glowHover
In the glowHover function (line 165), after mouseenter/mouseleave listeners (lines 201–202), add focusin/focusout:
node.addEventListener("focusin", onEnter);
node.addEventListener("focusout", onLeave);
Update destroy() to remove these listeners too.
Step 3: Verify build
Run: cd break-timer && npm run build
Step 4: Commit
git add break-timer/src/lib/utils/animate.ts
git commit -m "a11y: add keyboard feedback to pressable and focus glow to glowHover"
Task 7: Dashboard (Dashboard.svelte)
Files:
- Modify:
break-timer/src/lib/components/Dashboard.svelte
Step 1: Replace hardcoded #8a8a8a with theme token
Replace all text-[#8a8a8a] with text-text-sec throughout the file. There are ~10 instances on lines 180, 208, 216, 234, 241, 256, 301, 329, 361.
Replace border-[#222] with border-border on the three bottom buttons (lines 302, 328, 360).
Step 2: Wrap bottom buttons in nav landmark
Around the three bottom action buttons (lines 296–382), wrap in:
<nav aria-label="Main actions">
<!-- Bottom left: start break now -->
...
<!-- Bottom center: stats -->
...
<!-- Bottom right: settings -->
...
</nav>
The three <div class="absolute bottom-5 ..."> blocks move inside the <nav>.
Step 3: Make natural break toast persistent on hover
Replace the simple timeout logic (lines 118–126) with hover-aware persistence:
let toastHovering = $state(false);
$effect(() => {
if ($timer.naturalBreakOccurred) {
showNaturalBreakToast = true;
if (naturalBreakToastTimeout) clearTimeout(naturalBreakToastTimeout);
naturalBreakToastTimeout = setTimeout(() => {
if (!toastHovering) showNaturalBreakToast = false;
}, 5000);
}
});
On the toast div (line 266), add:
onmouseenter={() => toastHovering = true}
onmouseleave={() => { toastHovering = false; showNaturalBreakToast = false; }}
onfocusin={() => toastHovering = true}
onfocusout={() => { toastHovering = false; showNaturalBreakToast = false; }}
Add a close button inside the toast and an Escape key handler.
Step 4: Add progressbar role to daily goal bar
On the goal progress bar div (line 235), add ARIA:
<div class="w-16 h-[2px] rounded-full overflow-hidden" style="background: #161616;"
role="progressbar"
aria-label="Daily goal progress"
aria-valuemin={0}
aria-valuemax={$config.daily_goal_breaks}
aria-valuenow={dailyGoalProgress}
>
Step 5: Add sr-only text for pomodoro dots
After the pomodoro dots visual <div> (around line 192), add:
<span class="sr-only">
Pomodoro cycle: session {$timer.pomodoroCyclePosition + 1} of {$timer.pomodoroTotalInCycle}
</span>
Step 6: Verify build
Run: cd break-timer && npm run build
Step 7: Commit
git add break-timer/src/lib/components/Dashboard.svelte
git commit -m "a11y: dashboard contrast, nav landmark, toast persistence, goal progressbar"
Task 8: Settings — Part 1: Headings & Structure (Settings.svelte)
Files:
- Modify:
break-timer/src/lib/components/Settings.svelte
This is a large file (1176 lines). Split into two commits for reviewability.
Step 1: Change all <h3> to <h2>
Replace every <h3 class="mb-4 text-[11px] ... with <h2 class="mb-4 text-[11px] ... and corresponding </h3> with </h2>. There are 17 instances at approximate lines: 163, 218, 280, 346, 435, 444, 498, 590, 668, 732, 825, 862, 906, 976(Working Hours has no heading — add one), 1078, 1119, 1138.
Step 2: Add id to headings and aria-labelledby to sections
Each <section> gets an aria-labelledby pointing to its heading's id. Pattern:
<section aria-labelledby="settings-timer" class="rounded-2xl p-5 ...">
<h2 id="settings-timer" class="mb-4 ...">Timer</h2>
Do this for all 18 sections. Use ids: settings-timer, settings-pomodoro, settings-microbreaks, settings-breakscreen, settings-activities, settings-breathing, settings-behavior, settings-alerts, settings-sound, settings-idle, settings-presentation, settings-goals, settings-appearance, settings-workinghours, settings-minimode, settings-general, settings-shortcuts.
Step 3: Add missing heading for Working Hours section
The Working Hours section (line 975) has no <h3>/<h2>. Add:
<h2 id="settings-workinghours" class="mb-4 text-[11px] font-medium tracking-[0.15em] text-text-sec uppercase">
Working Hours
</h2>
Step 4: Verify build
Run: cd break-timer && npm run build
Step 5: Commit
git add break-timer/src/lib/components/Settings.svelte
git commit -m "a11y: settings heading hierarchy (h3→h2) and section landmarks"
Task 9: Settings — Part 2: ARIA, Contrast, Sizes (Settings.svelte)
Files:
- Modify:
break-timer/src/lib/components/Settings.svelte
Step 1: Breathing pattern — radiogroup/radio
Wrap the breathing pattern buttons (line 464) in a role="radiogroup" container:
<div role="radiogroup" aria-label="Breathing pattern" class="flex flex-col gap-1.5">
Each breathing pattern button gets role="radio" and aria-checked:
<button
role="radio"
aria-checked={$config.breathing_pattern === bp.id}
...
Step 2: Sound preset — aria-pressed
Each sound preset button (line 709) gets aria-pressed:
<button
aria-pressed={$config.sound_preset === preset}
...
Step 3: Replace hardcoded colors
- All
text-[#8a8a8a]→text-text-sec(many instances throughout) - All
bg-[#161616]dividers →bg-border(use--color-bordertoken) - All
border-[#161616]on inputs →border-border - All
placeholder:text-[#2a2a2a]→placeholder:text-[#555](3.37:1) - All
bg-[#141414]→ use updated stepper component (already handled in Task 5) - Back button
h-8 w-8→h-10 w-10 min-h-[44px] min-w-[44px](line 127)
Step 4: Reset button aria-live
Wrap the reset button text in an aria-live="polite" region, or add aria-live="polite" to the button itself so screen readers announce the confirmation state change:
<button
aria-live="polite"
...
>
{resetConfirming ? "Tap again to confirm reset" : "Reset to defaults"}
</button>
Step 5: Add abbreviation tags
For standalone unit abbreviations in setting descriptions, wrap with <abbr>:
"s"(seconds) →<abbr title="seconds">s</abbr>"min"→<abbr title="minutes">min</abbr>
Apply on first occurrence in: alert timing description (line 613), idle timeout (line 755), snooze duration (line 537).
Step 6: Add title tooltips on complex settings
Add title attributes on the section-level labels for Pomodoro, Smart breaks, Microbreaks, Breathing:
- Pomodoro
<h2>:title="Pomodoro technique alternates focused work sessions with short and long breaks" - Microbreaks
<h2>:title="20-20-20 rule: every 20 minutes, look 20 feet away for 20 seconds" - Smart breaks
<div>:title="Automatically counts time away from computer as a break" - Breathing guide
<h2>:title="Visual breathing exercise during breaks to reduce stress"
Step 7: Verify build
Run: cd break-timer && npm run build
Step 8: Commit
git add break-timer/src/lib/components/Settings.svelte
git commit -m "a11y: settings ARIA patterns, contrast, abbreviations, and tooltips"
Task 10: StatsView — Tabs & Data Tables (StatsView.svelte)
Files:
- Modify:
break-timer/src/lib/components/StatsView.svelte
Step 1: Change all <h3> to <h2>
Replace all <h3> with <h2> and </h3> with </h2> (~12 instances).
Step 2: Implement tablist pattern
Replace the tab navigation (lines 297–310):
<div role="tablist" aria-label="Statistics time range" class="flex gap-1 px-5 mb-3" use:fadeIn={{ duration: 0.3, y: 6 }}>
{#each [["today", "Today"], ["weekly", "Weekly"], ["monthly", "Monthly"]] as [tab, label]}
<button
role="tab"
aria-selected={activeTab === tab}
aria-controls="tabpanel-{tab}"
id="tab-{tab}"
use:pressable
class="rounded-lg px-4 py-2 min-h-[44px] text-[11px] tracking-wider uppercase transition-all duration-200
{activeTab === tab
? 'bg-[#1a1a1a] text-white'
: 'text-text-sec hover:text-white'}"
onclick={() => activeTab = tab as any}
>
{label}
</button>
{/each}
</div>
Wrap each tab's content in a role="tabpanel":
{#if activeTab === "today"}
<div role="tabpanel" id="tabpanel-today" aria-labelledby="tab-today">
...
</div>
Same for weekly and monthly.
Step 3: Add sr-only data tables for 30-day chart and heatmap
After the 30-day chart canvas (around line 533), add:
{#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}
After the heatmap canvas (around line 559), add:
{#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}
Step 4: Replace hardcoded colors
- All
text-[#8a8a8a]→text-text-sec bg-[#161616]dividers →bg-border- Back button:
h-8 w-8→h-10 w-10 min-h-[44px] min-w-[44px]
Step 5: Verify build
Run: cd break-timer && npm run build
Step 6: Commit
git add break-timer/src/lib/components/StatsView.svelte
git commit -m "a11y: stats tablist pattern, sr-only data tables, contrast updates"
Task 11: BreakScreen (BreakScreen.svelte)
Files:
- Modify:
break-timer/src/lib/components/BreakScreen.svelte
Step 1: Add strict mode focus safety
When strict_mode is true and buttons are hidden, the focus trap has zero focusable elements. After the {#if showButtons} block (around line 353 for in-app, line 203 for standalone), add an else block:
{:else}
<span tabindex="0" class="sr-only" aria-live="polite">
Break in progress, please wait
</span>
{/if}
This ensures the focus trap always has at least one focusable element.
Step 2: Fix breathing aria-live to only announce phase changes
The breathing guide <span aria-live="polite"> (lines 172 and 304) currently announces every countdown tick. Add a tracked variable that only updates on phase change:
In the script section, add:
let lastAnnouncedPhase = $state("");
let breathAnnouncement = $derived(
breathPhase !== lastAnnouncedPhase
? (() => { lastAnnouncedPhase = breathPhase; return `${breathPhase}`; })()
: ""
);
Actually, better approach — use a separate $effect and a dedicated announcement state:
let breathAnnouncement = $state("");
$effect(() => {
// Only announce when the breathing phase name changes, not countdown ticks
breathAnnouncement = breathPhase;
});
Then change the aria-live span to use a separate invisible span for SR announcements:
<span class="sr-only" aria-live="polite" aria-atomic="true">{breathAnnouncement}</span>
Keep the visible breathing text without aria-live (remove aria-live="polite" from the visible span).
Step 3: Replace hardcoded colors
text-[#8a8a8a]→text-text-secborder-[#222]→border-border
Step 4: Verify build
Run: cd break-timer && npm run build
Step 5: Commit
git add break-timer/src/lib/components/BreakScreen.svelte
git commit -m "a11y: break screen strict-mode focus safety and breathing phase announcements"
Task 12: Celebration Toast Persistence (Celebration.svelte)
Files:
- Modify:
break-timer/src/lib/components/Celebration.svelte
Step 1: Add hover/focus-aware auto-dismiss
Replace the CSS-driven 3.5s fade with JS-controlled timing. Add state:
let milestoneHovering = $state(false);
let goalHovering = $state(false);
For the .celebration-overlay div, add:
onmouseenter={() => milestoneHovering = true}
onmouseleave={() => milestoneHovering = false}
onfocusin={() => milestoneHovering = true}
onfocusout={() => milestoneHovering = false}
Remove pointer-events: none from .celebration-overlay and .goal-overlay styles.
Add $effect blocks that manage visibility based on hover state — when not hovering, set a timeout to add a CSS class that triggers fade-out.
Add a close button (sr-only label "Dismiss notification") to both overlays, and an Escape key handler.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/Celebration.svelte
git commit -m "a11y: celebration overlays persist on hover/focus with dismiss controls"
Task 13: MiniTimer Contrast (MiniTimer.svelte)
Files:
- Modify:
break-timer/src/lib/components/MiniTimer.svelte
Step 1: Fix paused text contrast
Change line 338: color: {state === 'paused' ? '#555' : '#fff'} to color: {state === 'paused' ? '#a8a8a8' : '#fff'}.
Replace #8a8a8a with the text-sec token value #a8a8a8 on line 344 for pomodoro indicator.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/MiniTimer.svelte
git commit -m "a11y: fix mini timer paused text contrast for AAA"
Task 14: MicrobreakOverlay (MicrobreakOverlay.svelte)
Files:
- Modify:
break-timer/src/lib/components/MicrobreakOverlay.svelte
Step 1: Add alertdialog role, heading, and label
Change the outer .microbreak-card div (line 41):
<div class="microbreak-card" role="alertdialog" aria-label="Microbreak" aria-describedby="microbreak-msg">
<h2 class="sr-only">Microbreak</h2>
<div class="flex items-center gap-3 mb-2">
...
<span id="microbreak-msg" class="text-[15px] font-medium text-white">
Look away — 20 feet for {timeRemaining}s
</span>
</div>
Replace text-[#8a8a8a] with text-text-sec.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/MicrobreakOverlay.svelte
git commit -m "a11y: microbreak overlay gets alertdialog role and heading"
Task 15: BreakOverlay (BreakOverlay.svelte)
Files:
- Modify:
break-timer/src/lib/components/BreakOverlay.svelte
Step 1: Add alertdialog role, heading, and label
Change the outer div (line 39):
<div
role="alertdialog"
aria-label="Break in progress"
class="fixed inset-0 flex flex-col items-center justify-center"
style="background: rgba(0, 0, 0, {$config.backdrop_opacity});"
>
<h2 class="sr-only">Break in Progress</h2>
<p class="text-[16px] font-medium text-white mb-4">Break in progress</p>
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/BreakOverlay.svelte
git commit -m "a11y: break overlay gets alertdialog role and heading"
Task 16: ColorPicker (ColorPicker.svelte)
Files:
- Modify:
break-timer/src/lib/components/ColorPicker.svelte
Step 1: Enlarge swatches and add aria-pressed
Change swatch buttons (line 247) from h-[22px] w-[22px] to h-[28px] w-[28px] with a min-h-[44px] min-w-[44px] clickable area (via padding or wrapper).
Add aria-pressed={value === color} to each swatch button.
Change the flex container gap from gap-[6px] to gap-[8px] to accommodate larger swatches.
Replace text-[#8a8a8a] with text-text-sec.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/ColorPicker.svelte
git commit -m "a11y: enlarge color swatches, add aria-pressed"
Task 17: ActivityManager Target Sizes (ActivityManager.svelte)
Files:
- Modify:
break-timer/src/lib/components/ActivityManager.svelte
Step 1: Enlarge star/remove buttons
Find all favorite (★) and remove (✕) buttons. Change from any w-8 h-8 / w-7 h-7 to w-9 h-9 min-h-[44px] min-w-[44px].
Replace text-[#8a8a8a] with text-text-sec.
Step 2: Verify build
Run: cd break-timer && npm run build
Step 3: Commit
git add break-timer/src/lib/components/ActivityManager.svelte
git commit -m "a11y: enlarge activity manager action buttons to 44px targets"
Task 18: Final Build Verification & Cleanup
Step 1: Full build
Run: cd break-timer && npm run build
Expected: Build succeeds with zero errors.
Step 2: Verify all theme token propagation
Search for any remaining hardcoded #8a8a8a in Svelte files — there should be none (all replaced with text-text-sec or inline #a8a8a8).
Run: grep -r "#8a8a8a" break-timer/src/ — should return zero results.
Step 3: Verify no remaining h3 headings in Settings/Stats
Run: grep -n "<h3" break-timer/src/lib/components/Settings.svelte break-timer/src/lib/components/StatsView.svelte — should return zero results.
Step 4: Final commit if any cleanup needed
git add -A break-timer/src/
git commit -m "a11y: final cleanup pass for WCAG 2.2 AAA compliance"
Dependency Graph
Task 1 (app.css tokens)
├── Task 2 (App.svelte)
├── Task 3 (Titlebar)
├── Task 4 (ToggleSwitch)
├── Task 5 (Stepper)
├── Task 6 (animate.ts)
├── Task 7 (Dashboard) ← depends on Task 6
├── Task 8 (Settings part 1)
├── Task 9 (Settings part 2) ← depends on Task 8
├── Task 10 (StatsView)
├── Task 11 (BreakScreen)
├── Task 12 (Celebration)
├── Task 13 (MiniTimer)
├── Task 14 (MicrobreakOverlay)
├── Task 15 (BreakOverlay)
├── Task 16 (ColorPicker)
└── Task 17 (ActivityManager)
Task 18 (Final verification) ← depends on all above
Tasks 2–17 are mostly independent of each other (except 7 depends on 6, and 9 depends on 8). They all depend on Task 1 being done first.