Anchor nprogress to main content (below header)

This commit is contained in:
Kalle
2026-06-12 17:35:50 +03:00
parent a678b953bf
commit 0f010db221
3 changed files with 36 additions and 2 deletions

View File

@@ -58,6 +58,13 @@ import { TopRightButtons } from "./TopRightButtons";
const MAX_DESKTOP_FRIENDS = 4;
/** Id of the loading-bar track rendered inside the header. NProgress mounts its
* bar into it; the track sits just below the header border, spans only the area
* between the sidebars, and clips the bar so it never extends over a sidebar.
* Living inside the header makes it follow the header on scroll and in
* standalone (PWA) mode where the header grows by the safe-area inset. */
export const NPROGRESS_ANCHOR_ID = "nprogress-anchor";
function useRelativeDayFormat() {
const { i18n } = useTranslation();
const { formatter: timeFormatter } = useDateTimeFormat({
@@ -438,6 +445,7 @@ export function Layout({
}
chatUnreadCount={chatContext?.totalUnreadCount}
/>
<div id={NPROGRESS_ANCHOR_ID} aria-hidden />
</header>
{showLeaderboard ? (
<FuseZone

View File

@@ -38,7 +38,7 @@ import type { Route } from "./+types/root";
import { Catcher } from "./components/Catcher";
import { SendouToastRegion, toastQueue } from "./components/elements/Toast";
import { FusePageInit } from "./components/fuse/Fuse";
import { Layout } from "./components/layout";
import { Layout, NPROGRESS_ANCHOR_ID } from "./components/layout";
import { getUser } from "./features/auth/core/user.server";
import { userMiddleware } from "./features/auth/core/user-middleware.server";
import { ChatProvider } from "./features/chat/ChatProvider";
@@ -73,6 +73,11 @@ import "~/styles/utils.css";
import "~/styles/flags.css";
import "nprogress/nprogress.css";
// Anchor the loading bar to the header so it sits between the sidebars. Set at
// module scope (not in an effect) so the very first navigation's NProgress.start
// already targets the header instead of briefly rendering over the sidebar.
NProgress.configure({ parent: `#${NPROGRESS_ANCHOR_ID}` });
export const shouldRevalidate: ShouldRevalidateFunction = (args) => {
if (isRevalidation(args)) return true;
@@ -277,7 +282,7 @@ function useLoadingIndicator() {
NProgress.done();
}
},
250,
150,
[transition.state],
);
}

View File

@@ -614,6 +614,27 @@
}
}
/* Track that NProgress renders its bar into (see NPROGRESS_ANCHOR_ID). It is a
* child of the header positioned just below the header's 1.5px bottom border, so
* it follows the header on scroll and in standalone (PWA) mode. It spans only the
* header width (the area between the sidebars), and its overflow: hidden clips the
* bar — which NProgress slides in from the left — so the bar can never extend over
* a sidebar. Unlayered + id selector so it beats NProgress's own unlayered
* .nprogress-custom-parent rules (and the @layer-wrapped CSS modules). */
#nprogress-anchor {
position: absolute;
top: calc(100% + 1.5px);
left: 0;
right: 0;
height: 2.5px;
overflow: hidden;
pointer-events: none;
}
.nprogress-custom-parent #nprogress .bar {
height: 100%;
}
.top-leaderboard {
min-height: 100px;
max-height: 100px;