Skip to content

Conversation

@remo-lab
Copy link
Contributor

problem

On mobile devices, the navigation bar did not behave consistently across sections:

  • Clicking Technology or Community redirected users to their respective pages instead of showing dropdowns.
  • Only Resources worked correctly as a collapsible dropdown.
    This created a mismatch between desktop and mobile experiences and limited quick access to recent posts.

Solution

We updated the mobile navbar to ensure Technology and Community behave like Resources:

  • Converted Technology and Community from Link components to Collapsible components.
  • Added dropdown content that displays the latest 4 posts for each category in a mobile-friendly list format.
  • Included a "View All" link at the top of each dropdown to navigate to the full category page.
  • Leveraged existing state variables (mobileTechOpen, mobileCommunityOpen) to control open/close behavior.
  • Implemented chevron rotation animation to visually indicate expanded/collapsed state.

Changes Made

  • Replaced Link components with Collapsible components for Technology and Community.
  • Added CollapsibleContent with:
    • Thumbnail, title, date, and author for each post.
    • Consistent styling with the Resources dropdown.
  • Ensured dropdowns are mobile-friendly and visually aligned with the rest of the navbar

Result

  • Technology and Community now show dropdowns on mobile, matching desktop behavior.
  • Users can preview the latest posts without leaving the current page.
  • Dropdowns can be expanded/collapsed by tapping the nav items.
  • The mobile navigation is now consistent across all three main sections: Technology, Community, and Resources.
Recording.2025-12-30.175604.mp4

Testing Notes

  • Verify dropdowns expand/collapse correctly on mobile.
  • Confirm chevron animations reflect open/closed state.
  • Check that "View All" links navigate to the correct category pages.
  • Ensure styling matches the existing Resources dropdown for consistency.

Copilot AI review requested due to automatic review settings December 30, 2025 12:43
@remo-lab remo-lab force-pushed the fix/mobile-navbar-dropdowns branch from 366f42b to 67bf3e6 Compare December 30, 2025 12:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the mobile navigation experience by converting the Technology and Community sections from simple links into interactive collapsible dropdowns, matching the existing behavior of the Resources section. This ensures a consistent user experience across all navigation items on mobile devices.

Key Changes:

  • Replaced Link components with Collapsible components for Technology and Community navigation items
  • Added dropdown content displaying the latest 4 posts from each category with thumbnails, titles, dates, and authors
  • Implemented "View All" links at the top of each dropdown for quick access to full category pages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 450 to 552
{/* Technology Collapsible */}
<Collapsible open={mobileTechOpen} onOpenChange={setMobileTechOpen}>
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
<span className="font-semibold text-[15px] text-black/90">Technology</span>
<ChevronDown className={`w-4 h-4 text-neutral-500 transition-transform duration-200 ${mobileTechOpen ? 'rotate-180':''}`} />
</CollapsibleTrigger>
<CollapsibleContent className="mt-2.5 space-y-2">
<div className="space-y-2 border-l-2 border-neutral-200/40 pl-4 ml-2">
{/* View All Technology Link */}
<Link
href="/technology"
onClick={()=>setMobileMenuOpen(false)}
className="flex items-center justify-between w-full px-4 py-3 rounded-xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[48px]"
>
<span className="font-medium text-sm text-black/75">View All Technology Posts</span>
<ChevronRight className="w-3.5 h-3.5 text-neutral-400" />
</Link>
{/* Latest Technology Posts */}
{(techState.length ? techState.slice(0,4) : new Array(4).fill(null)).map((edge, i) => {
if (!edge) {
return (
<div key={`tech-mobile-skel-${i}`} className="rounded-xl overflow-hidden ring-1 ring-neutral-200/60 bg-white/60 animate-pulse p-3 space-y-2 min-h-[60px]">
<div className="h-3 w-3/4 bg-white/60 rounded" />
<div className="h-2 w-2/3 bg-white/50 rounded" />
</div>
);
}
const { node } = edge;
return (
<Link
key={node.slug}
href={`/technology/${node.slug}`}
onClick={()=>setMobileMenuOpen(false)}
className="flex items-center gap-3 w-full px-4 py-3 rounded-xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[60px]"
>
<div className="relative w-16 h-12 flex-shrink-0 rounded-md overflow-hidden bg-white/40">
{node?.featuredImage?.node?.sourceUrl && (
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
)}
</div>
<div className="flex-1 min-w-0">
<h4 className="text-[13px] font-semibold text-neutral-900 line-clamp-2">{node.title}</h4>
<p className="text-[11px] text-neutral-600 mt-0.5">{new Date(node.date).toLocaleDateString()} • {node.ppmaAuthorName || "Keploy"}</p>
</div>
<ChevronRight className="w-3.5 h-3.5 text-neutral-400 flex-shrink-0" />
</Link>
);
})}
</div>
</CollapsibleContent>
</Collapsible>

{/* Community Link */}
<Link
href="/community"
onClick={()=>setMobileMenuOpen(false)}
className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]"
>
<span className="font-semibold text-[15px] text-black/90">Community</span>
<ChevronRight className="w-4 h-4 text-neutral-500" />
</Link>
{/* Community Collapsible */}
<Collapsible open={mobileCommunityOpen} onOpenChange={setMobileCommunityOpen}>
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
<span className="font-semibold text-[15px] text-black/90">Community</span>
<ChevronDown className={`w-4 h-4 text-neutral-500 transition-transform duration-200 ${mobileCommunityOpen ? 'rotate-180':''}`} />
</CollapsibleTrigger>
<CollapsibleContent className="mt-2.5 space-y-2">
<div className="space-y-2 border-l-2 border-neutral-200/40 pl-4 ml-2">
{/* View All Community Link */}
<Link
href="/community"
onClick={()=>setMobileMenuOpen(false)}
className="flex items-center justify-between w-full px-4 py-3 rounded-xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[48px]"
>
<span className="font-medium text-sm text-black/75">View All Community Posts</span>
<ChevronRight className="w-3.5 h-3.5 text-neutral-400" />
</Link>
{/* Latest Community Posts */}
{(communityState.length ? communityState.slice(0,4) : new Array(4).fill(null)).map((edge, i) => {
if (!edge) {
return (
<div key={`comm-mobile-skel-${i}`} className="rounded-xl overflow-hidden ring-1 ring-neutral-200/60 bg-white/60 animate-pulse p-3 space-y-2 min-h-[60px]">
<div className="h-3 w-3/4 bg-white/60 rounded" />
<div className="h-2 w-2/3 bg-white/50 rounded" />
</div>
);
}
const { node } = edge;
return (
<Link
key={node.slug}
href={`/community/${node.slug}`}
onClick={()=>setMobileMenuOpen(false)}
className="flex items-center gap-3 w-full px-4 py-3 rounded-xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[60px]"
>
<div className="relative w-16 h-12 flex-shrink-0 rounded-md overflow-hidden bg-white/40">
{node?.featuredImage?.node?.sourceUrl && (
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
)}
</div>
<div className="flex-1 min-w-0">
<h4 className="text-[13px] font-semibold text-neutral-900 line-clamp-2">{node.title}</h4>
<p className="text-[11px] text-neutral-600 mt-0.5">{new Date(node.date).toLocaleDateString()} • {node.ppmaAuthorName || "Keploy"}</p>
</div>
<ChevronRight className="w-3.5 h-3.5 text-neutral-400 flex-shrink-0" />
</Link>
);
})}
</div>
</CollapsibleContent>
</Collapsible>
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The Technology and Community collapsible sections contain nearly identical code with only minor differences (category name, state variables, and URLs). This duplication violates the DRY principle and makes maintenance harder. Consider extracting this logic into a reusable component or function that accepts parameters like categoryName, categoryState, mobileOpen state setter, and urlPrefix. This would reduce the code from ~100 lines to a single reusable component called twice with different props.

Copilot uses AI. Check for mistakes.
Comment on lines 452 to 456
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
<span className="font-semibold text-[15px] text-black/90">Technology</span>
<ChevronDown className={`w-4 h-4 text-neutral-500 transition-transform duration-200 ${mobileTechOpen ? 'rotate-180':''}`} />
</CollapsibleTrigger>
<CollapsibleContent className="mt-2.5 space-y-2">
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The CollapsibleTrigger elements lack proper ARIA attributes for accessibility. Consider adding aria-label or aria-labelledby attributes to provide clear context for screen reader users about what these buttons control. For example, aria-label="Toggle Technology posts dropdown" would help users understand the button's purpose.

Suggested change
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
<span className="font-semibold text-[15px] text-black/90">Technology</span>
<ChevronDown className={`w-4 h-4 text-neutral-500 transition-transform duration-200 ${mobileTechOpen ? 'rotate-180':''}`} />
</CollapsibleTrigger>
<CollapsibleContent className="mt-2.5 space-y-2">
<CollapsibleTrigger
className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]"
aria-label="Toggle Technology posts dropdown"
aria-controls="mobile-technology-posts-panel"
>
<span className="font-semibold text-[15px] text-black/90">Technology</span>
<ChevronDown className={`w-4 h-4 text-neutral-500 transition-transform duration-200 ${mobileTechOpen ? 'rotate-180':''}`} />
</CollapsibleTrigger>
<CollapsibleContent id="mobile-technology-posts-panel" className="mt-2.5 space-y-2">

Copilot uses AI. Check for mistakes.
</Link>
{/* Community Collapsible */}
<Collapsible open={mobileCommunityOpen} onOpenChange={setMobileCommunityOpen}>
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The CollapsibleTrigger element lacks proper ARIA attributes for accessibility. Consider adding aria-label or aria-labelledby attributes to provide clear context for screen reader users about what this button controls. For example, aria-label="Toggle Community posts dropdown" would help users understand the button's purpose.

Suggested change
<CollapsibleTrigger className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]">
<CollapsibleTrigger
className="flex items-center justify-between w-full px-5 py-3.5 rounded-2xl bg-white/60 ring-1 ring-neutral-200/50 hover:bg-white/80 hover:ring-orange-400/60 transition-all duration-200 shadow-sm hover:shadow-md min-h-[52px]"
aria-label="Toggle Community posts dropdown"
>

Copilot uses AI. Check for mistakes.
>
<div className="relative w-16 h-12 flex-shrink-0 rounded-md overflow-hidden bg-white/40">
{node?.featuredImage?.node?.sourceUrl && (
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The Image component's alt attribute uses node.title directly, which may not always be appropriate for accessibility. If node.title is empty or undefined, this would result in an empty alt attribute. Consider providing a fallback or more descriptive alt text, such as: alt={node.title || 'Technology post featured image'} to ensure meaningful alternative text is always provided.

Suggested change
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
<Image src={node.featuredImage.node.sourceUrl} alt={node?.title || "Technology post featured image"} fill className="object-cover" />

Copilot uses AI. Check for mistakes.
>
<div className="relative w-16 h-12 flex-shrink-0 rounded-md overflow-hidden bg-white/40">
{node?.featuredImage?.node?.sourceUrl && (
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The Image component's alt attribute uses node.title directly, which may not always be appropriate for accessibility. If node.title is empty or undefined, this would result in an empty alt attribute. Consider providing a fallback or more descriptive alt text, such as: alt={node.title || 'Community post featured image'} to ensure meaningful alternative text is always provided.

Suggested change
<Image src={node.featuredImage.node.sourceUrl} alt={node.title} fill className="object-cover" />
<Image src={node.featuredImage.node.sourceUrl} alt={node.title || "Community post featured image"} fill className="object-cover" />

Copilot uses AI. Check for mistakes.
@remo-lab
Copy link
Contributor Author

Hi @amaan-bhati 👋,
Could you please take a look at this PR when you get a chance?
It fixes the mobile navbar issue by making Technology and Community behave like Resources, with dropdowns showing the latest posts and a "View All" link.

I’d appreciate your feedback on the implementation and styling to ensure consistency across all sections. Thanks in advance for your review! 🙏

remo-lab added 2 commits December 30, 2025 18:49
Signed-off-by: remo-lab <remo@DESKTOP-V3SPST8.localdomain>
Signed-off-by: remo-lab <remo@DESKTOP-V3SPST8.localdomain>
@remo-lab remo-lab force-pushed the fix/mobile-navbar-dropdowns branch from ca34e30 to d7915bf Compare December 30, 2025 13:22
@remo-lab
Copy link
Contributor Author

@amaan-bhati
I’ve implemented accessibility improvements and a DRY refactor for the mobile navbar dropdowns.

Key updates:

  • Fallback alt text for all Image components to ensure meaningful descriptions.
  • aria-labels added to CollapsibleTrigger elements for better screen reader support.
  • Created a reusable MobileCategoryDropdown component to replace duplicated code (~100 lines).

This resolves accessibility concerns, eliminates duplication, and keeps functionality consistent across Technology, Community, and Resources.
Whenever you have time, I’d appreciate a review to confirm the changes align with our standards and design expectations.

@amaan-bhati amaan-bhati self-requested a review December 30, 2025 19:30
Copy link
Member

@amaan-bhati amaan-bhati left a comment

Choose a reason for hiding this comment

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

Hey @remo-lab Thanks for raising this pr, i have reviewed it on local, and the issue is resolved. Before i merge it, can you please take a look at the copilot comments and suggestions?

  • Tested on local for two dimensions of mobile devices:

Image
Image
  • Ran npm run build, nothing breaks:

Image

@remo-lab
Copy link
Contributor Author

Hi @amaan-bhati , I’ve already implemented the changes suggested by Copilot. Please let me know if there’s anything else I should update before you proceed with the merge. Thanks again for your review!

@amaan-bhati amaan-bhati self-requested a review January 6, 2026 22:59
Copy link
Member

@amaan-bhati amaan-bhati left a comment

Choose a reason for hiding this comment

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

LGTM!

@amaan-bhati amaan-bhati merged commit 5f8263e into keploy:main Jan 6, 2026
1 check passed
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.

2 participants