From b59d851103d2709331ca0daae92cdf3fbb05b70b Mon Sep 17 00:00:00 2001 From: mrunalC27 Date: Mon, 18 May 2026 19:18:41 +0530 Subject: [PATCH 1/3] feat: add aria-live region for sync status in DashboardHeader --- src/components/DashboardHeader.tsx | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/components/DashboardHeader.tsx b/src/components/DashboardHeader.tsx index effb33e..7a33572 100644 --- a/src/components/DashboardHeader.tsx +++ b/src/components/DashboardHeader.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useSession } from "next-auth/react"; import AccountToggle from "@/components/AccountToggle"; import SignOutButton from "@/components/SignOutButton"; @@ -10,7 +10,12 @@ import KeyboardShortcuts from "@/components/KeyboardShortcuts"; export default function DashboardHeader() { const { data: session } = useSession(); + const [isPublic, setIsPublic] = useState(null); + const [syncing, setSyncing] = useState(false); + const [announcement, setAnnouncement] = useState(""); + const timeoutRef = useRef | null>(null); + useEffect(() => { if (!session) { @@ -36,8 +41,43 @@ export default function DashboardHeader() { loadSettings(); }, [session]); + // Cleanup timeout on unmount + useEffect(() => { + return () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }; + }, []); + + async function handleSync() { + if (syncing) return; + setSyncing(true); + setAnnouncement(""); + + try { + const res = await fetch("/api/metrics/contributions?days=30"); + if (!res.ok) throw new Error("Sync failed"); + setAnnouncement("Metrics refreshed successfully"); + } catch { + setAnnouncement("Sync failed. Please try again."); + } finally { + setSyncing(false); + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => setAnnouncement(""), 3000); + } + } + return (
+ {/* Visually hidden aria-live region for screen readers */} + + {announcement} + +

@@ -49,6 +89,7 @@ export default function DashboardHeader() {

); -} +} \ No newline at end of file From 4c2566279e1b56e8f40e90dec74f6fe0545020ab Mon Sep 17 00:00:00 2001 From: smirk-dev Date: Tue, 19 May 2026 19:11:01 +0530 Subject: [PATCH 2/3] a11y: add ARIA loading feedback to skeleton loaders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skeleton loaders on the dashboard were silent for screen-reader users — no announcement during fetch and no signal when content arrived. Each widget's skeleton block now exposes a polite live region so assistive tech can describe loading state without visual or behavioral changes. For every widget skeleton: - Wrapper element gets role="status", aria-live="polite", aria-busy="true". - A visually hidden Loading [widget] provides the accessible name (reuses Tailwind's built-in sr-only utility, matching existing usage in src/app/dashboard/settings/page.tsx). - Each decorative pulse
gets aria-hidden="true" so only the status message is announced, not the placeholders themselves. Completion is communicated implicitly: when loading flips false the status region unmounts and the real content (with its existing heading) renders. No explicit "loaded" announcement is added, which avoids a cascade of overlapping ready messages as 13 widgets finish at different times. Closes #324. --- src/components/CIAnalytics.tsx | 9 ++++++++- src/components/CommitTimeChart.tsx | 9 ++++++++- src/components/ContributionGraph.tsx | 12 +++++++++++- src/components/GoalTracker.tsx | 20 +++++++++++++------- src/components/IssueMetrics.tsx | 9 ++++++++- src/components/LanguageBreakdown.tsx | 15 ++++++++++++--- src/components/PRBreakdownChart.tsx | 13 +++++++++++-- src/components/PRMetrics.tsx | 9 ++++++++- src/components/PersonalRecords.tsx | 9 ++++++++- src/components/PinnedRepos.tsx | 14 ++++++++++++-- src/components/StreakTracker.tsx | 16 +++++++++++----- src/components/TopRepos.tsx | 14 ++++++++++++-- src/components/WeeklySummaryCard.tsx | 9 ++++++++- 13 files changed, 130 insertions(+), 28 deletions(-) diff --git a/src/components/CIAnalytics.tsx b/src/components/CIAnalytics.tsx index d900942..c75de51 100644 --- a/src/components/CIAnalytics.tsx +++ b/src/components/CIAnalytics.tsx @@ -74,10 +74,17 @@ export default function CIAnalytics() {
{loading ? ( -
+
+ Loading CI analytics {[1, 2, 3, 4].map((item) => (