From 7370e6c128b819bf51260783c6ffbe47be8e0b47 Mon Sep 17 00:00:00 2001
From: yyamashita <yyamashita@hetzner.yyamashita.com>
Date: Tue, 14 Jul 2026 23:09:27 +0900
Subject: Add navigation progress bar for client-side transitions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 app/root.tsx | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

(limited to 'app/root.tsx')

diff --git a/app/root.tsx b/app/root.tsx
index 37827e4..50fca78 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -1,3 +1,4 @@
+import { useEffect, useState } from "react";
 import {
   isRouteErrorResponse,
   Links,
@@ -5,6 +6,7 @@ import {
   Outlet,
   Scripts,
   ScrollRestoration,
+  useNavigation,
 } from "react-router";
 
 import type { Route } from "./+types/root";
@@ -42,8 +44,46 @@ export function Layout({ children }: { children: React.ReactNode }) {
   );
 }
 
+function NavigationProgress() {
+  const navigation = useNavigation();
+  const [phase, setPhase] = useState<"idle" | "loading" | "done">("idle");
+
+  useEffect(() => {
+    if (navigation.state !== "idle") {
+      setPhase("loading");
+    } else {
+      setPhase((prev) => (prev === "loading" ? "done" : prev));
+    }
+  }, [navigation.state]);
+
+  useEffect(() => {
+    if (phase !== "done") return;
+    const t = setTimeout(() => setPhase("idle"), 500);
+    return () => clearTimeout(t);
+  }, [phase]);
+
+  if (phase === "idle") return null;
+
+  return (
+    <div
+      aria-hidden
+      className="fixed top-0 left-0 right-0 z-50 h-0.5 bg-indigo-400 origin-left"
+      style={
+        phase === "loading"
+          ? { animation: "nav-progress 10s cubic-bezier(0.1, 0.9, 0.2, 1) forwards" }
+          : { transform: "scaleX(1)", opacity: 0, transition: "opacity 400ms" }
+      }
+    />
+  );
+}
+
 export default function App() {
-  return <Outlet />;
+  return (
+    <>
+      <NavigationProgress />
+      <Outlet />
+    </>
+  );
 }
 
 export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
-- 
cgit v1.2.3

