Skip to content

style(navbar): polish sizing for compact layout#177

Merged
AnnatarHe merged 2 commits intomasterfrom
polish/navigation-bar-sizing
Apr 13, 2026
Merged

style(navbar): polish sizing for compact layout#177
AnnatarHe merged 2 commits intomasterfrom
polish/navigation-bar-sizing

Conversation

@AnnatarHe
Copy link
Copy Markdown
Member

@AnnatarHe AnnatarHe commented Apr 13, 2026

Summary

  • Standardize all interactive elements (buttons, icon buttons, avatar) to h-8 baseline, down from mixed h-10/h-11
  • Shrink logo container from 44px to 32px, remove letter-spacing spread and slogan line
  • Replace "More" text dropdown trigger with compact ellipsis icon button
  • Strip heavy card wrapper (border + background) from profile area, reduce avatar from 40px to 28px
  • Lighten tab control border opacity and tighten gaps throughout for a more compact nav

Test plan

  • Verify nav renders correctly at mobile (<640px), tablet (640-1024px), and desktop (1024px+)
  • Confirm dropdown opens/closes with the ellipsis button and Escape key
  • Check avatar premium gradient ring is visible at 28px size
  • Verify dark mode styling for all modified elements

🤖 Generated with Claude Code


Open with Devin

AnnatarHe and others added 2 commits April 14, 2026 00:59
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>
@AnnatarHe AnnatarHe merged commit 9586cf0 into master Apr 13, 2026
1 check passed
@AnnatarHe AnnatarHe deleted the polish/navigation-bar-sizing branch April 13, 2026 17:48
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 13, 2026

Code Review

Overall 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:


Bugs

1. Dropdown doesn't close after item selection

When a MenuItem triggers a non-navigation action (like onPhoneLogin), the dropdown stays open. The modal opens but the menu is still visible behind it. Fix by accepting an onClose callback and calling it in MenuItem:

// 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 (navigation-bar.tsx)

// Current — breaks non-English users
<Link ...>Login</Link>

// Should use t(), e.g.
<Link ...>{t('app.login')}</Link>

The existing t('app.slogan') was used in the old code path for unauthenticated users. A proper translation key should be used here.

3. Hardcoded aria-label="More options" in Dropdown (authed.tsx)

This component receives t via its parent scope. Since Dropdown is internal to the file, either accept a label prop or pass the translated string:

// In the Dropdown button
aria-label={t('app.menu.moreOptions')}

Code Quality

4. href as any casts in MenuItem and navigation-bar.tsx

These appear several times and silently bypass Next.js typed routes. If typed routes are enabled in next.config.ts, these casts can hide broken links at compile time. Either use proper Route types or disable typed routes globally — don't paper over it with as any.

5. Duplicate profile && profileSlug guards (navigation-bar.tsx)

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 repository is a Next.js 16 app

This project uses Next.js 15 (per CLAUDE.md and package.json). Should be corrected to avoid confusing future agents/contributors.


Notable Positive Changes

  • useId() for aria-controls — correct approach
  • Escape key handler with proper cleanup — was missing before
  • Removing inline <style jsx global> CSS-in-JS for animations — correct
  • MenuItem component is a clean abstraction that eliminates the previous copy-paste of link/button variants
  • icon as a component reference (not a render function) in leftMenu is the cleaner pattern
  • Removing the unused navigation-bar.module.css (which had a duplicate left: -5px and an invalid RGB value rgba(272, 242, 242, 0.5)) is good housekeeping

Minor

  • useCtrlP hook was removed, which drops the Ctrl+P keyboard shortcut for search. If this was intentional, worth noting in the PR description. If not, the shortcut should be re-wired.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread AGENTS.md
# 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__`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Next.js 16 has not been released yet (the current version is 15). This is likely a typo for Next.js 14 or 15.


{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">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
<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">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When switching to a flexbox layout for the menu, ensure the list items expand to fill the available space equally.

Suggested change
<li key={item.alt} className="min-w-0">
<li key={item.alt} className="min-w-0 flex-1">

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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',

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review


const { t } = useTranslation(undefined, 'navigation')
const activeSegment = useSelectedLayoutSegment()
const [searchVisible, setSearchVisible] = useState(false)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<h2 className="font-bold text-white">{t('app.slogan')}</h2>
</Link>
)}
Login
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

onClick={() => setIsOpen((current) => !current)}
aria-expanded={isOpen}
aria-controls={menuId}
aria-label="More options"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant