From 755c4cf34edabecb1d223851fcf4d6bce39e79d6 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Fri, 5 Jun 2026 16:02:34 -0400 Subject: [PATCH 1/2] Align calculator header with main website header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The calculator-app shell (/us/reports etc.) still rendered through StandardLayout → HomeHeader, which kept the pre-#1059 design (click-open dropdowns, no per-item underline, missing Model/Python/Events). Update NavItem to mirror the website Header (hover-open, underline with active detection, nested children), expand the navItems in HomeHeader to match, and teach MobileMenu to render nested children. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/src/components/homeHeader/MobileMenu.tsx | 24 +- app/src/components/homeHeader/NavItem.tsx | 348 ++++++++++++------- app/src/components/shared/HomeHeader.tsx | 42 ++- changelog_entry.yaml | 1 + 4 files changed, 290 insertions(+), 125 deletions(-) diff --git a/app/src/components/homeHeader/MobileMenu.tsx b/app/src/components/homeHeader/MobileMenu.tsx index e23cc37d4..9d62652a3 100644 --- a/app/src/components/homeHeader/MobileMenu.tsx +++ b/app/src/components/homeHeader/MobileMenu.tsx @@ -58,7 +58,7 @@ export default function MobileMenu({ opened, onOpen, onClose, navItems }: Mobile className="tw:flex tw:flex-col" style={{ gap: spacing.xs, paddingLeft: spacing.md }} > - {item.dropdownItems.map((dropdownItem) => ( + {item.dropdownItems.flatMap((dropdownItem) => [ {dropdownItem.label} - - ))} + , + ...(dropdownItem.children ?? []).map((grandchild) => ( + + {grandchild.label} + + )), + ])} ) : ( diff --git a/app/src/components/homeHeader/NavItem.tsx b/app/src/components/homeHeader/NavItem.tsx index 2dd4ecc01..d1b2d1f84 100644 --- a/app/src/components/homeHeader/NavItem.tsx +++ b/app/src/components/homeHeader/NavItem.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { IconChevronDown } from '@tabler/icons-react'; import { AppLink } from '@/components/AppLink'; +import { useAppPathname } from '@/contexts/LocationContext'; import { useAppNavigate } from '@/contexts/NavigationContext'; import { colors, typography } from '@/designTokens'; @@ -8,6 +9,8 @@ export interface DropdownItem { label: string; onClick?: () => void; href?: string; + /** Nested children rendered indented under this item. One level deep only. */ + children?: DropdownItem[]; } export interface NavItemSetup { @@ -22,47 +25,130 @@ interface NavItemProps { setup: NavItemSetup; } +const NAV_ITEM_PADDING_X = 14; +const NAV_UNDERLINE_INSET = 10; +const DROPDOWN_GAP = 10; +const HOVER_OPEN_DELAY_MS = 100; +const HOVER_CLOSE_DELAY_MS = 200; + const navItemStyle: React.CSSProperties = { color: colors.text.inverse, fontWeight: typography.fontWeight.medium, fontSize: '15px', fontFamily: typography.fontFamily.primary, textDecoration: 'none', - padding: '6px 14px', - borderRadius: '6px', - transition: 'background-color 0.15s ease', + padding: `8px ${NAV_ITEM_PADDING_X}px`, letterSpacing: '0.01em', + position: 'relative', }; -const hoverHandlers = { - onMouseEnter: (e: React.MouseEvent) => { - e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.12)'; - }, - onMouseLeave: (e: React.MouseEvent) => { - e.currentTarget.style.backgroundColor = 'transparent'; - }, -}; +function NavUnderline({ visible }: { visible: boolean }) { + return ( +