style(navbar): polish sizing for compact layout#177
Conversation
Standardize all interactive elements to h-8 baseline, shrink logo from 44px to 32px, remove slogan line, replace "More" text button with ellipsis icon, strip heavy card wrapper from profile area, and reduce avatar from 40px to 28px. Also fix pre-existing TS errors for dynamic href strings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code ReviewOverall this is a clean, well-structured refactor that meaningfully improves the navbar — better accessibility, simpler components, and a more consistent visual sizing system. A few issues worth addressing: Bugs1. Dropdown doesn't close after item selection When a // Pass onClose down
function MenuItem({ ..., onClose }: MenuItemProps & { onClose?: () => void }) {
const handleClick = () => {
onClick?.()
onClose?.()
}
// ...
}Or alternatively, close the dropdown when any item inside it is clicked (via event delegation on the container div). 2. Hardcoded "Login" string is not localized ( // Current — breaks non-English users
<Link ...>Login</Link>
// Should use t(), e.g.
<Link ...>{t('app.login')}</Link>The existing 3. Hardcoded This component receives // In the Dropdown button
aria-label={t('app.menu.moreOptions')}Code Quality4. These appear several times and silently bypass Next.js typed routes. If typed routes are enabled in 5. Duplicate The tab nav and the right-side auth nav are gated separately by identical conditions. They could be combined into a single conditional block to reduce repetition and make the structure clearer: {profile && profileSlug ? (
<div className="flex items-center gap-3 min-w-0 flex-1">
{/* tabs */}
{/* right auth nav */}
</div>
) : null}6. AGENTS.md version inaccuracy This project uses Next.js 15 (per Notable Positive Changes
Minor
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive set of repository guidelines in AGENTS.md and performs a significant refactor of the navigation bar components to improve accessibility, responsiveness, and visual consistency using Tailwind CSS. The changes include a more robust Dropdown component and a standardized MenuItem system. Feedback focuses on correcting a version typo in the documentation, improving the flexibility of the menu layout by replacing a fixed grid with flexbox, and ensuring consistent height across interactive elements to align with the project's design goals.
| # Repository Guidelines | ||
|
|
||
| ## Project Structure & Module Organization | ||
| This repository is a Next.js 16 app using the App Router and TypeScript. Primary code lives in `src/`: routes in `src/app`, reusable UI in `src/components`, server/client integrations in `src/services`, shared hooks in `src/hooks`, and utility helpers in `src/lib` and `src/utils`. GraphQL operations and generated types live under `src/schema` and `src/gql`. Static files are in `public/`, Jest setup and mocks are in `test/`, and parser-focused tests also live beside source in `src/store/clippings/__tests__`. |
|
|
||
| {profile && profileSlug ? ( | ||
| <div className="min-w-0 flex-1"> | ||
| <ul className="grid grid-cols-3 gap-1 rounded-xl border border-slate-200/60 bg-slate-50 p-1 dark:border-slate-800/60 dark:bg-slate-900/50"> |
There was a problem hiding this comment.
Hardcoding grid-cols-3 makes the layout fragile if items are added to or removed from leftMenu. Consider using a flexbox layout with flex-1 on the children to automatically adapt to the number of menu items.
| <ul className="grid grid-cols-3 gap-1 rounded-xl border border-slate-200/60 bg-slate-50 p-1 dark:border-slate-800/60 dark:bg-slate-900/50"> | |
| <ul className="flex gap-1 rounded-xl border border-slate-200/60 bg-slate-50 p-1 dark:border-slate-800/60 dark:bg-slate-900/50"> |
| const Icon = item.icon | ||
| const isActive = activeSegment === item.targetSegment | ||
| return ( | ||
| <li key={item.alt} className="min-w-0"> |
| aria-current={isActive ? 'page' : undefined} | ||
| className={cn( | ||
| 'flex min-h-9 items-center justify-center gap-2 rounded-xl px-3 py-1.5 text-sm font-medium transition-colors sm:justify-start sm:px-3.5', | ||
| isActive |
There was a problem hiding this comment.
The PR description mentions standardizing interactive elements to an h-8 (32px) baseline. However, these menu items are set to min-h-9 (36px). Using h-8 would be more consistent with the rest of the navigation bar and the stated goal.
'flex h-8 items-center justify-center gap-2 rounded-xl px-3 py-1.5 text-sm font-medium transition-colors sm:justify-start sm:px-3.5',
|
|
||
| const { t } = useTranslation(undefined, 'navigation') | ||
| const activeSegment = useSelectedLayoutSegment() | ||
| const [searchVisible, setSearchVisible] = useState(false) |
There was a problem hiding this comment.
🔴 Ctrl+P / Cmd+K keyboard shortcuts for opening search removed
The PR replaces useCtrlP() (from src/components/searchbar/hooks.tsx) with a plain useState(false) for managing search visibility. The useCtrlP hook registered global keyboard listeners for Ctrl+P, Cmd+P, Ctrl+K, and Cmd+K to toggle the search bar open, and also managed body scroll locking via disableBodyScroll/enableBodyScroll. After this change, users can no longer open the search bar with keyboard shortcuts, which is a significant UX regression. The hook still exists in the codebase (src/components/searchbar/hooks.tsx) and has no other consumers.
Prompt for agents
The useCtrlP hook from src/components/searchbar/hooks.tsx was removed during this refactor. It provided Ctrl+P, Cmd+P, Ctrl+K, and Cmd+K keyboard shortcuts to open the search bar, plus body scroll locking. The fix should either: (1) re-import and use the useCtrlP hook to control search visibility (replacing the plain useState), or (2) integrate the keyboard shortcut logic directly into the NavigationBar component. The hook is still exported from src/components/searchbar/hooks.tsx and just needs to be wired back up. The old code was: const { visible, setVisible } = useCtrlP(); and SearchBar received visible and setVisible(false) on close.
Was this helpful? React with 👍 or 👎 to provide feedback.
| <h2 className="font-bold text-white">{t('app.slogan')}</h2> | ||
| </Link> | ||
| )} | ||
| Login |
There was a problem hiding this comment.
🔴 Hardcoded English "Login" text violates i18n rule
The .windsurfrules mandate: "Supporting english, simplified chinese and korean by default. should import useTranslation from @/i18n or @/i18n/client." The "Login" button text on line 98 is hardcoded in English instead of using t(). The old code used {t('app.slogan')} for the unauthenticated view, which was properly internationalized.
Prompt for agents
Line 98 in navigation-bar.tsx has a hardcoded "Login" string. Per .windsurfrules, all user-facing text must be internationalized. Replace the hardcoded text with a translation call, e.g. t('app.login') or a similar key. You will also need to add the corresponding key to the locale files: src/locales/en/navigation.json, src/locales/zhCN/navigation.json, and src/locales/ko/navigation.json.
Was this helpful? React with 👍 or 👎 to provide feedback.
| onClick={() => setIsOpen((current) => !current)} | ||
| aria-expanded={isOpen} | ||
| aria-controls={menuId} | ||
| aria-label="More options" |
There was a problem hiding this comment.
🔴 Hardcoded English "More options" aria-label violates i18n rule
The .windsurfrules mandate: "Supporting english, simplified chinese and korean by default." The aria-label="More options" on the Dropdown trigger button (line 59) is hardcoded in English instead of using a translated string via t(). This means non-English screen-reader users will hear the label in English.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
h-8baseline, down from mixedh-10/h-11Test plan
🤖 Generated with Claude Code