Skip to content

Conversation

@praveenzsp
Copy link
Contributor

@praveenzsp praveenzsp commented Dec 3, 2025

Fixes #232

  1. Needed a shared, reusable card to represent sponsors/companies on both the landing page and dashboard, while staying consistent with the existing design system.
  2. Encapsulates the navigation behaviour and styling so consumers only need to pass content and links.

Implementation Details

  • Built on the existing Card primitive from apps/web/src/components/ui/card.tsx.
  • Added focus-visible ring and hover border transitions to match the rest of the app’s interactive elements.
  • Ensured type-safe props with SponsorCardProps and sensible defaults.

Manually added cards to landing page to display them

sponsorcard.mp4

Summary by CodeRabbit

  • New Features
    • Added a Sponsor Card component that displays sponsor logos, names and descriptions as a clickable card. Includes hover/focus styling and an option to open sponsor links in a new tab for improved navigation.

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

@vercel
Copy link

vercel bot commented Dec 3, 2025

@praveenzsp is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

A new SponsorCard React component was added at apps/web/src/components/ui/SponsorCard.tsx. It renders a linked card showing a sponsor logo, name, and description, accepts props for content and behavior, uses Next.js Image and composes with an existing Card component.

Changes

Cohort / File(s) Summary
New Sponsor Card Component
apps/web/src/components/ui/SponsorCard.tsx
Adds SponsorCard component with props: name, description, href, logoSrc, optional logoAlt, optional className, and optional openInNewTab (default true). Wraps content in an anchor, composes existing Card, and uses Next.js Image with hover/focus styling and layout classes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5–10 minutes

  • Verify prop/interface correctness and default for openInNewTab
  • Confirm Next.js Image usage (sizes, alt, optimization)
  • Review anchor attributes (target/rel) and keyboard/focus behavior
  • Check CSS/Tailwind classes for responsive layout and accessibility

Poem

🐰 A tiny card hops into place,
Logo tucked in a cozy space,
Click and it opens — perhaps a new tab,
Gentle hover, a subtle tad,
Sponsor cheer in a ribboned trace.

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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: creating a new SponsorCard component for featured projects, matching the primary objective.
Linked Issues check ✅ Passed The PR successfully implements a card component for featured projects as required by issue #232, with proper styling, type-safe props, and design system integration.
Out of Scope Changes check ✅ Passed The PR focuses solely on creating the SponsorCard component; all changes are directly related to the linked issue requirement with no unrelated modifications.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9581e0e and c87b9ba.

📒 Files selected for processing (1)
  • apps/web/src/components/ui/SponsorCard.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/ui/SponsorCard.tsx

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.

@praveenzsp praveenzsp changed the title created SponsorCard component for featured projects Created SponsorCard component for featured projects Dec 3, 2025
@praveenzsp
Copy link
Contributor Author

Hey @apsinghdev & @huamanraj created a basic card component by referring the draft u provided. lemme know if any changes are needed

Copy link
Contributor

@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: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cdc72d and 9581e0e.

📒 Files selected for processing (1)
  • apps/web/src/components/ui/SponsorCard.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Use PascalCase for component file names (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/components/ui/SponsorCard.tsx
🧠 Learnings (5)
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Include proper aria labels for accessibility

Applied to files:

  • apps/web/src/components/ui/SponsorCard.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts} : Use descriptive prop names and define prop types using TypeScript interfaces or types

Applied to files:

  • apps/web/src/components/ui/SponsorCard.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Provide alt text for images

Applied to files:

  • apps/web/src/components/ui/SponsorCard.tsx
📚 Learning: 2025-11-25T07:34:30.473Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T07:34:30.473Z
Learning: Applies to apps/web/src/**/*.{tsx,ts} : Optimize images using next/image

Applied to files:

  • apps/web/src/components/ui/SponsorCard.tsx
📚 Learning: 2025-11-25T07:35:19.071Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/web/.cursor/rules/frontend_rules.mdc:0-0
Timestamp: 2025-11-25T07:35:19.071Z
Learning: Applies to apps/web/src/components/**/*.{tsx,ts,jsx,js} : Always follow the design system defined in `apps/web/src/lib/design-tokens.ts` and `apps/web/tailwind.config.ts`

Applied to files:

  • apps/web/src/components/ui/SponsorCard.tsx
🧬 Code graph analysis (1)
apps/web/src/components/ui/SponsorCard.tsx (1)
apps/web/src/lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (1)
apps/web/src/components/ui/SponsorCard.tsx (1)

6-14: props shape and accessibility defaults look solid

props are clearly typed, use descriptive names, and the optional logoAlt with sensible fallback plus openInNewTab default cover common cases without overcomplicating the api. this aligns well with the repo guidelines on prop typing and accessibility.

As per coding guidelines, ...

Also applies to: 21-24

Comment on lines 1 to 4
import React from "react";
import { cn } from "@/lib/utils";
import Image from "next/image";
import { Card } from "./card";
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

align imports and component definition with project conventions

  • reorder imports to follow the documented order: react → third‑party → local components → utils. here that would mean next/image first, then ./card, then @/lib/utils.
  • the react default import is unused with the current jsx setup; it can be removed.
  • guidelines prefer const + arrow for components; consider:
-import React from "react";
-import { cn } from "@/lib/utils";
-import Image from "next/image";
-import { Card } from "./card";
+import Image from "next/image";
+import { Card } from "./card";
+import { cn } from "@/lib/utils";
@@
-function SponsorCard({
-  name,
-  description,
-  href,
-  logoSrc,
-  logoAlt,
-  className,
-  openInNewTab = true,
-}: SponsorCardProps) {
+const SponsorCard = ({
+  name,
+  description,
+  href,
+  logoSrc,
+  logoAlt,
+  className,
+  openInNewTab = true,
+}: SponsorCardProps) => {
@@
-}
+};

As per coding guidelines, ...

Also applies to: 16-24

🤖 Prompt for AI Agents
In apps/web/src/components/ui/SponsorCard.tsx around lines 1 to 4 (and component
definition at lines 16 to 24), the imports and component style don't follow
project conventions: remove the unused default React import, reorder imports to
follow react → third‑party → local components → utils (so place next/image
first, then ./card, then @/lib/utils), and refactor the component to use a const
arrow function (e.g., const SponsorCard: React.FC<Props> = (...) => {...})
instead of a function declaration; update any export accordingly.

Comment on lines 34 to 37
className={cn(
"flex h-full flex-col overflow-hidden border-[#252525] bg-[#111111] text-sm text-muted-foreground/90 [box-shadow:0_0_60px_-24px_#14111C_inset]",
"transition-colors hover:border-[#3a3a3a]",
className
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

replace hardcoded hex colors and box‑shadow with design‑token tailwind classes

the classes border-[#252525], bg-[#111111], bg-[#050505], hover:border-[#3a3a3a], and [box-shadow:0_0_60px_-24px_#14111C_inset] violate the rule to never use raw hex values in components. please swap these for semantic tailwind classes wired to your design tokens (e.g., appropriate bg-*, border-*, and shadow-* variants defined in design-tokens.ts / tailwind.config.ts).

As per coding guidelines, ...

Also applies to: 40-41, 51-51

🤖 Prompt for AI Agents
In apps/web/src/components/ui/SponsorCard.tsx around lines 34-37 (also apply
same changes at 40-41 and 51), replace the hardcoded hex utility classes
(border-[#252525], bg-[#111111], bg-[#050505], hover:border-[#3a3a3a],
[box-shadow:0_0_60px_-24px_#14111C_inset]) with the corresponding semantic
Tailwind design-token classes defined in design-tokens.ts / tailwind.config.ts
(use the configured bg-*, border-*, hover:border-*, and shadow-* tokens
instead). Update the cn(...) call to remove raw hex and inline box-shadow and
substitute the appropriate token names, ensuring consistency with other
components and running the Tailwind build to verify class names exist.

Comment on lines 41 to 48
<Image
src={logoSrc}
alt={logoAlt ?? `${name} logo`}
fill
className="object-contain"
unoptimized
priority
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🌐 Web query:

next/image unoptimized priority props best practices reusable components

💡 Result:

Here are concise best practices for using next/image’s unoptimized and priority props and for building reusable image components.

Key rules

  • Use next/image (default) for automatic responsive resizing, modern formats, caching and to avoid layout shift — only use unoptimized when you deliberately want to bypass Next’s optimizer (e.g., images already served by a CDN, hitting Vercel optimization quotas, or to preserve exact source pixels). [1][4]
  • Add priority only to the single LCP / above‑the‑fold image per page. Do NOT set priority on many images — it disables lazy loading and increases bandwidth/processing cost. Use priority sparingly. [1]
  • Always supply width+height (or use fill with a container that has explicit size) and good alt text to avoid CLS and for accessibility. [1]

Server/process recommendations

  • Install sharp in production to speed image processing (reduces server-side latency for optimized images). Test that your deployment’s image optimizer is behaving as expected. [2]
  • If you use Vercel’s image optimization and hit quotas or want zero server processing, set images.unoptimized or use Image unoptimized prop—but expect larger payloads and loss of responsive format delivery. Monitor for broken images if quota limits are reached. [4]

Reusable Image component patterns

  • Component API (minimal, safe defaults):
    • props: src, alt, width?, height?, sizes?, className?, priority?: boolean (default false), unoptimized?: boolean (default false), quality?
    • Internally compute sizes based on layout/responsive breakpoints; map a boolean isLCP or page prop to set priority.
  • Defaults and guardrails:
    • Default priority = false. Only expose a semantic prop like isLCP or critical that sets priority internally so callers don’t overuse it.
    • Default unoptimized = false; expose forceUnoptimized only when intentionally bypassing optimization.
    • Validate/normalize src (reject empty/invalid), and provide a fallback src and onError handler to avoid infinite retries/ broken images.
  • Performance helpers:
    • Provide sensible sizes and srcSet through sizes prop; compute srcSets for common breakpoints.
    • Lazy-load non-critical images (omit priority) so they use native lazy loading.
    • Memoize component or wrap in React.memo where props are stable to avoid re-renders.
    • If many images are processed server-side, consider pre-generating/resizing at build time or use a CDN that serves pre-optimized variants to reduce runtime processing.
  • Accessibility & UX:
    • Require alt, support aria-* where needed, offer low‑quality placeholder (blurDataURL) for perceived performance.

Example (conceptual)

  • export default function AppImage({src, alt, width, height, isLCP=false, forceUnoptimized=false, ...}) {
    return {alt}
    }

When to use unoptimized vs priority (summary)

  • Use priority for the single LCP image to improve LCP. [1]
  • Use unoptimized only when you must bypass Next’s optimizer (CDN already optimized, Vercel quota issues, or need exact source bytes). Be aware of larger payloads and potential quality/format differences. [1][4]

References

  • Next.js Image docs (priority, sizing, best practices). [1]
  • Recommendation to install sharp for production image processing speed. [2]
  • General image optimization and tradeoffs (guidance on when to avoid unoptimized). [3]
  • Reports/experience about Vercel optimization quotas and broken images when quota reached. [4]

If you want, I can: (A) sketch a production-ready reusable Image component (TS + prop types + sizes logic), or (B) review your current Image component and suggest concrete fixes. Which do you prefer?


🏁 Script executed:

# Search for SponsorCard usage to understand the impact of priority attribute
rg "SponsorCard" --type tsx --type ts -l

Repository: apsinghdev/opensox

Length of output: 180


Expose image optimization as opt-in props with safe defaults for reusable components

The unoptimized prop disables next/image optimization entirely, and priority should only be used for a single above-the-fold image per page—setting it by default on a reusable card component will result in multiple priority images across pages, defeating lazy loading and increasing bandwidth.

For a generic UI component, remove both props or expose them as opt-in props with semantic naming and safe defaults:

  • Replace priority with an isLCP?: boolean prop (default false) so callers explicitly mark only the critical image per page
  • Replace unoptimized with a forceUnoptimized?: boolean prop (default false) and expose it only when intentionally bypassing optimization

This ensures the component respects next/image best practices by default while allowing callers to opt in when needed.

🤖 Prompt for AI Agents
In apps/web/src/components/ui/SponsorCard.tsx around lines 41 to 48, the Image
currently hardcodes unoptimized and priority which breaks next/image
best-practices for reusable components; add two optional props to the component
signature isLCP?: boolean = false and forceUnoptimized?: boolean = false, remove
the hardcoded unoptimized and priority attributes from the Image tag, and
instead pass unoptimized={forceUnoptimized} and priority={isLCP} so callers must
opt in to bypass optimization or mark the image as LCP; also update the
component's props type/interface and default values accordingly and adjust any
callers if they need the previous behavior.

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.

[UI] design a card component for featured projects

1 participant