-
Notifications
You must be signed in to change notification settings - Fork 2
feat(home): add sponsors section with animated display #237
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
Add a new SponsorsSection component to showcase project sponsors with animation effects. The section includes logos, titles, and links for each sponsor, with hover animations and a scrolling display. Also updates the project name in package-lock.json.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a new client-side SponsorsSection component and integrates it into the Home page via a dynamic import with SSR disabled. The Home page wraps the section in Suspense and provides loading fallbacks. The SponsorsSection renders a scrolling gallery of sponsor tiles with optional links and hover effects. Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant N as Next.js App Router
participant H as HomePage
participant S as Suspense
participant D as Dynamic Import (SponsorsSection)
participant C as SponsorsSection
U->>N: Request Home (/)
N->>H: Render page
H->>S: Wrap SponsorsSection with fallback
S->>U: Show Suspense fallback
H->>D: Trigger dynamic import (ssr: false)
D-->>H: Load pending (client-side only)
Note over S,D: Two fallbacks exist: inline + dynamic loading skeleton
D-->>H: Module resolved
H->>C: Render SponsorsSection
S-->>U: Replace fallback with content
C-->>U: Display scrolling sponsors gallery
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Comment |
Docstrings generation was requested by @codeunia-dev. * #237 (comment) The following files were modified: * `app/page.tsx` * `components/home/SponsorsSection.tsx`
|
Note Generated docstrings for this pull request at #238 |
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.
Actionable comments posted: 3
🧹 Nitpick comments (6)
components/home/SponsorsSection.tsx (4)
94-99: Ensure animate-scroll is defined and support reduced motion.
- If the animate-scroll class/keyframes aren’t defined globally, the strip won’t move.
- Respect prefers-reduced-motion to avoid motion sickness.
If missing, add to your global CSS:
@keyframes sponsors-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } } .animate-scroll { animation: sponsors-scroll 30s linear infinite; will-change: transform; } @media (prefers-reduced-motion: reduce) { .animate-scroll { animation: none; } }
99-133: DRY up the card markup and keys.The anchor/non-anchor branches duplicate large blocks, and you render two maps with near-identical bodies. Use a single SponsorCard and map over a duplicated array. Also prefer stable keys.
Example:
+type Sponsor = { name: string; title: string; logo: string; link?: string }; + +function SponsorCard({ sponsor, duplicate }: { sponsor: Sponsor; duplicate?: boolean }) { + const Card = ( + <motion.div + className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" + whileHover={{ y: -5 }} + {...(duplicate ? { 'aria-hidden': true } : {})} + > + <div className="relative w-full h-48 flex items-center justify-center"> + <Image src={sponsor.logo} alt={sponsor.name} fill sizes="300px" className="object-contain p-4" /> + </div> + <div className="text-center"> + <h3 className="font-semibold text-lg">{sponsor.name}</h3> + <p className="text-sm text-muted-foreground">{sponsor.title}</p> + </div> + </motion.div> + ); + return sponsor.link ? ( + <a + href={sponsor.link} + target="_blank" + rel="noopener noreferrer" + className="no-underline" + {...(duplicate ? { 'aria-hidden': true } : {})} + > + {Card} + </a> + ) : Card; +} @@ - {sponsors.map((sponsor, idx) => ( - sponsor.link ? ( - <a - key={`first-${idx}`} + {[...sponsors, ...sponsors].map((sponsor, idx) => ( + <div key={`${sponsor.name}-${idx}`} className="contents"> + <SponsorCard sponsor={sponsor} duplicate={idx >= sponsors.length} /> + </div> - ) : ( - … - ) ))} - {sponsors.map((sponsor, idx) => ( - … - ))}Also applies to: 135-161, 164-199, 200-227
101-102: Prefer stable keys over array indices.Avoid idx-based keys to reduce accidental remounts;
sponsor.nameis stable and unique here.- key={`first-${idx}`} + key={`first-${sponsor.name}`} … - key={`second-${idx}`} + key={`second-${sponsor.name}`}Also applies to: 136-137, 167-168, 201-202
82-83: Nit:-bottom-0is equivalent tobottom-0.Minor cleanup.
- <span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] … + <span className="absolute bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] …app/page.tsx (2)
107-111: Redundant Suspense vs dynamicloadingfallback.Since this dynamic import doesn’t set
suspense: true, the Suspense fallback won’t apply; theloadingoption already covers the client load. Choose one:
- Remove the Suspense wrapper, or
- Enable
suspense: truein dynamic options and drop theloadingprop for consistency with other sections.-const SponsorsSection = dynamic(() => import("@/components/home/SponsorsSection").then(mod => ({ default: mod.SponsorsSection })), { - loading: () => (/* skeleton */), - ssr: false -}); +const SponsorsSection = dynamic( + () => import("@/components/home/SponsorsSection").then(mod => ({ default: mod.SponsorsSection })), + { ssr: false, suspense: true } +); @@ - <Suspense fallback={<div className="py-16"><div className="container px-4 mx-auto"><div className="animate-pulse"><div className="h-8 bg-gray-200 rounded w-1/3 mx-auto mb-4"></div><div className="h-4 bg-gray-200 rounded w-1/2 mx-auto"></div></div></div></div>}> - <SponsorsSection /> - </Suspense> + <Suspense fallback={<div className="py-16"><div className="container px-4 mx-auto"><div className="animate-pulse"><div className="h-8 bg-gray-200 rounded w-1/3 mx-auto mb-4"></div><div className="h-4 bg-gray-200 rounded w-1/2 mx-auto"></div></div></div></div>}> + <SponsorsSection /> + </Suspense>
60-74: Optional: consider enabling SSR for faster first paint of static content.If SEO or initial paint matters for this section,
ssr: trueis safe here (the component is client-safe and data-free).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
app/page.tsx(2 hunks)components/home/SponsorsSection.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/page.tsx (2)
components/home/SponsorsSection.tsx (1)
SponsorsSection(58-234)app/about/components/SponsorsSection.tsx (1)
SponsorsSection(58-269)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Suite
🔇 Additional comments (3)
components/home/SponsorsSection.tsx (2)
58-234: Overall: solid section and visuals.Nice, cohesive UI with thoughtful motion and theming. Once the a11y and image tweaks are in, this will be production-ready.
7-56: No missing sponsor images — filenames match public/images/sponsorsAll logos referenced in components/home/SponsorsSection.tsx are present in public/images/sponsors: Innovxus.png, alexadevcommunity.png, codecrafter.png, codeunia.png, geekforgeeks.png, rotaract.png, studentchaptercu.png, unstop.png, webytes.png. The earlier "MISS" results were caused by checking ./images/... instead of ./public/images/....
app/page.tsx (1)
60-74: Dynamic import of named export is correct; SSR disabled is fine for a purely visual section.Looks good as a non-critical, client-only block.
| <div className="flex flex-col items-center justify-center gap-4"> | ||
| <button className="bg-slate-800 no-underline group relative shadow-2xl shadow-zinc-900 rounded-full p-px text-sm font-semibold leading-6 text-white inline-block cursor-default"> | ||
| <span className="absolute inset-0 overflow-hidden rounded-full"> | ||
| <span className="absolute inset-0 rounded-full bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" /> | ||
| </span> | ||
| <div className="relative flex space-x-2 items-center z-10 rounded-full bg-zinc-950 py-0.5 px-4 ring-1 ring-white/10"> | ||
| <span>Our Sponsors</span> | ||
| <span> | ||
| <Handshake className="w-3 h-3" /> | ||
| </span> | ||
| </div> | ||
| <span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-emerald-400/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40" /> | ||
| </button> | ||
| </div> |
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.
🛠️ Refactor suggestion
Non-interactive “button” hurts accessibility; use a non-focusable element.
This badge is decorative and not clickable. A is focusable and announced as interactive.
Apply:
- <button className="bg-slate-800 no-underline group relative shadow-2xl shadow-zinc-900 rounded-full p-px text-sm font-semibold leading-6 text-white inline-block cursor-default">
+ <div aria-hidden="true" className="bg-slate-800 no-underline group relative shadow-2xl shadow-zinc-900 rounded-full p-px text-sm font-semibold leading-6 text-white inline-block">
…
- </button>
+ </div>📝 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.
| <div className="flex flex-col items-center justify-center gap-4"> | |
| <button className="bg-slate-800 no-underline group relative shadow-2xl shadow-zinc-900 rounded-full p-px text-sm font-semibold leading-6 text-white inline-block cursor-default"> | |
| <span className="absolute inset-0 overflow-hidden rounded-full"> | |
| <span className="absolute inset-0 rounded-full bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" /> | |
| </span> | |
| <div className="relative flex space-x-2 items-center z-10 rounded-full bg-zinc-950 py-0.5 px-4 ring-1 ring-white/10"> | |
| <span>Our Sponsors</span> | |
| <span> | |
| <Handshake className="w-3 h-3" /> | |
| </span> | |
| </div> | |
| <span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-emerald-400/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40" /> | |
| </button> | |
| </div> | |
| <div className="flex flex-col items-center justify-center gap-4"> | |
| <div aria-hidden="true" className="bg-slate-800 no-underline group relative shadow-2xl shadow-zinc-900 rounded-full p-px text-sm font-semibold leading-6 text-white inline-block"> | |
| <span className="absolute inset-0 overflow-hidden rounded-full"> | |
| <span className="absolute inset-0 rounded-full bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" /> | |
| </span> | |
| <div className="relative flex space-x-2 items-center z-10 rounded-full bg-zinc-950 py-0.5 px-4 ring-1 ring-white/10"> | |
| <span>Our Sponsors</span> | |
| <span> | |
| <Handshake className="w-3 h-3" /> | |
| </span> | |
| </div> | |
| <span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-emerald-400/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40" /> | |
| </div> | |
| </div> |
🤖 Prompt for AI Agents
In components/home/SponsorsSection.tsx around lines 71-84, the badge is
implemented as a <button> which is focusable and exposed as interactive though
it is decorative; replace the <button> with a non-interactive element (e.g.,
<div> or <span>) keeping the same className and structure, remove any
button-specific semantics, and mark it decorative with aria-hidden="true" so it
is not announced or focusable by assistive tech (ensure no tabIndex is set).
| <div className={"relative w-full h-48 flex items-center justify-center"}> | ||
| <Image | ||
| src={sponsor.logo} | ||
| alt={sponsor.name} | ||
| fill | ||
| className={ | ||
| sponsor.name === "Unstop" | ||
| ? "object-contain p-4" | ||
| : sponsor.name === "Code Crafters" | ||
| ? "object-contain w-32 h-16 mx-auto" | ||
| : sponsor.name === "Innovxus" | ||
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | ||
| : "object-cover" | ||
| } | ||
| /> |
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.
🛠️ Refactor suggestion
Next/Image: avoid mixing fill with width/height utility classes.
When using fill, width/height classes like w-32 h-16 are ignored/conflict. Also, using object-cover may crop logos.
- Use consistent
object-containand padding for all logos. - Provide
sizes="300px"to improve image selection.
Apply:
- <Image
+ <Image
src={sponsor.logo}
alt={sponsor.name}
- fill
- className={
- sponsor.name === "Unstop"
- ? "object-contain p-4"
- : sponsor.name === "Code Crafters"
- ? "object-contain w-32 h-16 mx-auto"
- : sponsor.name === "Innovxus"
- ? "object-contain w-24 h-10 mx-auto flex items-center justify-center"
- : "object-cover"
- }
+ fill
+ sizes="300px"
+ className="object-contain p-4"
/>If you need per-brand tweaks, adjust the wrapping container (add extra padding) rather than overriding Image width/height.
Also applies to: 140-154, 177-191, 205-219
🤖 Prompt for AI Agents
In components/home/SponsorsSection.tsx around lines 112-126 (also apply same
pattern at 140-154, 177-191, 205-219): the Image components use Next/Image with
the fill prop while conditionally applying width/height utility classes and
object-cover, which conflict with fill and can crop logos. Replace per-brand
width/height utilities on the Image with a consistent object-contain and
optional padding class, move any size/spacing tweaks into the wrapping div
(adjust padding/margins or set explicit width/height on the wrapper), and add
sizes="300px" to the Image props so the browser can pick an appropriate src;
ensure no width/height Tailwind classes remain on Image when using fill.
| {sponsors.map((sponsor, idx) => ( | ||
| sponsor.link ? ( | ||
| <a | ||
| key={`second-${idx}`} | ||
| href={sponsor.link} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="no-underline" | ||
| > | ||
| <motion.div | ||
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | ||
| whileHover={{ y: -5 }} | ||
| > | ||
| <div className={"relative w-full h-48 flex items-center justify-center"}> | ||
| <Image | ||
| src={sponsor.logo} | ||
| alt={sponsor.name} | ||
| fill | ||
| className={ | ||
| sponsor.name === "Unstop" | ||
| ? "object-contain p-4" | ||
| : sponsor.name === "Code Crafters" | ||
| ? "object-contain w-32 h-16 mx-auto" | ||
| : sponsor.name === "Innovxus" | ||
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | ||
| : "object-cover" | ||
| } | ||
| /> | ||
| </div> | ||
| <div className="text-center"> | ||
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | ||
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | ||
| </div> | ||
| </motion.div> | ||
| </a> | ||
| ) : ( | ||
| <motion.div | ||
| key={`second-${idx}`} | ||
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | ||
| whileHover={{ y: -5 }} | ||
| > | ||
| <div className={"relative w-full h-48 flex items-center justify-center"}> | ||
| <Image | ||
| src={sponsor.logo} | ||
| alt={sponsor.name} | ||
| fill | ||
| className={ | ||
| sponsor.name === "Unstop" | ||
| ? "object-contain p-4" | ||
| : sponsor.name === "Code Crafters" | ||
| ? "object-contain w-32 h-16 mx-auto" | ||
| : sponsor.name === "Innovxus" | ||
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | ||
| : "object-cover" | ||
| } | ||
| /> | ||
| </div> | ||
| <div className="text-center"> | ||
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | ||
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | ||
| </div> | ||
| </motion.div> | ||
| ) | ||
| ))} |
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.
🛠️ Refactor suggestion
Duplicate strip should be hidden from screen readers.
The second mapping is for continuous scrolling only; it repeats content for AT.
Apply aria-hidden on duplicate items:
- <a
+ <a
+ aria-hidden="true"
key={`second-${idx}`}
href={sponsor.link}
target="_blank"
rel="noopener noreferrer"
className="no-underline"
>
- <motion.div
+ <motion.div
+ aria-hidden="true"
key={`second-${idx}`}
className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group"
whileHover={{ y: -5 }}
>Do the same for the non-link branch within the duplicate set.
📝 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.
| {sponsors.map((sponsor, idx) => ( | |
| sponsor.link ? ( | |
| <a | |
| key={`second-${idx}`} | |
| href={sponsor.link} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="no-underline" | |
| > | |
| <motion.div | |
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | |
| whileHover={{ y: -5 }} | |
| > | |
| <div className={"relative w-full h-48 flex items-center justify-center"}> | |
| <Image | |
| src={sponsor.logo} | |
| alt={sponsor.name} | |
| fill | |
| className={ | |
| sponsor.name === "Unstop" | |
| ? "object-contain p-4" | |
| : sponsor.name === "Code Crafters" | |
| ? "object-contain w-32 h-16 mx-auto" | |
| : sponsor.name === "Innovxus" | |
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | |
| : "object-cover" | |
| } | |
| /> | |
| </div> | |
| <div className="text-center"> | |
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | |
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | |
| </div> | |
| </motion.div> | |
| </a> | |
| ) : ( | |
| <motion.div | |
| key={`second-${idx}`} | |
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | |
| whileHover={{ y: -5 }} | |
| > | |
| <div className={"relative w-full h-48 flex items-center justify-center"}> | |
| <Image | |
| src={sponsor.logo} | |
| alt={sponsor.name} | |
| fill | |
| className={ | |
| sponsor.name === "Unstop" | |
| ? "object-contain p-4" | |
| : sponsor.name === "Code Crafters" | |
| ? "object-contain w-32 h-16 mx-auto" | |
| : sponsor.name === "Innovxus" | |
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | |
| : "object-cover" | |
| } | |
| /> | |
| </div> | |
| <div className="text-center"> | |
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | |
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | |
| </div> | |
| </motion.div> | |
| ) | |
| ))} | |
| {sponsors.map((sponsor, idx) => ( | |
| sponsor.link ? ( | |
| <a | |
| aria-hidden="true" | |
| key={`second-${idx}`} | |
| href={sponsor.link} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="no-underline" | |
| > | |
| <motion.div | |
| aria-hidden="true" | |
| key={`second-${idx}`} | |
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | |
| whileHover={{ y: -5 }} | |
| > | |
| <div className={"relative w-full h-48 flex items-center justify-center"}> | |
| <Image | |
| src={sponsor.logo} | |
| alt={sponsor.name} | |
| fill | |
| className={ | |
| sponsor.name === "Unstop" | |
| ? "object-contain p-4" | |
| : sponsor.name === "Code Crafters" | |
| ? "object-contain w-32 h-16 mx-auto" | |
| : sponsor.name === "Innovxus" | |
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | |
| : "object-cover" | |
| } | |
| /> | |
| </div> | |
| <div className="text-center"> | |
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | |
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | |
| </div> | |
| </motion.div> | |
| </a> | |
| ) : ( | |
| <motion.div | |
| aria-hidden="true" | |
| key={`second-${idx}`} | |
| className="flex-shrink-0 w-[300px] h-[200px] rounded-xl border border-primary/10 bg-background/50 backdrop-blur-sm p-2 flex flex-col items-center justify-center gap-4 hover:border-primary/20 transition-all duration-300 hover:shadow-lg group" | |
| whileHover={{ y: -5 }} | |
| > | |
| <div className={"relative w-full h-48 flex items-center justify-center"}> | |
| <Image | |
| src={sponsor.logo} | |
| alt={sponsor.name} | |
| fill | |
| className={ | |
| sponsor.name === "Unstop" | |
| ? "object-contain p-4" | |
| : sponsor.name === "Code Crafters" | |
| ? "object-contain w-32 h-16 mx-auto" | |
| : sponsor.name === "Innovxus" | |
| ? "object-contain w-24 h-10 mx-auto flex items-center justify-center" | |
| : "object-cover" | |
| } | |
| /> | |
| </div> | |
| <div className="text-center"> | |
| <h3 className="font-semibold text-lg">{sponsor.name}</h3> | |
| <p className="text-sm text-muted-foreground">{sponsor.title}</p> | |
| </div> | |
| </motion.div> | |
| ) | |
| ))} |
🤖 Prompt for AI Agents
In components/home/SponsorsSection.tsx around lines 164 to 227, the duplicated
sponsor items used for continuous scrolling should be hidden from assistive
technologies; add aria-hidden="true" to the duplicate branches: set
aria-hidden="true" on the outer <a> element wrapping the duplicate linked branch
and on the root <motion.div> of the duplicate non-link branch (and if you prefer
consistency, also add aria-hidden="true" to the duplicate <motion.div> inside
the linked branch), keeping existing attributes intact.
Add a new SponsorsSection component to showcase project sponsors with animation effects. The section includes logos, titles, and links for each sponsor, with hover animations and a scrolling display. Also updates the project name in package-lock.json.
Summary by CodeRabbit
New Features
Performance
UI/UX