-
Notifications
You must be signed in to change notification settings - Fork 189
fix: enable dropdowns for Technology and Community in mobile navbar #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
366f42b to
67bf3e6
Compare
There was a problem hiding this 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.
| {/* 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> |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| <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"> |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| <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"> |
| </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]"> |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| <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" | |
| > |
| > | ||
| <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" /> |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| <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" /> |
| > | ||
| <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" /> |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| <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" /> |
|
Hi @amaan-bhati 👋, I’d appreciate your feedback on the implementation and styling to ensure consistency across all sections. Thanks in advance for your review! 🙏 |
Signed-off-by: remo-lab <remo@DESKTOP-V3SPST8.localdomain>
Signed-off-by: remo-lab <remo@DESKTOP-V3SPST8.localdomain>
ca34e30 to
d7915bf
Compare
|
@amaan-bhati Key updates:
This resolves accessibility concerns, eliminates duplication, and keeps functionality consistent across Technology, Community, and Resources. |
amaan-bhati
left a comment
There was a problem hiding this 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:
- Ran
npm run build, nothing breaks:
|
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
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
problem
On mobile devices, the navigation bar did not behave consistently across sections:
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:
Changes Made
Result
Recording.2025-12-30.175604.mp4
Testing Notes