From 95f2903067f675f42031402d9ad83e5dcb478f5d Mon Sep 17 00:00:00 2001 From: asepharyana Date: Tue, 18 Aug 2026 09:50:52 +0700 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20micro-interactions=20?= =?UTF-8?q?=E2=80=94=20page-enter=20transition=20+=20MetricTile=20lift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add animate-fade-up utility (reduced-motion aware) and a PageTransition wrapper; every dashboard view now rises + settles on mount/route change. - MetricTile gains a subtle hover lift (-translate-y) + ring-focus glow. - Theme toggle and command palette (⌘K) already existed and persist; no-op. --- .../src/app/(dashboard)/analysis/page.tsx | 7 ++++++- .../src/app/(dashboard)/dashboard/page.tsx | 7 ++++++- .../frontend/src/app/(dashboard)/media/page.tsx | 7 ++++++- .../src/app/(dashboard)/messages/page.tsx | 11 +++++++---- .../src/app/(dashboard)/moderation/page.tsx | 7 ++++++- .../src/app/(dashboard)/recordings/page.tsx | 7 ++++++- .../frontend/src/app/(dashboard)/voice/page.tsx | 7 ++++++- services/frontend/src/app/globals.css | 8 +++++++- services/frontend/src/components/shared/index.ts | 1 + .../src/components/shared/page-transition.tsx | 15 +++++++++++++++ .../frontend/src/components/shared/section.tsx | 7 ++++++- 11 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 services/frontend/src/components/shared/page-transition.tsx diff --git a/services/frontend/src/app/(dashboard)/analysis/page.tsx b/services/frontend/src/app/(dashboard)/analysis/page.tsx index dd76297..00eaeed 100644 --- a/services/frontend/src/app/(dashboard)/analysis/page.tsx +++ b/services/frontend/src/app/(dashboard)/analysis/page.tsx @@ -1,7 +1,12 @@ +import { PageTransition } from "@/components/shared"; import { AnalysisView } from "./view"; export const dynamic = "force-dynamic"; export default function AnalysisPage() { - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/(dashboard)/dashboard/page.tsx b/services/frontend/src/app/(dashboard)/dashboard/page.tsx index 7052caa..ee1b5c4 100644 --- a/services/frontend/src/app/(dashboard)/dashboard/page.tsx +++ b/services/frontend/src/app/(dashboard)/dashboard/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getActivity, getDashboardStats } from "@/lib/api/server"; import { DashboardView } from "./view"; @@ -14,5 +15,9 @@ export default async function DashboardPage() { } catch { // Backend unavailable — client hooks will surface the error state. } - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/(dashboard)/media/page.tsx b/services/frontend/src/app/(dashboard)/media/page.tsx index f4b01b5..587fc7e 100644 --- a/services/frontend/src/app/(dashboard)/media/page.tsx +++ b/services/frontend/src/app/(dashboard)/media/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getMediaStatus } from "@/lib/api/server"; import { MediaView } from "./view"; @@ -10,5 +11,9 @@ export default async function MediaPage() { } catch { /* client hooks surface errors */ } - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/(dashboard)/messages/page.tsx b/services/frontend/src/app/(dashboard)/messages/page.tsx index de6ce74..cb4c8a0 100644 --- a/services/frontend/src/app/(dashboard)/messages/page.tsx +++ b/services/frontend/src/app/(dashboard)/messages/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getConfig, getGuilds } from "@/lib/api/server"; import { MessagesView } from "./view"; @@ -12,9 +13,11 @@ export default async function MessagesPage() { /* client hooks surface errors */ } return ( - + + + ); } diff --git a/services/frontend/src/app/(dashboard)/moderation/page.tsx b/services/frontend/src/app/(dashboard)/moderation/page.tsx index 9863712..1bfc694 100644 --- a/services/frontend/src/app/(dashboard)/moderation/page.tsx +++ b/services/frontend/src/app/(dashboard)/moderation/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getModerationActions, getModerationStats } from "@/lib/api/server"; import { ModerationView } from "./view"; @@ -14,5 +15,9 @@ export default async function ModerationPage() { } catch { /* client hooks surface errors */ } - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/(dashboard)/recordings/page.tsx b/services/frontend/src/app/(dashboard)/recordings/page.tsx index 95b2da6..672a088 100644 --- a/services/frontend/src/app/(dashboard)/recordings/page.tsx +++ b/services/frontend/src/app/(dashboard)/recordings/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getRecordings } from "@/lib/api/server"; import { RecordingsView } from "./view"; @@ -12,5 +13,9 @@ export default async function RecordingsPage() { } catch { /* client hooks surface errors */ } - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/(dashboard)/voice/page.tsx b/services/frontend/src/app/(dashboard)/voice/page.tsx index 7a9bfd6..d6eba00 100644 --- a/services/frontend/src/app/(dashboard)/voice/page.tsx +++ b/services/frontend/src/app/(dashboard)/voice/page.tsx @@ -1,3 +1,4 @@ +import { PageTransition } from "@/components/shared"; import { getGuilds, getVoiceStatus } from "@/lib/api/server"; import { VoiceView } from "./view"; @@ -11,5 +12,9 @@ export default async function VoicePage() { } catch { /* client hooks surface errors */ } - return ; + return ( + + + + ); } diff --git a/services/frontend/src/app/globals.css b/services/frontend/src/app/globals.css index 6960f4f..86aab0e 100644 --- a/services/frontend/src/app/globals.css +++ b/services/frontend/src/app/globals.css @@ -276,6 +276,11 @@ } .animate-breathe { animation: breathe 3.5s ease-in-out infinite; } +/* Page-enter: subtle rise+settle. Respect reduced-motion (handled globally). */ +.animate-fade-up { + animation: fade-up 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; +} + @media (prefers-reduced-motion: reduce) { .scan-line::after, .scan-line, @@ -283,7 +288,8 @@ .animate-spin-disc, .animate-pulse-ring, .animate-shimmer, - .animate-breathe { + .animate-breathe, + .animate-fade-up { animation: none !important; } .scan-line { diff --git a/services/frontend/src/components/shared/index.ts b/services/frontend/src/components/shared/index.ts index cdac96c..3d5c499 100644 --- a/services/frontend/src/components/shared/index.ts +++ b/services/frontend/src/components/shared/index.ts @@ -1,5 +1,6 @@ export { GuildChannelPicker } from "./guild-picker"; export { MarkdownLite } from "./markdown"; +export { PageTransition } from "./page-transition"; export { MetricTile, SectionHeader } from "./section"; export { EmptyState, diff --git a/services/frontend/src/components/shared/page-transition.tsx b/services/frontend/src/components/shared/page-transition.tsx new file mode 100644 index 0000000..216ca41 --- /dev/null +++ b/services/frontend/src/components/shared/page-transition.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils"; + +/** + * Wraps a view's root so the whole panel stack rises + settles on mount. + * The animation is disabled under prefers-reduced-motion via globals.css. + */ +export function PageTransition({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { + return
{children}
; +} diff --git a/services/frontend/src/components/shared/section.tsx b/services/frontend/src/components/shared/section.tsx index 8c461b0..f5e7e74 100644 --- a/services/frontend/src/components/shared/section.tsx +++ b/services/frontend/src/components/shared/section.tsx @@ -55,7 +55,12 @@ export function MetricTile({ ? "var(--color-signal)" : "var(--color-ink)"; return ( -
+
{label}
{icon && {icon}}