Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -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
───────────────────────────────────────── */
Expand Down
6 changes: 3 additions & 3 deletions src/components/AIMentorWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<span className="sr-only">Loading AI Mentor insights</span>
<div className="flex items-center justify-between mb-4">
Expand Down Expand Up @@ -107,7 +107,7 @@ export function AIMentorWidget() {

if (error) {
return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<p className="text-sm text-[var(--muted-foreground)]">{error}</p>
</div>
);
Expand All @@ -124,7 +124,7 @@ export function AIMentorWidget() {
: "";

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-[var(--card-foreground)] flex items-center gap-2">
Expand Down
2 changes: 1 addition & 1 deletion src/components/ActivityRingChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default function ActivityRingChart() {
}

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm flex flex-col h-full">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm flex flex-col h-full transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="flex items-center justify-between mb-2">
<h2 className="text-lg font-semibold text-[var(--card-foreground)]">
Activity Ring
Expand Down
6 changes: 3 additions & 3 deletions src/components/BadgeSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function BadgeSection({ username }: BadgeSectionProps) {
const combinedMarkdown = `${streakMarkdown} ${commitsMarkdown}`;

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<h2 className="mb-4 text-lg font-semibold text-[var(--card-foreground)]">
📌 Get Your Badge
</h2>
Expand Down Expand Up @@ -105,13 +105,13 @@ function CopyableCodeBlock({ code }: { code: string }) {
};

return (
<div className="flex items-center justify-between rounded-lg bg-[var(--control)] p-3 border border-[var(--border)]">
<div className="flex items-center justify-between rounded-lg bg-[var(--control)] p-3 border border-[var(--border)] transition-colors duration-200 hover:bg-[var(--card-muted)]">
<code className="flex-1 text-xs text-[var(--card-foreground)] overflow-auto scrollbar-thin">
{code}
</code>
<button
onClick={handleCopy}
className="ml-2 shrink-0 px-2 py-1 text-xs font-medium rounded bg-[var(--accent)] text-[var(--accent-foreground)] hover:opacity-90 transition-opacity"
className="ml-2 shrink-0 px-2 py-1 text-xs font-medium rounded bg-[var(--accent)] text-[var(--accent-foreground)] hover:opacity-90 transition-opacity active:scale-95"
>
{copied ? "✓ Copied!" : "Copy"}
</button>
Expand Down
12 changes: 6 additions & 6 deletions src/components/CIAnalytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function CIAnalytics() {
: "Refresh";

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="mb-4 flex items-start justify-between gap-3">
<div>
<h2 className="text-lg font-semibold text-[var(--card-foreground)]">
Expand All @@ -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 ? (
<svg className="animate-spin h-3 w-3 text-[var(--muted-foreground)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
Expand All @@ -140,7 +140,7 @@ export default function CIAnalytics() {
<div
key={item}
aria-hidden="true"
className="h-20 rounded-lg bg-[var(--card-muted)] animate-pulse"
className="h-20 rounded-lg skeleton-shimmer"
/>
))}
</div>
Expand All @@ -165,11 +165,11 @@ export default function CIAnalytics() {
</div>
) : data ? (
<div className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 stagger-children">
{stats.map((stat) => (
<div
key={stat.label}
className="rounded-lg bg-[var(--control)] p-4 text-center"
className="rounded-lg bg-[var(--control)] p-4 text-center stat-cell animate-fade-in-up"
>
<div className="text-2xl font-bold text-[var(--accent)]">
{stat.value}
Expand All @@ -181,7 +181,7 @@ export default function CIAnalytics() {
))}
</div>

<div className="rounded-lg bg-[var(--control)] p-4">
<div className="rounded-lg bg-[var(--control)] p-4 stat-cell">
<p className="text-sm font-medium text-[var(--card-foreground)]">
Flakiest workflow
</p>
Expand Down
4 changes: 2 additions & 2 deletions src/components/CodingActivityInsightsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export default function CodingActivityInsightsCard() {
: `${dataWindowLabel} · Commits by hour · Local timezone`;

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="mb-4 flex items-start justify-between gap-3">
<div>
<h2 className="text-lg font-semibold text-[var(--card-foreground)]">
Expand All @@ -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 ? (
<>
Expand Down
4 changes: 2 additions & 2 deletions src/components/CodingTimeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default function CodingTimeWidget() {

if (loading) {
return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="h-5 w-40 bg-[var(--card-muted)] rounded animate-pulse mb-4" />
<div className="h-48 bg-[var(--card-muted)] rounded animate-pulse w-full" />
</div>
Expand All @@ -62,7 +62,7 @@ export default function CodingTimeWidget() {
}

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<h2 className="text-lg font-semibold text-[var(--card-foreground)] mb-4">
Wakatime Coding Activity (7 Days)
</h2>
Expand Down
4 changes: 2 additions & 2 deletions src/components/CommitSearchPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<svg
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
Expand Down Expand Up @@ -243,7 +243,7 @@ export default function CommitSearchPanel({ commits, loading }: CommitSearchPane
<div className="mt-2 flex justify-center">
<button
onClick={handleLoadMore}
className="rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-1.5 text-sm font-medium text-[var(--foreground)] hover:bg-[var(--control-hover)] transition-colors"
className="rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-1.5 text-sm font-medium text-[var(--foreground)] hover:bg-[var(--control-hover)] transition-all hover:opacity-90 active:scale-95"
>
Load more ({filteredCommits.length - visibleCount} remaining)
</button>
Expand Down
4 changes: 2 additions & 2 deletions src/components/CommitTimeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default function CommitTimeChart() {
}, [fetchContributions]);

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm flex flex-col h-full">
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm flex flex-col h-full transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="flex items-center justify-between mb-2">
<h2 className="text-lg font-semibold text-[var(--card-foreground)]">
Commits by Time of Day
Expand Down Expand Up @@ -131,7 +131,7 @@ export default function CommitTimeChart() {
<div
key={i}
aria-hidden="true"
className="h-10 rounded bg-[var(--card-muted)] animate-pulse"
className="h-10 rounded skeleton-shimmer"
/>
))}
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/CommunityMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function CommunityMetrics() {
metrics.commentsPosted === 0;

return (
<div className="h-full rounded-xl border border-[var(--border)] bg-[var(--card)] p-4 sm:p-6 shadow-sm">
<div className="h-full rounded-xl border border-[var(--border)] bg-[var(--card)] p-4 sm:p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1">
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="min-w-0">
<h2 className="text-lg font-semibold text-[var(--card-foreground)]">
Expand All @@ -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 ? (
<svg className="animate-spin h-3 w-3 text-[var(--muted-foreground)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
Expand All @@ -98,7 +98,7 @@ export default function CommunityMetrics() {
<div
key={item}
aria-hidden="true"
className="min-h-20 rounded-lg bg-[var(--card-muted)] animate-pulse"
className="min-h-20 rounded-lg skeleton-shimmer"
/>
))}
</div>
Expand All @@ -120,7 +120,7 @@ export default function CommunityMetrics() {
{stats.map((stat) => (
<div
key={stat.label}
className="rounded-lg bg-[var(--control)] p-4 text-center min-w-0 sm:min-h-24"
className="rounded-lg bg-[var(--control)] p-4 text-center min-w-0 sm:min-h-24 stat-cell animate-fade-in-up"
>
<div className="text-[clamp(1.5rem,5vw,1.75rem)] font-bold leading-none text-[var(--accent)]">
{stat.value}
Expand All @@ -133,7 +133,7 @@ export default function CommunityMetrics() {
</div>

{isEmpty && (
<div className="rounded-lg border border-[var(--border)] bg-[var(--control)]/60 p-4 text-sm text-[var(--muted-foreground)]">
<div className="rounded-lg border border-[var(--border)] bg-[var(--control)]/60 p-4 text-sm text-[var(--muted-foreground)] transition-colors duration-200 hover:bg-[var(--card-muted)]">
No discussion activity yet in this 30-day window.
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ContributionGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export default function ContributionGraph() {
return (
<div
id="contribution-activity"
className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm"
className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm transition-all duration-300 hover:shadow-md hover:-translate-y-1"
>
<div className="flex flex-wrap items-center justify-between mb-4 gap-2">
<div className="min-w-0">
Expand Down Expand Up @@ -490,7 +490,7 @@ export default function ContributionGraph() {
)}
<button
onClick={handleCustomApply}
className="mt-1 w-full rounded-md bg-[var(--accent)] py-1.5 text-sm font-medium text-[var(--background)] hover:opacity-90 transition-opacity"
className="mt-1 w-full rounded-md bg-[var(--accent)] py-1.5 text-sm font-medium text-[var(--background)] hover:opacity-90 transition-opacity active:scale-95"
>
Apply
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ContributionHeatmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ export default function ContributionHeatmap({
)}
<button
onClick={handleCustomApply}
className="mt-2 w-full rounded-md bg-[var(--accent)] px-3 py-1 text-xs font-medium text-[var(--background)] transition-opacity hover:opacity-90"
className="mt-2 w-full rounded-md bg-[var(--accent)] px-3 py-1 text-xs font-medium text-[var(--background)] transition-opacity hover:opacity-90 active:scale-95"
>
Apply
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/CopyLinkButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? (
<>
Expand Down
Loading
Loading