diff --git a/app/src/App.css b/app/src/App.css
index 8f417f52..783dbecf 100644
--- a/app/src/App.css
+++ b/app/src/App.css
@@ -67,6 +67,46 @@ html .mantine-Notifications-root[data-position^='top'] {
background: rgba(0, 0, 0, 0.12);
}
+/*
+ * The tab for the section the user is in.
+ *
+ * react-router marks it with `active` and `aria-current` on its own, and always did -- the
+ * mark reached the DOM and nothing here drew it, so every tab looked identical and clicking
+ * Videos appeared to leave the bar untouched.
+ *
+ * The underline is a pseudo-element in `currentColor`, not a border: `currentColor` follows
+ * the foreground the bar computed for whatever navbar color the user picked, so this works
+ * on all twelve of them (see `navColorNames`) and in all four themes without naming a color;
+ * and a pseudo-element adds no height, where a border would jog the whole row as the mark
+ * moved between tabs. The darker wash is the hover treatment deepened, so the two read as
+ * the same idea.
+ */
+.wrolpi-navbar-link.active {
+ position: relative;
+ background: rgba(0, 0, 0, 0.2);
+}
+
+.wrolpi-navbar-link.active::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ /* rem, not px, so the mark grows with the --ui-scale setting like the text above it. */
+ height: 0.1875rem;
+ background: currentColor;
+}
+
+/*
+ * The same tabs inside the overflow "More" menu and the mobile hamburger, which are the
+ * only place most of them appear on a phone. Those render in a portal, outside the bar,
+ * so they take theme tokens rather than the bar's foreground.
+ */
+.mantine-Menu-item.active {
+ background: var(--head);
+ font-weight: 600;
+}
+
/*
* Everything in the bar takes the bar's foreground.
*
diff --git a/app/src/components/Nav.js b/app/src/components/Nav.js
index 3ee35d49..8dfce4f4 100644
--- a/app/src/components/Nav.js
+++ b/app/src/components/Nav.js
@@ -1,5 +1,5 @@
import React from "react";
-import {Link, NavLink} from "react-router";
+import {Link, NavLink, useLocation} from "react-router";
import {IconMenu2, IconPlugOff, IconTemperature, IconTemperaturePlus} from "@tabler/icons-react";
import {Icon, IconButton, Menu, Tooltip} from "./ui";
import {Media, SettingsContext, StatusContext} from "../contexts/contexts";
@@ -61,9 +61,11 @@ const allLinks = [
{text: 'Playlists', to: '/playlists', key: 'playlists'},
{text: 'Zim', to: '/zim', key: 'zim'},
{text: 'Inventory', to: '/inventory', key: 'inventory'},
- {to: '/more/calculators', text: 'Calculators', key: 'calculators', end: true},
+ // No `end` on these two: a tab stays current for the pages beneath it, and `end` would
+ // drop the mark the moment either grows a sub-page.
+ {to: '/more/calculators', text: 'Calculators', key: 'calculators'},
{text: 'Flasher', to: '/flasher', key: 'flasher'},
- {to: '/more/statistics', text: 'Statistics', key: 'statistics', end: true},
+ {to: '/more/statistics', text: 'Statistics', key: 'statistics'},
];
function MenuLink({link}) {
@@ -130,12 +132,42 @@ const NavDropdownTrigger = React.forwardRef(({text, className, ...props}, ref) =
);
NavDropdownTrigger.displayName = 'NavDropdownTrigger';
+/**
+ * Whether `link` covers `pathname`, matching NavLink's own rule: a link owns the pages
+ * beneath it unless it asks to match its path exactly.
+ *
+ * Duplicated from NavLink deliberately. The question here is asked about a link that is
+ * NOT rendered -- one hidden inside the overflow menu -- so there is no NavLink to ask, and
+ * the hooks that would answer it cannot be called in a loop.
+ */
+export const isLinkActive = (pathname, link) => {
+ // An off-site link (Help) is never the current section.
+ if (!link.to || /^[a-z]+:\/\//i.test(link.to)) return false;
+ const to = link.to.length > 1 ? link.to.replace(/\/+$/, '') : link.to;
+ if (link.end || to === '/') return pathname === to;
+ return pathname === to || pathname.startsWith(`${to}/`);
+}
+
function DropdownLinks({link}) {
// A labelled dropdown trigger: the desktop "More" overflow, or any nested
// link group handed to MenuLink.
+ const {pathname} = useLocation();
+ /*
+ * Mark the trigger when the current section is one of the links folded inside it.
+ *
+ * Without this the bar goes blank the moment the window narrows enough to push the
+ * current tab into "More" -- the mark exists, but only after the user opens the menu
+ * to look for it, which is precisely when they least need telling.
+ *
+ * The class only, not `aria-current`: the real NavLink inside the menu already carries
+ * that, and a second "current" on the button that opens the menu would announce the
+ * same page twice.
+ */
+ const holdsCurrent = (link.links || []).some(child => isLinkActive(pathname, child));
+
return