diff --git a/src/app/globals.css b/src/app/globals.css index 4c778e61..7ce19af5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -155,6 +155,59 @@ body { animation: fadeUp 0.7s ease-out; } +/* ───────────────────────────────────────── + MICRO-ANIMATION UTILITIES + ───────────────────────────────────────── */ + +/* Staggered animation delays for child elements */ +.stagger-children > *:nth-child(1) { animation-delay: 0ms; } +.stagger-children > *:nth-child(2) { animation-delay: 50ms; } +.stagger-children > *:nth-child(3) { animation-delay: 100ms; } +.stagger-children > *:nth-child(4) { animation-delay: 150ms; } +.stagger-children > *:nth-child(5) { animation-delay: 200ms; } +.stagger-children > *:nth-child(6) { animation-delay: 250ms; } +.stagger-children > *:nth-child(7) { animation-delay: 300ms; } +.stagger-children > *:nth-child(8) { animation-delay: 350ms; } +.stagger-children > *:nth-child(9) { animation-delay: 400ms; } +.stagger-children > *:nth-child(10) { animation-delay: 450ms; } + +/* Shimmer skeleton loading effect */ +.skeleton-shimmer { + background: linear-gradient( + 90deg, + var(--card-muted) 0%, + color-mix(in srgb, var(--card-muted) 60%, var(--border) 40%) 50%, + var(--card-muted) 100% + ); + background-size: 200% 100%; + animation: shimmer 1.8s ease-in-out infinite; +} + +/* Interactive stat cell — hover lift + glow */ +.stat-cell { + transition: transform 200ms ease, box-shadow 200ms ease, background-color 200ms ease, border-color 200ms ease; +} +.stat-cell:hover { + transform: translateY(-2px); + box-shadow: 0 4px 16px -4px rgba(96, 165, 250, 0.15); +} + +/* Interactive list item — subtle left-border reveal + slide */ +.list-item-hover { + transition: transform 200ms ease, background-color 200ms ease, box-shadow 200ms ease, border-color 200ms ease; + border-left: 3px solid transparent; +} +.list-item-hover:hover { + transform: translateX(4px); + border-left-color: var(--accent); + box-shadow: 0 2px 8px -2px rgba(96, 165, 250, 0.1); +} + +/* Progress bar animated fill */ +.progress-fill { + transition: width 600ms cubic-bezier(0.4, 0, 0.2, 1); +} + /* ───────────────────────────────────────── DARK MODE GLOBAL TYPOGRAPHY ───────────────────────────────────────── */ diff --git a/src/components/AIMentorWidget.tsx b/src/components/AIMentorWidget.tsx index 28fe8b3c..9e360a73 100644 --- a/src/components/AIMentorWidget.tsx +++ b/src/components/AIMentorWidget.tsx @@ -39,7 +39,7 @@ function SkeletonCard() { role="status" aria-busy="true" aria-live="polite" - className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm animate-pulse" + className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm animate-pulse transition-all duration-300 hover:shadow-md hover:-translate-y-1" > Loading AI Mentor insights
@@ -107,7 +107,7 @@ export function AIMentorWidget() { if (error) { return ( -
+

{error}

); @@ -124,7 +124,7 @@ export function AIMentorWidget() { : ""; return ( -
+

diff --git a/src/components/ActivityRingChart.tsx b/src/components/ActivityRingChart.tsx index 2b5a125c..e7135562 100644 --- a/src/components/ActivityRingChart.tsx +++ b/src/components/ActivityRingChart.tsx @@ -81,7 +81,7 @@ export default function ActivityRingChart() { } return ( -
+

Activity Ring diff --git a/src/components/BadgeSection.tsx b/src/components/BadgeSection.tsx index 7aaa1f9a..b267e95e 100644 --- a/src/components/BadgeSection.tsx +++ b/src/components/BadgeSection.tsx @@ -35,7 +35,7 @@ export default function BadgeSection({ username }: BadgeSectionProps) { const combinedMarkdown = `${streakMarkdown} ${commitsMarkdown}`; return ( -
+

📌 Get Your Badge

@@ -105,13 +105,13 @@ function CopyableCodeBlock({ code }: { code: string }) { }; return ( -
+
{code} diff --git a/src/components/CIAnalytics.tsx b/src/components/CIAnalytics.tsx index 6b908401..80894dcf 100644 --- a/src/components/CIAnalytics.tsx +++ b/src/components/CIAnalytics.tsx @@ -104,7 +104,7 @@ export default function CIAnalytics() { : "Refresh"; return ( -
+

@@ -119,7 +119,7 @@ export default function CIAnalytics() { onClick={fetchCIAnalytics} disabled={isRateLimited || loading} title={isRateLimited ? "GitHub API rate limit reached" : "Refresh CI data"} - className="inline-flex items-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-50" + className="inline-flex items-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-all hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-50 hover:opacity-90 active:scale-95" > {loading ? ( @@ -140,7 +140,7 @@ export default function CIAnalytics() { @@ -165,11 +165,11 @@ export default function CIAnalytics() {

) : data ? (
-
+
{stats.map((stat) => (
{stat.value} @@ -181,7 +181,7 @@ export default function CIAnalytics() { ))}
-
+

Flakiest workflow

diff --git a/src/components/CodingActivityInsightsCard.tsx b/src/components/CodingActivityInsightsCard.tsx index f3d6375b..176384b7 100644 --- a/src/components/CodingActivityInsightsCard.tsx +++ b/src/components/CodingActivityInsightsCard.tsx @@ -229,7 +229,7 @@ export default function CodingActivityInsightsCard() { : `${dataWindowLabel} · Commits by hour · Local timezone`; return ( -
+

@@ -252,7 +252,7 @@ export default function CodingActivityInsightsCard() { type="button" onClick={fetchInsights} disabled={loading} - className="flex items-center gap-2 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-50" + className="flex items-center gap-2 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-all hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-50 hover:opacity-90 active:scale-95" > {loading ? ( <> diff --git a/src/components/CodingTimeWidget.tsx b/src/components/CodingTimeWidget.tsx index 1af55156..d7496804 100644 --- a/src/components/CodingTimeWidget.tsx +++ b/src/components/CodingTimeWidget.tsx @@ -50,7 +50,7 @@ export default function CodingTimeWidget() { if (loading) { return ( -
+
@@ -62,7 +62,7 @@ export default function CodingTimeWidget() { } return ( -
+

Wakatime Coding Activity (7 Days)

diff --git a/src/components/CommitSearchPanel.tsx b/src/components/CommitSearchPanel.tsx index 81fcbd29..d00ec425 100644 --- a/src/components/CommitSearchPanel.tsx +++ b/src/components/CommitSearchPanel.tsx @@ -151,7 +151,7 @@ export default function CommitSearchPanel({ commits, loading }: CommitSearchPane onClick={handleToggle} aria-label={isOpen ? "Collapse commit list" : "Expand commit list"} aria-expanded={isOpen} - className="flex items-center gap-1 rounded-lg border border-[var(--border)] bg-[var(--control)] px-3 py-2 text-sm font-medium text-[var(--foreground)] hover:bg-[var(--control-hover)] transition-colors" + className="flex items-center gap-1 rounded-lg border border-[var(--border)] bg-[var(--control)] px-3 py-2 text-sm font-medium text-[var(--foreground)] hover:bg-[var(--control-hover)] transition-all hover:opacity-90 active:scale-95" > diff --git a/src/components/CommitTimeChart.tsx b/src/components/CommitTimeChart.tsx index 91ab081e..c3a981e3 100644 --- a/src/components/CommitTimeChart.tsx +++ b/src/components/CommitTimeChart.tsx @@ -97,7 +97,7 @@ export default function CommitTimeChart() { }, [fetchContributions]); return ( -
+

Commits by Time of Day @@ -131,7 +131,7 @@ export default function CommitTimeChart() { diff --git a/src/components/CommunityMetrics.tsx b/src/components/CommunityMetrics.tsx index 6ac68834..32f0e02f 100644 --- a/src/components/CommunityMetrics.tsx +++ b/src/components/CommunityMetrics.tsx @@ -62,7 +62,7 @@ export default function CommunityMetrics() { metrics.commentsPosted === 0; return ( -
+

@@ -76,7 +76,7 @@ export default function CommunityMetrics() { type="button" onClick={fetchMetrics} disabled={loading} - className="inline-flex w-full items-center justify-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:bg-[var(--control)] sm:w-auto disabled:cursor-not-allowed disabled:opacity-60" + className="inline-flex w-full items-center justify-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-all hover:bg-[var(--control)] sm:w-auto disabled:cursor-not-allowed disabled:opacity-60 hover:opacity-90 active:scale-95" > {loading ? ( @@ -98,7 +98,7 @@ export default function CommunityMetrics() { @@ -120,7 +120,7 @@ export default function CommunityMetrics() { {stats.map((stat) => (
{stat.value} @@ -133,7 +133,7 @@ export default function CommunityMetrics() {
{isEmpty && ( -
+
No discussion activity yet in this 30-day window.
)} diff --git a/src/components/ContributionGraph.tsx b/src/components/ContributionGraph.tsx index 5d60575e..abce9bb5 100644 --- a/src/components/ContributionGraph.tsx +++ b/src/components/ContributionGraph.tsx @@ -376,7 +376,7 @@ export default function ContributionGraph() { return (
@@ -490,7 +490,7 @@ export default function ContributionGraph() { )} diff --git a/src/components/ContributionHeatmap.tsx b/src/components/ContributionHeatmap.tsx index d92231c7..ad57710d 100644 --- a/src/components/ContributionHeatmap.tsx +++ b/src/components/ContributionHeatmap.tsx @@ -409,7 +409,7 @@ export default function ContributionHeatmap({ )} diff --git a/src/components/CopyLinkButton.tsx b/src/components/CopyLinkButton.tsx index 1774aede..b233894d 100644 --- a/src/components/CopyLinkButton.tsx +++ b/src/components/CopyLinkButton.tsx @@ -30,7 +30,7 @@ export default function CopyLinkButton({ url }: CopyLinkButtonProps) { type="button" onClick={handleCopy} aria-label="Copy profile link" - className="inline-flex items-center gap-1.5 px-3 py-1.5 bg-[var(--control)] border border-[var(--border)] text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:border-[var(--accent)] rounded-lg text-sm font-medium transition-colors shadow-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]/50 active:scale-[0.98]" + className="inline-flex items-center gap-1.5 px-3 py-1.5 bg-[var(--control)] border border-[var(--border)] text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:border-[var(--accent)] rounded-lg text-sm font-medium transition-all shadow-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]/50 active:scale-[0.98] hover:opacity-90 active:scale-95" > {copied ? ( <> diff --git a/src/components/DiscussionsWidget.tsx b/src/components/DiscussionsWidget.tsx index b017632b..daf2905e 100644 --- a/src/components/DiscussionsWidget.tsx +++ b/src/components/DiscussionsWidget.tsx @@ -53,7 +53,7 @@ export default function DiscussionsWidget() { : []; return ( -
+

Discussion Activity

@@ -63,7 +63,7 @@ export default function DiscussionsWidget() { {[1, 2, 3].map((i) => (
))}
@@ -79,11 +79,11 @@ export default function DiscussionsWidget() {
) : ( -
+
{stats.map((stat) => (
diff --git a/src/components/ExportButton.tsx b/src/components/ExportButton.tsx index 631c2212..123743e6 100644 --- a/src/components/ExportButton.tsx +++ b/src/components/ExportButton.tsx @@ -661,7 +661,7 @@ export default function ExportButton() { type="button" onClick={exportCSV} disabled={isExportingCSV} - className="flex w-full items-center justify-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-2 text-sm font-medium text-[var(--card-foreground)] transition-colors hover:border-[var(--accent)] disabled:opacity-50 sm:w-auto sm:min-w-[140px]" + className="flex w-full items-center justify-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-2 text-sm font-medium text-[var(--card-foreground)] transition-all hover:border-[var(--accent)] disabled:opacity-50 sm:w-auto sm:min-w-[140px] hover:opacity-90 active:scale-95" > @@ -673,7 +673,7 @@ export default function ExportButton() { type="button" onClick={exportPDF} disabled={isExportingPDF} - className="flex min-w-0 flex-1 items-center justify-center gap-2 rounded-lg bg-[var(--accent)] px-4 py-2 text-sm font-medium text-[var(--accent-foreground)] transition-colors hover:opacity-90 disabled:opacity-50 sm:min-w-[140px] sm:flex-none" + className="flex min-w-0 flex-1 items-center justify-center gap-2 rounded-lg bg-[var(--accent)] px-4 py-2 text-sm font-medium text-[var(--accent-foreground)] transition-all hover:opacity-90 disabled:opacity-50 sm:min-w-[140px] sm:flex-none active:scale-95" suppressHydrationWarning > diff --git a/src/components/FriendComparison.tsx b/src/components/FriendComparison.tsx index a8da58b7..dc6c8f1f 100644 --- a/src/components/FriendComparison.tsx +++ b/src/components/FriendComparison.tsx @@ -290,7 +290,7 @@ export default function FriendComparison() { @@ -386,7 +386,7 @@ export default function FriendComparison() { diff --git a/src/components/GitHubAchievements.tsx b/src/components/GitHubAchievements.tsx index 05f64d24..3e4621cc 100644 --- a/src/components/GitHubAchievements.tsx +++ b/src/components/GitHubAchievements.tsx @@ -13,7 +13,7 @@ export default function GitHubAchievements({ error = null, }: GitHubAchievementsProps) { return ( -
+

GitHub Achievements

@@ -30,7 +30,7 @@ export default function GitHubAchievements({ diff --git a/src/components/InactiveRepositoriesCard.tsx b/src/components/InactiveRepositoriesCard.tsx index 5f97b0c7..2cd54a5b 100644 --- a/src/components/InactiveRepositoriesCard.tsx +++ b/src/components/InactiveRepositoriesCard.tsx @@ -71,7 +71,7 @@ export default function InactiveRepositoriesCard() { }, [fetchInactiveRepos]); return ( -
+

@@ -98,7 +98,7 @@ export default function InactiveRepositoriesCard() { type="button" onClick={fetchInactiveRepos} disabled={loading} - className="inline-flex items-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-colors hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-60" + className="inline-flex items-center gap-1.5 rounded-md border border-[var(--border)] px-3 py-1.5 text-xs font-medium text-[var(--muted-foreground)] transition-all hover:bg-[var(--control)] disabled:cursor-not-allowed disabled:opacity-60 hover:opacity-90 active:scale-95" > {loading ? ( @@ -116,7 +116,7 @@ export default function InactiveRepositoriesCard() { @@ -136,14 +136,14 @@ export default function InactiveRepositoriesCard() { Nice! No inactive repositories for this period.

) : ( -