Fix nested-route nav highlighting, frozen footer year, missing themeColor, keyboard-unreachable dropdown - #323
Merged
chonilius merged 4 commits intoAug 25, 2026
Conversation
Active-nav-item detection used exact pathname equality (pathname === item.href). A maintainer navigating from the pipeline board into a specific bounty (/issues/abc123, reached via PipelineBoard.tsx's own links) made pathname !== "/issues", so "Bounty pipeline" lost its highlighted state exactly while the user was drilled into a page that's conceptually still part of that section — the sidebar appeared to have nothing selected, a common and expected navigation state this exact-match check didn't account for. Added a prefix check for non-overview items (anything not under /dashboard/*) so a nested route like /issues/abc123 keeps its parent section highlighted, while /dashboard/* overview links keep exact matching so they don't stay highlighted while browsing unrelated sections (closes MergeFi#220).
…mized builds Footer.tsx computed new Date().getFullYear() directly in a Server Component with no dynamic API usage of its own. Next.js statically optimizes any route it can prove has no per-request dynamic data, so that call was evaluated once, at build time, not per-request, on any route Next was able to optimize this way — nothing in Footer.tsx declared force-dynamic or otherwise opted into per-request evaluation, so the correctness of the displayed year was incidentally dependent on unrelated routes' fetch behavior. On a statically-optimized route, the year silently froze at whatever `next build` was run and stayed wrong for the rest of that build's lifetime, with nothing surfacing the staleness. Extracted the year into a new CopyrightYear Client Component: the initial render still uses whatever Date.now() was available at build/SSR time, but a useEffect always runs client-side on every hydration and re-reads the browser's actual current date, correcting any stale build-time year the moment a visitor's page hydrates rather than waiting for the next deploy (closes MergeFi#221).
…kgrounds viewport only set colorScheme, which controls native UI elements (scrollbars, form controls) but not the mobile browser's own chrome — the address/status bar background on iOS Safari and Android Chrome. Neither this app's dark background (#0a0a0f) nor its light background (#fbfbfd, both from globals.css) was ever passed as themeColor, so mobile browsers fell back to their own default (usually white), visibly clashing with the page the instant it loads — especially in dark mode, where a white/default chrome bar sits directly above a near-black page. Added a themeColor array mirroring the same prefers-color-scheme split already used in globals.css (closes MergeFi#224).
The "Dashboards" submenu was a CSS-only hover pattern (group-hover:visible group-hover:opacity-100) with no focus/ focus-within variant anywhere, no onClick on the trigger, and no aria-expanded/aria-haspopup. A keyboard user tabbing to the "Dashboards" button never made the submenu visible — Tab skipped straight past it — and the three links inside remained in the tab order (invisible/ opacity-0, not display: none) rather than being removed from it, so focus could land on a link a keyboard user couldn't see was there at all, worse than being skipped entirely. Added group-focus-within:visible group-focus-within:opacity-100 alongside the existing hover variant, so focus landing anywhere inside the wrapper (the trigger, or one of the links themselves once tabbed to) keeps the menu open the same way hover does. Also added aria-haspopup="menu" to the trigger for correct semantics (closes MergeFi#222).
|
@miraclesonly is attempting to deploy a commit to the chonilius' projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@miraclesonly Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four fixes across navigation, layout metadata, and accessibility:
Nested routes lose sidebar highlighting (#220) — active-nav detection used exact
pathname === item.hrefequality. Navigating from the pipeline board into a specific bounty (/issues/abc123) madepathname !== "/issues", so "Bounty pipeline" lost its highlighted state exactly while the user was still conceptually inside that section — the sidebar appeared to have nothing selected. Added a prefix check for non-overview items so nested routes keep their parent section highlighted, while/dashboard/*overview links keep exact matching.Frozen copyright year on static builds (#221) —
Footer.tsxcomputednew Date().getFullYear()directly in a Server Component with no dynamic API usage. On any route Next.js could statically optimize, that call evaluated once at build time, not per-request — the year could silently freeze for the rest of that build's lifetime. Extracted it into a newCopyrightYearClient Component whoseuseEffectalways runs client-side on every hydration, correcting a stale build-time year the moment any visitor's page hydrates.No themeColor viewport metadata (#224) —
viewportonly setcolorScheme, which doesn't control the mobile browser's own chrome (address/status bar). Neither the app's dark (#0a0a0f) nor light (#fbfbfd) background was ever passed asthemeColor, so mobile browsers fell back to a default (usually white) that visibly clashes with the page, especially in dark mode. Added athemeColorarray matchingglobals.css's existingprefers-color-schemesplit.Keyboard-unreachable Dashboards dropdown (#222) — the submenu was CSS-only hover (
group-hover:visible group-hover:opacity-100) with nofocus/focus-withinvariant, noonClick, and noaria-haspopup/aria-expanded. A keyboard user tabbing to the trigger never made the submenu visible, and the three links inside stayed in the tab order while invisible — worse than being skipped, since focus could land somewhere a keyboard user couldn't see. Addedgroup-focus-within:visible group-focus-within:opacity-100alongside the hover variant, plusaria-haspopup="menu".Changes
src/components/dashboard/DashboardShell.tsx— prefix-based active-nav check.src/components/layout/Footer.tsx/ newCopyrightYear.tsx— self-correcting year.src/app/layout.tsx—themeColoradded toviewport.src/components/layout/Navbar.tsx—focus-within+aria-haspopupon the dropdown.Test plan
npx tsc --noEmit— cleannpx eslinton all changed files — cleannpx jest(full suite) — 207/207 passing, no regressionsCloses #220
Closes #221
Closes #224
Closes #222