Skip to content

feat: implement search UI with responsive mobile and desktop experiences#29

Merged
michellepace merged 1 commit intomainfrom
feature/searchbox
Jan 7, 2026
Merged

feat: implement search UI with responsive mobile and desktop experiences#29
michellepace merged 1 commit intomainfrom
feature/searchbox

Conversation

@michellepace
Copy link
Owner

Summary

  • Add desktop search with popover hints panel showing syntax examples (e.g., [tag], user:1234, "exact phrase")
  • Add mobile search with full-screen overlay, animated slide-in input, and delayed hints display
  • Create shared components: SearchInput, SearchHints, SearchPopoverContent, and useSearch hook
  • Add /search route with query parameter display (results page scaffolded)
  • Remove unused command.tsx (cmdk wrapper) and consolidate CSS tokens in globals.css

Test plan

  • Desktop: click search input → popover with hints appears
  • Desktop: type query and press Enter → navigates to /search?q=...
  • Desktop: press Escape → popover closes
  • Mobile: tap search icon → overlay + input bar appear with animations
  • Mobile: hints appear after ~500ms delay
  • Mobile: tap backdrop → search closes and resets
  • Mobile: submit search → navigates to /search?q=...
  • Verify "Ask a question" link in hints panel works

🤖 Generated with Claude Code

Search Components:
- Add DesktopSearch with popover hints panel on focus
- Add MobileSearch with full-screen overlay and animated slide-in input
- Create shared SearchInput, SearchHints, and SearchPopoverContent
- Add useSearch hook for state management (open/close, query, submit)
- Add /search route with query parameter display

Navigation:
- Integrate DesktopSearch in desktop-topbar (max-w-2xl container)
- Add MobileSearch icon button to mobile-topbar

Cleanup:
- Remove placeholder searchbox.tsx and unused command.tsx (cmdk wrapper)
- Consolidate CSS tokens in globals.css using var() references

Docs:
- Add code quality evaluation workflow prompt
- Move search feature spec to DONE folder

Replaces the placeholder with a working search UI. Desktop shows syntax hints
(e.g., [tag], user:1234, "exact phrase") in a popover. Mobile uses an overlay
pattern with delayed hint display. Search results page is scaffolded for future
implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
devflow Ready Ready Preview, Comment Jan 7, 2026 1:03am

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added search functionality with integrated search bar in mobile and desktop navigation.
    • Created dedicated search results page.
    • Implemented contextual search hints and suggestions panel.
  • Refactor

    • Updated core colour theming system for improved consistency.
    • Redesigned search components in app navigation.
  • Chores

    • Removed legacy command palette UI components.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This pull request introduces a comprehensive search feature including a dedicated search page, new desktop and mobile search UI components, a search hints system, and a supporting hook for search state management. The theme tokens in globals.css are reorganised for consistency, and the command palette component is removed. Navigation components are updated to integrate the new search interface.

Changes

Cohort / File(s) Change Summary
Search Page
app/(app)/search/page.tsx
New async server component rendering search results page with query parameter handling and conditional query display.
Search Components
components/search/desktop-search.tsx, components/search/mobile-search.tsx
Two new client-side search components—desktop uses popover overlay for hints, mobile uses slide-in panel with backdrop overlay and auto-focus mechanics.
Search Support
components/search/search-input.tsx, components/search/search-hints.tsx, components/search/search-hints-data.ts, components/search/search-popover-content.tsx
New reusable search input field with icon, search hints panel with responsive grid layout (1 column mobile, 2 desktop), hint data constants and types, and configured popover wrapper.
Search Hook
hooks/use-search.ts
New custom hook managing search state (open/close, query, input ref) and handlers for form submission (navigating to /search with encoded query) and keyboard events.
Navigation Updates
components/navigation/desktop-topbar.tsx, components/navigation/mobile-topbar.tsx
Desktop topbar replaces Searchbox with DesktopSearch and narrows max-width. Mobile topbar wraps MobileSearch and MobileNav in a flex container.
Theme Tokens
app/globals.css
Reorganised color token definitions: card/popover linked to foreground, accent to muted, input to border, sidebar colours shimmed to use secondary/card tokens; dark-mode overrides aligned for consistency.
Removed Components
components/search/searchbox.tsx, components/ui/command.tsx
Searchbox placeholder component deleted; entire command palette module (Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandSeparator, CommandItem, CommandShortcut) removed.
Documentation
x_docs/DONE/search.md, x_docs/my_notes/prompt-QA.md
New design specification for Stack Overflow-inspired search UI with mobile/desktop behaviour, hints schema, and form interaction; new QA workflow documentation for code quality evaluation and reporting.

Sequence Diagram

sequenceDiagram
    participant User
    participant SearchUI as Search UI<br/>(Desktop/Mobile)
    participant SearchInput as Search Input
    participant SearchHints as Search Hints
    participant useSearch as useSearch Hook
    participant Router as Router
    participant SearchPage as Search Page

    User->>SearchUI: Click search trigger
    activate SearchUI
    SearchUI->>SearchUI: open() & focus input
    SearchUI->>SearchHints: Show hints panel<br/>(mobile: after 0.5s delay)
    activate SearchHints
    
    User->>SearchInput: Type query
    SearchInput->>useSearch: setQuery(value)
    
    User->>SearchInput: Press Enter
    SearchInput->>useSearch: handleSubmit()
    activate useSearch
    useSearch->>useSearch: Build /search?q=<encoded>
    useSearch->>Router: navigate(url)
    activate Router
    deactivate useSearch
    deactivate SearchHints
    deactivate SearchUI
    
    Router->>SearchPage: Route to /search
    activate SearchPage
    SearchPage->>SearchPage: Await searchParams
    SearchPage->>SearchPage: Render results for q
    deactivate SearchPage
    deactivate Router
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #28: Introduces related search UI component code and command palette modifications, overlapping with searchbox and popover/dialog primitives.
  • PR #26: Modifies the same navigation components (desktop-topbar.tsx, mobile-topbar.tsx) and global styles (app/globals.css), affecting topbar layout and theme consistency.
  • PR #4: Updates app/globals.css theme token definitions and colour mappings, affecting the same CSS variable reorganisation approach.

Poem

🐰 Hoppity-hops through the search,
Desktop and mobile in perfect perch,
Hints bloom bright, tokens align,
Commands removed—oh how they shine!
A search renewed, so spry and keen,

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarises the main change: implementing responsive search UI with mobile and desktop experiences, which is the primary focus of the changeset.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing a clear summary of changes, component additions, routing, and a detailed test plan.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/searchbox

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @components/search/mobile-search.tsx:
- Line 80: The className contains invalid Tailwind syntax
`top-(--top-bar-height)` in the mobile search component; update the arbitrary
value to use square-bracket syntax with the CSS custom property, e.g., replace
that token with the Tailwind arbitrary form using the CSS variable (use
top-[var(--top-bar-height)]) inside the same className string so Tailwind
recognizes the value.

In @components/search/search-hints.tsx:
- Line 1: This component is missing the "use client" directive which is required
because it accepts and invokes the onClose callback (used in the event handler
in this component); fix it by adding the "use client" directive as the very
first line of components/search/search-hints.tsx (before the import Link
statement) so the file is treated as a client component and can receive function
props like onClose from DesktopSearch/MobileSearch.

In @x_docs/my_notes/prompt-QA.md:
- Line 39: Replace the possessive "its" with the contraction "it's" in the note
fragment "(do not limit architecture consideration to this only, its provided as
an aid)" so the text reads "(do not limit architecture consideration to this
only, it's provided as an aid)"; update the single-word token "its" to "it's"
where that exact phrase appears (e.g., in the prompt-QA.md note line).
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cf3324 and fc07694.

📒 Files selected for processing (15)
  • app/(app)/search/page.tsx
  • app/globals.css
  • components/navigation/desktop-topbar.tsx
  • components/navigation/mobile-topbar.tsx
  • components/search/desktop-search.tsx
  • components/search/mobile-search.tsx
  • components/search/search-hints-data.ts
  • components/search/search-hints.tsx
  • components/search/search-input.tsx
  • components/search/search-popover-content.tsx
  • components/search/searchbox.tsx
  • components/ui/command.tsx
  • hooks/use-search.ts
  • x_docs/DONE/search.md
  • x_docs/my_notes/prompt-QA.md
💤 Files with no reviewable changes (2)
  • components/search/searchbox.tsx
  • components/ui/command.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Use British English throughout the codebase (comments, strings, documentation)

Files:

  • components/search/mobile-search.tsx
  • x_docs/DONE/search.md
  • components/search/search-popover-content.tsx
  • hooks/use-search.ts
  • components/search/search-input.tsx
  • components/search/desktop-search.tsx
  • components/search/search-hints-data.ts
  • app/(app)/search/page.tsx
  • components/navigation/desktop-topbar.tsx
  • components/search/search-hints.tsx
  • x_docs/my_notes/prompt-QA.md
  • components/navigation/mobile-topbar.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Only add "use client" directive when interactivity is needed
Avoid manual useMemo/useCallback unless profiling shows need

Files:

  • components/search/mobile-search.tsx
  • components/search/search-popover-content.tsx
  • hooks/use-search.ts
  • components/search/search-input.tsx
  • components/search/desktop-search.tsx
  • components/search/search-hints-data.ts
  • app/(app)/search/page.tsx
  • components/navigation/desktop-topbar.tsx
  • components/search/search-hints.tsx
  • components/navigation/mobile-topbar.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use @/ import aliases, even for sibling files (use @/app/fonts instead of ./fonts)

Files:

  • components/search/mobile-search.tsx
  • components/search/search-popover-content.tsx
  • hooks/use-search.ts
  • components/search/search-input.tsx
  • components/search/desktop-search.tsx
  • components/search/search-hints-data.ts
  • app/(app)/search/page.tsx
  • components/navigation/desktop-topbar.tsx
  • components/search/search-hints.tsx
  • components/navigation/mobile-topbar.tsx
**/*.css

📄 CodeRabbit inference engine (CLAUDE.md)

Tailwind CSS v4: Use @import "tailwindcss" syntax instead of @tailwind directives

Files:

  • app/globals.css
🧠 Learnings (10)
📚 Learning: 2025-12-10T20:20:46.607Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:46.607Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) from clerk/nextjs can be used inside Server Components without adding 'use client' in the consuming component. They manage client/server boundary internally. When reviewing code, prefer omitting 'use client' in server components that render these Clerk components and avoid introducing client directives solely for these components. This guideline helps maintain server/server boundary and reduce client bundle size.

Applied to files:

  • components/search/mobile-search.tsx
  • components/search/search-popover-content.tsx
  • components/search/search-input.tsx
  • components/search/desktop-search.tsx
  • app/(app)/search/page.tsx
  • components/navigation/desktop-topbar.tsx
  • components/search/search-hints.tsx
  • components/navigation/mobile-topbar.tsx
📚 Learning: 2026-01-04T14:21:37.960Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 27
File: components/question-card.tsx:1-1
Timestamp: 2026-01-04T14:21:37.960Z
Learning: If a React component renders relative time by calling new Date() during render (e.g., via a getRelativeTime utility), add the 'use client' directive to the component to avoid hydration mismatches between server and client. This guideline applies to components that rely on client-side timestamps; if server-rendered data is required, consider moving time computations to the client side or memoizing/separating the logic.

Applied to files:

  • components/search/mobile-search.tsx
  • components/search/search-popover-content.tsx
  • components/search/search-input.tsx
  • components/search/desktop-search.tsx
  • components/navigation/desktop-topbar.tsx
  • components/search/search-hints.tsx
  • components/navigation/mobile-topbar.tsx
📚 Learning: 2025-12-10T20:20:54.402Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:54.402Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) exported from clerk/nextjs are designed to work in Server Components without requiring a "use client" directive in the consuming component, as they handle the client/server boundary internally with their own directives.

Applied to files:

  • hooks/use-search.ts
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to app/**/[*]/page.{ts,tsx} : Next.js 16: Dynamic route params must be awaited as they are now a Promise - use { params }: { params: Promise<{ id: string }> }

Applied to files:

  • app/(app)/search/page.tsx
📚 Learning: 2025-12-25T13:56:02.914Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 18
File: components/navigation/content-top-bar.tsx:23-30
Timestamp: 2025-12-25T13:56:02.914Z
Learning: In a Grok-style layout, move authenticated user controls (Clerk's UserButton) out of the top navigation and into the sidebar footer (e.g., components/app-sidebar.tsx). Ensure components/navigation/content-top-bar.tsx only renders SignedOut options (Sign in/Sign up). Apply this guideline to all TSX files under components/navigation to enforce consistent placement of authentication UI across the navigation layer.

Applied to files:

  • components/navigation/desktop-topbar.tsx
  • components/navigation/mobile-topbar.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signin.tsx : Sign In component (components/auth/clerk-signin.tsx) should be a client component with theme-aware logo

Applied to files:

  • components/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signup.tsx : Sign Up component (components/auth/clerk-signup.tsx) should use a static logo

Applied to files:

  • components/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-28T22:41:43.692Z
Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/clerk-provider.tsx : ClerkProvider should be implemented in components/clerk-provider.tsx and apply shadcn theme and Inter font

Applied to files:

  • components/navigation/desktop-topbar.tsx
📚 Learning: 2025-12-30T21:09:31.840Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 23
File: app/globals.css:131-135
Timestamp: 2025-12-30T21:09:31.840Z
Learning: In CSS theme files, duplicating variable declarations in :root and .dark blocks is intentional to keep a parallel structure for readability and future maintenance. This pattern improves quick visual comparison and simplifies adding new themes. If you duplicate, ensure both blocks have identical values for the variables that are shared; consider centralizing truly shared tokens later only if maintenance burden grows.

Applied to files:

  • app/globals.css
📚 Learning: 2026-01-01T17:53:46.364Z
Learnt from: michellepace
Repo: michellepace/devflow PR: 24
File: components/ui/badge.tsx:7-26
Timestamp: 2026-01-01T17:53:46.364Z
Learning: Files generated by `npx shadcnlatest add <component>` in components/ui/ should not be refactored for style or readability (e.g., splitting long className strings). The project prioritizes staying aligned with upstream shadcn/ui conventions to simplify future diff comparisons and component updates.

Applied to files:

  • x_docs/my_notes/prompt-QA.md
🧬 Code graph analysis (6)
components/search/mobile-search.tsx (5)
hooks/use-search.ts (1)
  • useSearch (11-61)
lib/utils.ts (1)
  • cn (4-6)
components/search/search-input.tsx (1)
  • SearchInput (18-53)
components/search/search-popover-content.tsx (1)
  • SearchPopoverContent (9-26)
components/search/search-hints.tsx (1)
  • SearchHints (11-51)
components/search/search-popover-content.tsx (2)
components/ui/popover.tsx (3)
  • PopoverContent (48-48)
  • PopoverContent (20-40)
  • Popover (8-12)
lib/utils.ts (1)
  • cn (4-6)
components/search/search-input.tsx (2)
components/ui/input.tsx (2)
  • Input (21-21)
  • Input (5-19)
lib/utils.ts (1)
  • cn (4-6)
components/search/desktop-search.tsx (5)
hooks/use-search.ts (1)
  • useSearch (11-61)
components/ui/popover.tsx (3)
  • Popover (48-48)
  • PopoverAnchor (48-48)
  • PopoverAnchor (42-46)
components/search/search-input.tsx (1)
  • SearchInput (18-53)
components/search/search-popover-content.tsx (1)
  • SearchPopoverContent (9-26)
components/search/search-hints.tsx (1)
  • SearchHints (11-51)
components/navigation/desktop-topbar.tsx (1)
components/search/desktop-search.tsx (1)
  • DesktopSearch (9-40)
components/navigation/mobile-topbar.tsx (2)
components/search/mobile-search.tsx (1)
  • MobileSearch (13-105)
components/navigation/mobile-nav.tsx (1)
  • MobileNav (31-132)
🪛 LanguageTool
x_docs/my_notes/prompt-QA.md

[uncategorized] ~39-~39: “its” (belonging to it) seems less likely than “it’s” (it is)
Context: ...rchitecture consideration to this only, its provided as an aid) ### 2. shadcn/ui A...

(AI_HYDRA_LEO_CPT_ITS_ITIS)


[style] ~54-~54: Would you like to use the Oxford spelling “Centralized”? The spelling ‘Centralised’ is also correct.
Context: ...(bg-card not arbitrary colours) ### 4. Centralised Theming - CSS variables for colours, s...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~147-~147: Possible missing comma found.
Context: ...an the main content area (max-w-2xl = 672px vs content's max-w-5xl = 1024px). Thi...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~196-~196: Loose punctuation mark.
Context: ...per ## Shared Components SearchInput, SearchPopoverContent, SearchHints, ...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (11)
components/navigation/desktop-topbar.tsx (1)

2-2: LGTM! Clean component substitution with appropriate width constraint.

The replacement of Searchbox with DesktopSearch and the narrowing to max-w-2xl provides the intended visual distinction between the search bar and page content. The comment accurately reflects the design decision.

Also applies to: 8-11

components/search/search-popover-content.tsx (1)

1-26: LGTM! Well-structured wrapper component.

The SearchPopoverContent component provides a clean abstraction with sensible defaults (start alignment, increased sideOffset, prevented auto-focus) and properly composes the shadcn PopoverContent styling. The "use client" directive is appropriate as this wraps a client component and is consumed in client contexts.

components/search/search-hints-data.ts (1)

1-10: LGTM! Clean data module with proper type safety.

The SEARCH_HINTS array uses as const for immutability and precise type inference, and the SearchHint type derivation is idiomatic TypeScript. All descriptions use British English correctly.

hooks/use-search.ts (1)

1-61: LGTM! Well-structured search state management hook.

The useSearch hook provides a clean API for managing search UI state and navigation. The logic correctly:

  • Trims and validates the query before navigation
  • Encodes the query parameter for URL safety
  • Handles Escape key dismissal
  • Supports configurable reset behaviour via clearOnSubmit
  • Uses non-blocking router.push appropriate for App Router

The "use client" directive is necessary for the React hooks used.

app/globals.css (1)

78-182: LGTM: Token reorganisation improves consistency.

The changes create a clear hierarchy of theme tokens (e.g., card references foreground, popover references card, accent references muted). This consolidation aligns with the PR objectives and improves maintainability by reducing duplication whilst preserving the parallel :root/.dark structure for readability.

components/navigation/mobile-topbar.tsx (1)

14-17: LGTM: Clean integration of mobile search UI.

The flex container appropriately groups the MobileSearch and MobileNav components, maintaining the server component boundary whilst delegating interactivity to child components.

app/(app)/search/page.tsx (1)

1-25: LGTM: Correctly implements Next.js 15 async searchParams pattern.

The component properly awaits the searchParams promise and scaffolds the search results page with appropriate placeholder UI. The TODO at Line 19 clearly indicates the planned implementation path.

components/search/desktop-search.tsx (1)

1-40: LGTM: Well-structured desktop search implementation.

The component correctly uses the "use client" directive, properly integrates the useSearch hook for state management, and cleanly coordinates the Popover, SearchInput, and SearchHints components. The event handling and state synchronisation are implemented correctly.

x_docs/DONE/search.md (1)

1-139: Excellent documentation quality.

The search design specification is comprehensive, well-structured, and clearly documents the requirements, behaviour, and implementation notes for both mobile and desktop search patterns. The British English usage is consistent throughout.

components/search/mobile-search.tsx (1)

1-79: Well-implemented mobile search with proper state management and accessibility.

The component correctly uses the "use client" directive for interactive elements, properly manages focus and hints timing with useEffect cleanup, and includes appropriate aria-labels for accessibility. The integration with the useSearch hook is clean and the overlay/backdrop pattern is implemented correctly.

Also applies to: 81-105

components/search/search-input.tsx (1)

1-55: Clean, well-implemented search input component.

The component correctly uses the "use client" directive for its interactive nature, properly forwards the ref, and implements all prop handlers cleanly. The use of @/ import aliases follows the coding guidelines, and the accessibility attributes are appropriate.

{/* Search input bar - separate card floating below topbar */}
<div
data-slot="mobile-search-input-bar"
className="animate-in slide-in-from-top-2 fade-in-0 fixed left-0 right-0 top-(--top-bar-height) z-50 border-b bg-card px-4 py-3 shadow-sm duration-300 md:hidden"
Copy link

@coderabbitai coderabbitai bot Jan 7, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix invalid Tailwind CSS syntax for CSS custom property.

The syntax top-(--top-bar-height) is invalid. Tailwind requires square brackets for arbitrary values with CSS custom properties.

🔎 Proposed fix
-            className="animate-in slide-in-from-top-2 fade-in-0 fixed left-0 right-0 top-(--top-bar-height) z-50 border-b bg-card px-4 py-3 shadow-sm duration-300 md:hidden"
+            className="animate-in slide-in-from-top-2 fade-in-0 fixed left-0 right-0 top-[var(--top-bar-height)] z-50 border-b bg-card px-4 py-3 shadow-sm duration-300 md:hidden"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="animate-in slide-in-from-top-2 fade-in-0 fixed left-0 right-0 top-(--top-bar-height) z-50 border-b bg-card px-4 py-3 shadow-sm duration-300 md:hidden"
className="animate-in slide-in-from-top-2 fade-in-0 fixed left-0 right-0 top-[var(--top-bar-height)] z-50 border-b bg-card px-4 py-3 shadow-sm duration-300 md:hidden"
🤖 Prompt for AI Agents
In @components/search/mobile-search.tsx at line 80, The className contains
invalid Tailwind syntax `top-(--top-bar-height)` in the mobile search component;
update the arbitrary value to use square-bracket syntax with the CSS custom
property, e.g., replace that token with the Tailwind arbitrary form using the
CSS variable (use top-[var(--top-bar-height)]) inside the same className string
so Tailwind recognizes the value.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Hi @coderabbitai,

I'm skipping this comment because I'm using Tailwind v4.1, and the syntax top-(--top-bar-height) is actually valid.

In Tailwind v4, the parentheses syntax is a shorthand that automatically wraps the CSS variable in var():

<!-- v4 shorthand (what I'm using) -->
<div class="top-(--my-variable)">

<!-- Equivalent traditional syntax -->
<div class="top-[var(--my-variable)]">

From the Tailwind docs:

If you're referencing a CSS variable as an arbitrary value, you can use the custom property syntax... This is just a shorthand for fill-[var(--my-brand-color)] that adds the var() function for you automatically.

No changes needed here! 🎉

Copy link

Choose a reason for hiding this comment

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

@michellepace, you're absolutely right, and I apologize for the incorrect flag!

The top-(--top-bar-height) syntax is indeed valid in Tailwind v4. I should have caught this, especially since I have a learning that confirms this project uses Tailwind v4's shorthand syntax with parentheses for CSS variable references (like bg-(image:--gradient-primary)). The parentheses syntax automatically wraps the variable in var(), making it equivalent to top-[var(--top-bar-height)].

Thank you for the clarification and the documentation reference! The code is correct as written. 🎉

(・ω・) 📚


🧠 Learnings used
Learnt from: michellepace
Repo: michellepace/devflow PR: 18
File: lib/utils.ts:17-19
Timestamp: 2025-12-25T15:46:14.812Z
Learning: In Tailwind CSS v4, the shorthand syntax `bg-(image:--gradient-primary)` with parentheses is valid and equivalent to the traditional bracket syntax `bg-[image:var(--gradient-primary)]`. The project michellepace/devflow has standardized on using the v4 shorthand syntax with parentheses for CSS variable references.

Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to **/*.css : Tailwind CSS v4: Use import "tailwindcss" syntax instead of tailwind directives

@@ -0,0 +1,51 @@
import Link from "next/link";
Copy link

@coderabbitai coderabbitai bot Jan 7, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add missing "use client" directive.

This component receives a callback function (onClose) and uses it in an event handler (Line 44). Since it's consumed by client components (DesktopSearch and MobileSearch) that pass function props, it must be marked as a client component to handle the function prop across the component boundary.

🔎 Proposed fix
+"use client";
+
 import Link from "next/link";
 import { SEARCH_HINTS } from "@/components/search/search-hints-data";
 import { Button } from "@/components/ui/button";
🤖 Prompt for AI Agents
In @components/search/search-hints.tsx at line 1, This component is missing the
"use client" directive which is required because it accepts and invokes the
onClose callback (used in the event handler in this component); fix it by adding
the "use client" directive as the very first line of
components/search/search-hints.tsx (before the import Link statement) so the
file is treated as a client component and can receive function props like
onClose from DesktopSearch/MobileSearch.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Hi @coderabbitai,

I'm skipping this comment because I'm using Next.js 16 and according to the official documentation:

Good to know: You do not need to add the 'use client' directive to every file that contains Client Components. You only need to add it to the files whose components you want to render directly within Server Components.

And:

Once a file is marked with "use client", all its imports and child components are considered part of the client bundle.

Since SearchHints is only ever imported by DesktopSearch and MobileSearch (both of which have "use client"), it's automatically part of the client bundle. The onClose function prop is passed within the client boundary, not across it.

The serialisation restriction only applies when crossing the Server→Client boundary, which isn't happening here.

Sources:

Copy link

Choose a reason for hiding this comment

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

@michellepace, you're absolutely correct, and I apologize for the incorrect suggestion.

Your explanation is spot-on: SearchHints is only imported by DesktopSearch and MobileSearch, both of which already have the "use client" directive. This means SearchHints automatically becomes part of the client bundle through the import chain, and the onClose function prop is passed entirely within the client boundary—not across a Server→Client boundary.

I should have applied the existing learning in my knowledge base (which you previously taught me!) that states exactly this principle. Thank you for the clear explanation and documentation references.


🧠 Learnings used
Learnt from: michellepace
Repo: michellepace/devflow PR: 0
File: :0-0
Timestamp: 2026-01-07T01:26:41.714Z
Learning: In Next.js, child components that are only imported by client components (files with "use client" directive) do not need their own "use client" directive, as they automatically become part of the client bundle through the import chain. The directive only needs to be added at the boundary where the component is rendered directly within Server Components.

Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to **/*.{ts,tsx} : Only add "use client" directive when interactivity is needed

Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:54.402Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) exported from clerk/nextjs are designed to work in Server Components without requiring a "use client" directive in the consuming component, as they handle the client/server boundary internally with their own directives.

Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to components/auth/clerk-signin.tsx : Sign In component (components/auth/clerk-signin.tsx) should be a client component with theme-aware logo

Learnt from: michellepace
Repo: michellepace/devflow PR: 27
File: components/question-card.tsx:1-1
Timestamp: 2026-01-04T14:21:42.060Z
Learning: Components that display relative time by calling `new Date()` during render (e.g., via getRelativeTime utility) require the "use client" directive to avoid React hydration mismatch errors, since server and client will compute different timestamps.

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Tip

For best results, initiate chat on the files or code changes.

@michellepace, you're absolutely correct, and thank you for the detailed explanation!

You've correctly identified that SearchHints doesn't need the "use client" directive because it's only ever imported by components that already have the directive (DesktopSearch and MobileSearch). As you noted, once a file is marked with "use client", all its imports automatically become part of the client bundle, so the directive doesn't need to be repeated in child components.

I appreciate you taking the time to cite the official Next.js documentation—this is a good pattern that reduces unnecessary directive declarations in the codebase. My apologies for the overly cautious comment!


✏️ Learnings added
Learnt from: michellepace
URL: https://github.com/michellepace/devflow/pull/29

Timestamp: 2026-01-07T01:26:41.714Z
Learning: In Next.js, child components that are only imported by client components (files with "use client" directive) do not need their own "use client" directive, as they automatically become part of the client bundle through the import chain. The directive only needs to be added at the boundary where the component is rendered directly within Server Components.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: michellepace
Repo: michellepace/devflow PR: 7
File: components/navigation/navbar/index.tsx:1-12
Timestamp: 2025-12-10T20:20:54.402Z
Learning: Clerk's Next.js components (SignedIn, SignedOut, SignInButton, SignUpButton, UserButton) exported from clerk/nextjs are designed to work in Server Components without requiring a "use client" directive in the consuming component, as they handle the client/server boundary internally with their own directives.

Learnt from: CR
Repo: michellepace/devflow PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T22:41:43.692Z
Learning: Applies to **/*.{ts,tsx} : Only add "use client" directive when interactivity is needed

@michellepace michellepace merged commit f0fee64 into main Jan 7, 2026
7 checks passed
@michellepace michellepace deleted the feature/searchbox branch January 7, 2026 01:28
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