feat: implement hero section, navbar and footer#30
feat: implement hero section, navbar and footer#30QwertyMD wants to merge 4 commits intoBIC-DevSphere:developfrom
Conversation
QwertyMD
commented
Mar 4, 2026
- Added InfiniteGallery component with scrolling functionality and example images.
- Created GalleryItem component for individual gallery items with rotation effect.
- Introduced UserFooter component with social and quick links.
- Updated UserLayout to include UserFooter.
- Enhanced UserNavbar with mobile menu and navigation links.
- Created reusable components for mobile menu, navigation links, and social links.
- Added styling for gallery and user components in respective CSS files.
There was a problem hiding this comment.
Pull request overview
This PR builds out the user-facing landing experience by adding a hero section with decorative assets, an infinite-scrolling gallery, and a complete user navbar/footer layout.
Changes:
- Implemented a new
UserHomehero section and added anInfiniteGallery+GalleryItempolaroid-style scrolling gallery. - Replaced placeholder user navbar with desktop links + social icons and a mobile hamburger menu (factored into reusable subcomponents).
- Added a
UserFooterand wired it intoUserLayout.
Reviewed changes
Copilot reviewed 12 out of 14 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/user/UserHome.tsx | Adds hero section and renders the new infinite gallery. |
| src/components/gallery/InfiniteGallery.tsx | Adds horizontally scrolling gallery track with duplicated image items. |
| src/components/gallery/GalleryItem.tsx | Adds polaroid-style gallery item with “pin” element and rotation. |
| src/components/gallery/gallery.css | Adds gallery animation and styling for the gallery components. |
| src/components/user/UserNavbar.tsx | Implements the navbar layout with desktop + mobile variants. |
| src/components/user/navbar/navbarData.tsx | Centralizes nav/social link metadata. |
| src/components/user/navbar/NavLinks.tsx | Renders nav links using react-router-dom NavLink. |
| src/components/user/navbar/SocialLinks.tsx | Renders social icon links. |
| src/components/user/navbar/MobileMenu.tsx | Adds mobile dropdown menu with nav + social links. |
| src/components/user/UserFooter.tsx | Adds footer with social links, quick links, and legal links. |
| src/components/user/UserLayout.tsx | Adds UserFooter to the user layout. |
| src/App.css | Introduces extra global CSS used by the hero/gallery. |
| public/rocket.svg | Adds rocket SVG used in the hero section. |
| package-lock.json | Lockfile updates (peer flags removed for multiple deps). |
Comments suppressed due to low confidence (3)
src/components/user/navbar/NavLinks.tsx:15
- Using the array index as the React
keycan cause unnecessary remounts if the link list ever changes order. Sincetois unique per nav item, prefer usingto(orlabel) as the key.
{navLinks.map(({ to, label, icon }, idx) => (
<NavLink
key={idx}
to={to}
onClick={onClick}
src/components/user/navbar/SocialLinks.tsx:13
- Using the array index as the React
keycan cause unnecessary remounts if the list changes. Sincehrefis unique per item, prefer usinghref(orlabel) as the key.
<div className={`flex items-center gap-4 ${className}`}>
{socialLinks.map(({ href, icon, label }, idx) => (
<a
key={idx}
href={href}
src/components/gallery/GalleryItem.tsx:2
- Both
InfiniteGalleryandGalleryItemimport./gallery.css, which can lead to duplicated CSS injection and makes style ordering harder to reason about. Prefer importing the stylesheet once at the gallery entry component level.
import './gallery.css';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <img | ||
| className="absolute -top-10 -left-20 w-24 md:-top-12 md:-left-36 md:w-40" | ||
| src="rocket.svg" | ||
| alt="rocket" | ||
| /> | ||
| <div className="mb-4 text-5xl font-bold md:text-7xl"> | ||
| <span className="text-red-500">Learn.</span> | ||
| <span className="bg-red-700 px-4 text-white line-through">Code</span> | ||
| </div> | ||
| <div className="text-5xl font-bold md:text-7xl"> | ||
| <span className="text-black">Grow. </span> | ||
| <span className="text-red-500">Together</span> | ||
| </div> | ||
| <img | ||
| className="absolute -right-20 -bottom-10 w-24 md:-right-36 md:-bottom-12 md:w-40" | ||
| src="rocket.svg" | ||
| alt="rocket" |
There was a problem hiding this comment.
src="rocket.svg" will resolve relative to the current route (e.g. /events/rocket.svg) and can 404 in production. Since the SVG is in /public, use an absolute public path (e.g. /rocket.svg) or import the asset and reference it via the bundler.
src/pages/user/UserHome.tsx
Outdated
| @@ -1,8 +1,36 @@ | |||
| import '@/App.css'; | |||
There was a problem hiding this comment.
Importing @/App.css inside UserHome makes these styles load only when this route is rendered, and the file contains very generic selectors (e.g. .pin, .gallery-item) that can unexpectedly affect other components depending on CSS load order. Consider moving truly-global styles to src/index.css, and/or scoping these styles with component-specific class prefixes / CSS modules.
| import '@/App.css'; |
src/components/gallery/gallery.css
Outdated
| transform: translateX(-50%); | ||
| transform: translateY(20%); |
There was a problem hiding this comment.
In .pin, transform is declared twice; the second declaration (translateY(20%)) overrides the first (translateX(-50%)), so the pin won’t be horizontally centered as intended. Combine the transforms into a single transform value.
| transform: translateX(-50%); | |
| transform: translateY(20%); | |
| transform: translateX(-50%) translateY(20%); |
| const GalleryItem: React.FC<GalleryItemProps> = ({ imageSrc, altText, rotation = 0 }) => { | ||
| return ( | ||
| <div | ||
| className="gallery-item mx-8 flex-shrink-0" |
There was a problem hiding this comment.
gallery.css defines .gallery-item { margin: 0 1rem; }, but this component also applies Tailwind margin via mx-8. Because both are single-class selectors, whichever CSS loads last wins, making spacing unpredictable. Consider owning the spacing in one place (either CSS or Tailwind) to avoid overrides.
| className="gallery-item mx-8 flex-shrink-0" | |
| className="gallery-item flex-shrink-0" |
| <footer className="text-foreground border-border w-full space-y-10 border-t px-10 py-8"> | ||
| <div className="flex flex-col gap-10 md:flex-row md:justify-between"> | ||
| {/* Left Section */} | ||
| <div className="space-y-4 md:max-w-1/5"> |
There was a problem hiding this comment.
md:max-w-1/5 is not a standard Tailwind utility (and there’s no Tailwind config file in this repo suggesting it’s customized), so it will be ignored at runtime. Use a valid utility (e.g. md:max-w-[20%], md:basis-1/5, or a predefined max-w-*) depending on the desired layout.
| <div className="space-y-4 md:max-w-1/5"> | |
| <div className="space-y-4 md:max-w-[20%]"> |
src/components/user/UserFooter.tsx
Outdated
| { to: '/', label: 'Members' }, | ||
| { to: '/', label: 'Events' }, | ||
| { to: '/', label: 'Projects' }, |
There was a problem hiding this comment.
The “Quick Link” entries all route to /, but the app’s user routes include /members, /events, and /projects. If these are meant to be functional navigation links, update the to values to match the actual routes (or make it explicit these are placeholders).
| { to: '/', label: 'Members' }, | |
| { to: '/', label: 'Events' }, | |
| { to: '/', label: 'Projects' }, | |
| { to: '/members', label: 'Members' }, | |
| { to: '/events', label: 'Events' }, | |
| { to: '/projects', label: 'Projects' }, |
src/App.css
Outdated
| .pin { | ||
| width: 1rem; | ||
| height: 1rem; | ||
| background-color: #ef4444; | ||
| border-radius: 50%; | ||
| border: 2px solid white; | ||
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | ||
| position: relative; | ||
| z-index: 10; | ||
| } | ||
|
|
||
| .infinite-gallery { | ||
| width: 100vw; | ||
| height: auto; | ||
| display: flex; | ||
| gap: 2rem; | ||
| overflow-x: auto; | ||
| scroll-behavior: smooth; | ||
| padding: 2rem 2rem; | ||
| cursor: grab; | ||
| user-select: none; | ||
| scrollbar-width: none; | ||
| } | ||
|
|
||
| .infinite-gallery::-webkit-scrollbar { | ||
| display: none; | ||
| } | ||
|
|
||
| .infinite-gallery:active { | ||
| cursor: grabbing; | ||
| } | ||
|
|
||
| .gallery-item { | ||
| flex-shrink: 0; | ||
| min-width: fit-content; | ||
| } |
There was a problem hiding this comment.
This file defines .pin and .gallery-item, which are also defined in src/components/gallery/gallery.css with different semantics (circle vs vertical pin). Because App.css and gallery.css are imported from different components, the effective styling can change based on render/import order. Rename/scope these selectors (e.g. .gallery-pin, .hero-grid-bg) or convert to CSS modules to avoid cross-component collisions.
src/App.css
Outdated
|
|
||
| .pinned-line { | ||
| position: relative; | ||
| width: 100vw; | ||
| height: 2rem; | ||
| display: flex; | ||
| align-items: center; | ||
| } | ||
|
|
||
| .pinned-line::before { | ||
| content: ''; | ||
| position: absolute; | ||
| width: 100%; | ||
| height: 0.25rem; | ||
| background-color: black; | ||
| } | ||
|
|
||
| .pin { | ||
| width: 1rem; | ||
| height: 1rem; | ||
| background-color: #ef4444; | ||
| border-radius: 50%; | ||
| border: 2px solid white; | ||
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | ||
| position: relative; | ||
| z-index: 10; | ||
| } | ||
|
|
||
| .infinite-gallery { | ||
| width: 100vw; | ||
| height: auto; | ||
| display: flex; | ||
| gap: 2rem; | ||
| overflow-x: auto; | ||
| scroll-behavior: smooth; | ||
| padding: 2rem 2rem; | ||
| cursor: grab; | ||
| user-select: none; | ||
| scrollbar-width: none; | ||
| } | ||
|
|
||
| .infinite-gallery::-webkit-scrollbar { | ||
| display: none; | ||
| } | ||
|
|
||
| .infinite-gallery:active { | ||
| cursor: grabbing; | ||
| } | ||
|
|
||
| .gallery-item { | ||
| flex-shrink: 0; | ||
| min-width: fit-content; | ||
| } |
There was a problem hiding this comment.
Several selectors here appear unused by the current gallery implementation (.pinned-line, .infinite-gallery, and the .pin/.gallery-item rules conflict with the gallery-specific CSS). Keeping dead/overlapping styles in a global stylesheet will make future styling changes brittle; consider deleting unused rules or relocating them into the owning component stylesheet.
| .pinned-line { | |
| position: relative; | |
| width: 100vw; | |
| height: 2rem; | |
| display: flex; | |
| align-items: center; | |
| } | |
| .pinned-line::before { | |
| content: ''; | |
| position: absolute; | |
| width: 100%; | |
| height: 0.25rem; | |
| background-color: black; | |
| } | |
| .pin { | |
| width: 1rem; | |
| height: 1rem; | |
| background-color: #ef4444; | |
| border-radius: 50%; | |
| border: 2px solid white; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| position: relative; | |
| z-index: 10; | |
| } | |
| .infinite-gallery { | |
| width: 100vw; | |
| height: auto; | |
| display: flex; | |
| gap: 2rem; | |
| overflow-x: auto; | |
| scroll-behavior: smooth; | |
| padding: 2rem 2rem; | |
| cursor: grab; | |
| user-select: none; | |
| scrollbar-width: none; | |
| } | |
| .infinite-gallery::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .infinite-gallery:active { | |
| cursor: grabbing; | |
| } | |
| .gallery-item { | |
| flex-shrink: 0; | |
| min-width: fit-content; | |
| } |
| {items.map((src, index) => ( | ||
| <GalleryItem | ||
| key={index} | ||
| imageSrc={src} | ||
| altText={`Gallery image ${index}`} | ||
| rotation={(index % 2 === 0 ? 2 : -2) + (Math.random() * 2 - 1)} | ||
| /> |
There was a problem hiding this comment.
rotation uses Math.random() during render. Any re-render (theme change, parent state update, StrictMode dev re-render, etc.) will cause images to “jump” to new rotations. Precompute a stable rotation list (e.g. via useMemo seeded by src/index) so the layout stays deterministic.
| const socialLinks = [ | ||
| { | ||
| href: 'https://discord.com/', | ||
| icon: <FaDiscord className="text-blue-600" />, | ||
| label: 'Discord', | ||
| }, | ||
| { | ||
| href: 'https://instagram.com/', | ||
| icon: <FaInstagram className="text-red-400" />, | ||
| label: 'Instagram', | ||
| }, | ||
| { | ||
| href: 'https://linkedin.com/', | ||
| icon: <FaLinkedin className="text-sky-600" />, | ||
| label: 'LinkedIn', | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
socialLinks is duplicated here and in src/components/user/navbar/navbarData.tsx with the same destinations/labels. Duplicating link metadata tends to drift over time; consider exporting a shared socialLinks (and optionally quickLinks) source and reusing it in both navbar and footer.
- Added InfiniteGallery component with scrolling functionality and example images. - Created GalleryItem component for individual gallery items with rotation effect. - Introduced UserFooter component with social and quick links. - Updated UserLayout to include UserFooter. - Enhanced UserNavbar with mobile menu and navigation links. - Created reusable components for mobile menu, navigation links, and social links. - Added styling for gallery and user components in respective CSS files.
c03bf93 to
fe4e22c
Compare