diff --git a/packages/ui/styles/base.css b/packages/ui/styles/base.css index b83ec98b9..29f103b92 100644 --- a/packages/ui/styles/base.css +++ b/packages/ui/styles/base.css @@ -114,17 +114,18 @@ animation: chat-text-shimmer 2.5s linear infinite; } -/* Navigation progress bar: 1px brand-tinted indeterminate sweep that shows - * across the top of the dashboard while a transition-wrapped push/replace is - * committing. Driven by useIsNavigating(); independent of the actual network, - * so it disappears the moment React commits the new route. */ +/* Navigation progress bar: 2px brand-colored indeterminate sweep with a + * right-edge glow that shows across the top of the dashboard while a + * transition-wrapped push/replace is committing. Driven by useIsNavigating(); + * independent of the actual network, so it disappears the moment React commits + * the new route. */ @keyframes nav-progress-sweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } .animate-nav-progress-sweep { - animation: nav-progress-sweep 1.1s ease-in-out infinite; + animation: nav-progress-sweep 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite; } /* Border beam: a brand-tinted highlight sweeps continuously around the diff --git a/packages/views/layout/navigation-progress.tsx b/packages/views/layout/navigation-progress.tsx index 0dedbf5e8..e24418619 100644 --- a/packages/views/layout/navigation-progress.tsx +++ b/packages/views/layout/navigation-progress.tsx @@ -1,19 +1,45 @@ "use client"; +import { useEffect, useState } from "react"; + import { useIsNavigating } from "../navigation"; -// 1px top-of-content progress bar shown while a transition-wrapped +// 2px top-of-content progress bar shown while a transition-wrapped // push/replace is mid-flight. Indeterminate by design — we don't know // when the next route will commit, just that it's coming. +// +// The container stays mounted so it can fade out over 200ms instead of +// vanishing in one frame. The inner sweep is mounted only while navigating +// (plus the fade-out tail); leaving its `infinite` keyframe animation +// running while hidden would burn paints on every dashboard view. export function NavigationProgress() { const isNavigating = useIsNavigating(); - if (!isNavigating) return null; + const [renderSweep, setRenderSweep] = useState(false); + + useEffect(() => { + if (isNavigating) setRenderSweep(true); + }, [isNavigating]); + return (
{ + if (event.propertyName === "opacity" && !isNavigating) { + setRenderSweep(false); + } + }} + className="pointer-events-none absolute inset-x-0 top-0 z-50 h-0.5 overflow-hidden opacity-0 transition-opacity duration-200 data-[visible=true]:opacity-100" > -
+ {renderSweep && ( +
+ )}
); }