diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
index 3c7a6a7d5..165cb69d1 100644
--- a/src/app/dashboard/page.tsx
+++ b/src/app/dashboard/page.tsx
@@ -1,4 +1,4 @@
-import TodayFocusHero from "@/components/TodayFocusHero";
+import TodayFocusHero from "@/components/TodayFocusHero";
import DashboardHeader from "@/components/DashboardHeader";
import ExportButton from "@/components/ExportButton";
import Link from "next/link";
diff --git a/src/components/ConfirmModal.tsx b/src/components/ConfirmModal.tsx
index c04a28758..54baff4a4 100644
--- a/src/components/ConfirmModal.tsx
+++ b/src/components/ConfirmModal.tsx
@@ -1,6 +1,7 @@
"use client";
import { useEffect, useRef } from "react";
+import { Button } from "@/components/ui/button";
interface ConfirmModalProps {
isOpen: boolean;
@@ -70,20 +71,20 @@ export default function ConfirmModal({
-
-
diff --git a/src/components/DashboardHeader.tsx b/src/components/DashboardHeader.tsx
index ac6ec6e5e..40d7e0f76 100644
--- a/src/components/DashboardHeader.tsx
+++ b/src/components/DashboardHeader.tsx
@@ -18,6 +18,7 @@ import UserAvatar from "@/components/UserAvatar";
import KeyboardShortcuts from "@/components/KeyboardShortcuts";
import { Moon, Sun } from "lucide-react";
import { toast } from "sonner";
+import { Button, buttonVariants } from "@/components/ui/button";
type DashboardSyncContextValue = {
lastSynced: Date | null;
@@ -268,7 +269,7 @@ export default function DashboardHeader() {
href={`/u/${session.githubLogin}`}
target="_blank"
rel="noopener noreferrer"
- className="primary-button inline-flex items-center justify-center rounded-xl px-4 py-2 text-sm font-semibold"
+ className={buttonVariants({ variant: "default" })}
title="View your public profile"
>
Share Profile
@@ -300,9 +301,10 @@ export default function DashboardHeader() {
{/* Mobile hamburger button */}
- setMenuOpen((v) => !v)}
aria-label="Toggle menu"
aria-expanded={menuOpen}
@@ -339,7 +341,7 @@ export default function DashboardHeader() {
)}
-
+
{/* Mobile dropdown */}
@@ -372,7 +374,7 @@ export default function DashboardHeader() {
href={`/u/${session.githubLogin}`}
target="_blank"
rel="noopener noreferrer"
- className="primary-button inline-flex w-full items-center justify-center rounded-xl px-4 py-2 text-sm font-semibold"
+ className={buttonVariants({ variant: "default", className: "w-full" })}
title="View your public profile"
onClick={() => setMenuOpen(false)}
>
diff --git a/src/components/GoalTracker.tsx b/src/components/GoalTracker.tsx
index 166266fc0..338e250ca 100644
--- a/src/components/GoalTracker.tsx
+++ b/src/components/GoalTracker.tsx
@@ -5,6 +5,7 @@ import { useSession } from "next-auth/react";
import { submitGoalWithRefresh } from "@/lib/goal-tracker";
import ConfirmModal from "@/components/ConfirmModal"; // 🎯 Imported the native project confirmation layout
import { buildPublicGoalShareUrl } from "@/lib/goals/share";
+import { Button } from "@/components/ui/button";
type Recurrence = "none" | "weekly" | "monthly";
@@ -411,12 +412,13 @@ export default function GoalTracker() {
{/* ── Header ── */}
Goals
-
{syncing ? "Syncing…" : "Refresh"}
-
+
{/* Sync Error */}
@@ -552,7 +554,9 @@ export default function GoalTracker() {
{/* Manual +1 only for non-auto-synced goals */}
{!isAutoSynced && (
- {
const newCurrent = goal.current + 1;
if (newCurrent > goal.target) return;
@@ -571,25 +575,25 @@ export default function GoalTracker() {
}}
disabled={goal.current >= goal.target}
aria-label={`Increment "${goal.title}" progress by 1`}
- className="rounded bg-blue-600 px-2 py-1 text-xs text-white hover:bg-blue-700 disabled:opacity-50"
>
+1
-
+
)}
{/* 🎯 Clean interception: Clicking trash icon sets confirmingId instead of trigger-deleting */}
- setConfirmingId(goal.id)}
disabled={isDeleting}
- className="text-[var(--muted-foreground)] hover:text-[var(--destructive)] transition-colors disabled:opacity-50"
aria-label={`Delete goal: ${goal.title}`}
title="Delete goal"
>
-
+
@@ -630,13 +634,14 @@ export default function GoalTracker() {
{goal.is_public && (
- copyGoalShareLink(goal.id)}
- className="secondary-button mt-3 rounded-lg px-3 py-1.5 text-sm"
>
{copiedGoalId === goal.id ? "Copied!" : "Copy share link"}
-
+
)}
@@ -768,10 +773,9 @@ export default function GoalTracker() {
)}
-
{creating ? (
<>
@@ -781,7 +785,7 @@ export default function GoalTracker() {
) : (
"Create goal"
)}
-
+
{createError && (
{createError}
)}
diff --git a/src/components/SignOutButton.tsx b/src/components/SignOutButton.tsx
index c1e168034..61da47031 100644
--- a/src/components/SignOutButton.tsx
+++ b/src/components/SignOutButton.tsx
@@ -2,6 +2,7 @@
import { useState } from "react"
import { signOut } from "next-auth/react"
+import { Button } from "@/components/ui/button"
export default function SignOutButton() {
const [signingOut, setSigningOut] = useState(false)
@@ -21,37 +22,34 @@ export default function SignOutButton() {
if (confirming) {
return (
-
{signingOut ? "Signing out..." : "Confirm"}
-
+
- setConfirming(false)}
disabled={signingOut}
- className="inline-flex h-10 items-center rounded-full border px-4 text-sm font-semibold hover:bg-gray-100"
aria-label="Cancel sign out"
>
Cancel
-
+
)
}
return (
- setConfirming(true)}
aria-label="Sign out"
- className="inline-flex h-10 items-center gap-2 rounded-full border border-[var(--destructive)]/50 bg-[var(--destructive)]/80 px-4 text-sm font-semibold text-[var(--foreground)] transition-colors hover:bg-[var(--destructive)] disabled:cursor-not-allowed disabled:opacity-70"
>
{signingOut && (
)
}
diff --git a/src/components/repo-analytics/RepoCard.tsx b/src/components/repo-analytics/RepoCard.tsx
index c185c1f7b..beef2a7be 100644
--- a/src/components/repo-analytics/RepoCard.tsx
+++ b/src/components/repo-analytics/RepoCard.tsx
@@ -13,6 +13,7 @@ import {
formatRelativeDate,
formatDate,
} from "@/lib/repoAnalyticsUtils";
+import { Button, buttonVariants } from "@/components/ui/button";
interface RepoCardProps {
repo: ExplorerRepoCardData;
@@ -140,18 +141,17 @@ export default function RepoCard({
href={repo.htmlUrl}
target="_blank"
rel="noopener noreferrer"
- className="flex items-center justify-center rounded-2xl border border-[var(--border)] bg-[var(--card)] px-4 py-3 text-sm font-medium text-[var(--card-foreground)] transition hover:bg-[color:color-mix(in_srgb,var(--card)_80%,var(--accent)_20%)]"
+ className={buttonVariants({ variant: "outline" })}
>
Repo
- onViewAnalytics(repo)}
- className="flex items-center justify-center rounded-2xl border border-[var(--border)] bg-[var(--card)] px-4 py-3 text-sm font-medium text-[var(--card-foreground)] transition hover:bg-[color:color-mix(in_srgb,var(--card)_80%,var(--accent)_20%)]"
>
View
-
+
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index eb9cfe5c5..37514b501 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -3,26 +3,26 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--ring)] disabled:pointer-events-none disabled:opacity-50",
+ "inline-flex items-center justify-center whitespace-nowrap rounded-xl text-sm font-semibold transition duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--background)] focus-visible:ring-[var(--accent)] disabled:pointer-events-none disabled:opacity-50 gap-2 active:scale-[0.98]",
{
variants: {
variant: {
default:
"bg-[var(--accent)] text-[var(--accent-foreground)] shadow hover:opacity-90",
destructive:
- "bg-[var(--destructive)] text-white shadow-sm hover:opacity-90",
+ "border border-[var(--destructive)]/50 bg-[var(--destructive)]/80 text-white shadow hover:bg-[var(--destructive)]",
outline:
- "border border-[var(--border)] bg-[var(--background)] shadow-sm hover:bg-[var(--card-muted)] hover:text-[var(--foreground)]",
+ "border border-[var(--border)] bg-[var(--background)] shadow-sm hover:border-[var(--accent)] hover:text-[var(--accent)] hover:bg-[var(--card-muted)]/50",
secondary:
- "bg-[var(--card-muted)] text-[var(--foreground)] shadow-sm hover:opacity-80",
- ghost: "hover:bg-[var(--card-muted)] hover:text-[var(--foreground)]",
+ "bg-[var(--card-muted)] text-[var(--foreground)] border border-[var(--border)] shadow-sm hover:opacity-80",
+ ghost: "hover:text-[var(--destructive)] transition-colors",
link: "text-[var(--accent)] underline-offset-4 hover:underline",
},
size: {
- default: "h-9 px-4 py-2",
- sm: "h-8 rounded-md px-3 text-xs",
- lg: "h-10 rounded-md px-8",
- icon: "h-9 w-9",
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-lg px-3",
+ lg: "h-11 rounded-xl px-8",
+ icon: "h-10 w-10",
},
},
defaultVariants: {
diff --git a/tests/snapshots/visual-regression.spec.js/landing-page-dark.png b/tests/snapshots/visual-regression.spec.js/landing-page-dark.png
index 173ae4110..1a934acea 100644
Binary files a/tests/snapshots/visual-regression.spec.js/landing-page-dark.png and b/tests/snapshots/visual-regression.spec.js/landing-page-dark.png differ