Skip to content

Comments

feat: add motion animations to enhance website UX#101

Open
KrishSharma4406 wants to merge 1 commit intoketankauntia:masterfrom
KrishSharma4406:updates
Open

feat: add motion animations to enhance website UX#101
KrishSharma4406 wants to merge 1 commit intoketankauntia:masterfrom
KrishSharma4406:updates

Conversation

@KrishSharma4406
Copy link

@KrishSharma4406 KrishSharma4406 commented Feb 20, 2026

  • Created comprehensive animations library (lib/animations.ts)
  • Added scroll-triggered animations to all major components
  • Implemented fade-in, slide, scale, and stagger animations
  • Enhanced cards, value blocks, testimonials, FAQ, and articles
  • Added smooth header slide-down and footer animations
  • All animations respect prefers-reduced-motion for accessibility

Summary by CodeRabbit

  • New Features
    • Added smooth entrance animations throughout the site, including fade-in, scale, and slide effects triggered when content enters the viewport.
    • Introduced staggered animations for multi-element sections to enhance visual flow and hierarchy.
    • Animations automatically adapt to users' accessibility preferences for reduced motion.

- Created comprehensive animations library (lib/animations.ts)
- Added scroll-triggered animations to all major components
- Implemented fade-in, slide, scale, and stagger animations
- Enhanced cards, value blocks, testimonials, FAQ, and articles
- Added smooth header slide-down and footer animations
- All animations respect prefers-reduced-motion for accessibility
@vercel
Copy link

vercel bot commented Feb 20, 2026

@KrishSharma4406 is attempting to deploy a commit to the Ketan's Personal Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Expected 'de' | 'de-DE' | 'de-AT' | 'de-CH' | 'en' | 'en-US' | 'en-AU' | 'en-GB' | 'en-CA' | 'en-NZ' | 'en-ZA' | 'es' | 'es-AR' | 'fr' | 'fr-CA' | 'fr-CH' | 'fr-BE' | 'nl' | 'nl-BE' | 'pt-AO' | 'pt' | 'pt-BR' | 'pt-MZ' | 'pt-PT' | 'ar' | 'ast-ES' | 'ast' | 'be-BY' | 'be' | 'br-FR' | 'br' | 'ca-ES' | 'ca' | 'ca-ES-valencia' | 'ca-ES-balear' | 'da-DK' | 'da' | 'de-DE-x-simple-language' | 'el-GR' | 'el' | 'eo' | 'fa' | 'ga-IE' | 'ga' | 'gl-ES' | 'gl' | 'it' | 'ja-JP' | 'ja' | 'km-KH' | 'km' | 'ko-KR' | 'ko' | 'pl-PL' | 'pl' | 'ro-RO' | 'ro' | 'ru-RU' | 'ru' | 'sk-SK' | 'sk' | 'sl-SI' | 'sl' | 'sv' | 'ta-IN' | 'ta' | 'tl-PH' | 'tl' | 'tr' | 'uk-UA' | 'uk' | 'zh-CN' | 'zh' | 'crh-UA' | 'crh' | 'cs-CZ' | 'cs' | 'nb' | 'no' | 'nl-NL' | 'de-DE-x-simple-language-DE' | 'es-ES' | 'it-IT' | 'fa-IR' | 'sv-SE' | 'de-LU' | 'fr-FR' | 'bg-BG' | 'bg' | 'he-IL' | 'he' | 'hi-IN' | 'hi' | 'vi-VN' | 'vi' | 'th-TH' | 'th' | 'bn-BD' | 'bn', received object at "language"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

This pull request adds Framer Motion animations across the UI by introducing a centralized animation library (lib/animations.ts) and integrating reusable animation variants into 11 component files. Components now feature entrance animations such as fade-in, slide-in, and scale effects when entering the viewport, while preserving all existing functionality and component APIs.

Changes

Cohort / File(s) Summary
Animation Library
lib/animations.ts
New module exporting reusable Framer Motion variants (fadeInUp, fadeInLeft, fadeInRight, slideIn, scaleIn, staggerContainer, staggerItem) and utilities (defaultViewport config, getAccessibleVariants for reduced-motion support). Centralizes animation definitions for consistent UX across components.
Header & Hero Components
components/header.tsx, components/hero-component.tsx
Integrates entrance animations into top-level navigation and hero sections. Header nav now uses motion.nav with entrance transition; hero wraps content in staggered motion.divs to cascade animations downward.
Content Section Components
components/faq.tsx, components/latest-articles.tsx, components/testimonials.tsx, components/value-blocks.tsx
Applies fadeInUp, fadeInLeft, fadeInRight, and scaleIn animations with viewport triggers to section headers, grids, and accordion/carousel containers. Uses staggering for multi-item layouts to animate items sequentially.
Card & Item Components
components/project-card.tsx, components/organization-card.tsx
Wraps individual card/item components in motion containers with fadeInUp animation and viewport intersection detection for reveal-on-scroll behavior without modifying card structure or props.
CTA & Footer Components
components/waitlist-cta.tsx, components/Footer.tsx, components/trending-orgs-client.tsx
Adds fadeInUp and scaleIn animations with viewport triggers to form CTAs, footer grids, and organization trend sections. Animations layer on top of existing functionality without altering form logic or data flow.
Dependencies
package.json
Minor updates to support Framer Motion integration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Behold, the components now float in with grace,
Each fading, scaling, sliding into place,
The animations dance as viewports align,
Staggered and smooth, a design so fine!
With accessibility tucked safe inside,
The UI springs to life with motion and pride.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is missing required template sections (Summary, Testing, and Checklist) specified in the repository template. Restructure the description to follow the template: add a Summary section, provide Testing details, and include the required checklist with acknowledgment of each item.
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding motion animations to enhance UX across the website.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

Copy link

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

🧹 Nitpick comments (5)
components/header.tsx (2)

37-40: Inconsistent animation approach and reduced-motion gap

Every other component in this PR uses variants from lib/animations. The header instead uses bare initial/animate/transition inline props. Beyond the inconsistency, this approach has an accessibility problem: even if a MotionConfig reducedMotion="user" is present at the app root, initial={{ y: -100, opacity: 0 }} still places the element offscreen on the first paint — it will just snap to position instantly rather than smoothly, which can still disturb users with vestibular disorders. Use the shared variant (or a motion.nav-compatible named variant) so that the getAccessibleVariants utility exported from lib/animations can be applied, or at minimum start from opacity: 0 without the positional offset when reduced motion is active.

♻️ Align with the variants-based approach
+import { fadeInDown, defaultViewport } from '@/lib/animations'
 ...
 <motion.nav
-    initial={{ y: -100, opacity: 0 }}
-    animate={{ y: 0, opacity: 1 }}
-    transition={{ duration: 0.5, ease: "easeOut" }}
+    variants={fadeInDown}
+    initial="hidden"
+    animate="visible"
     data-state={menuState && 'active'}
     ...
 >

If fadeInDown doesn't exist in lib/animations, add it alongside the other variants using the same easing constants so it benefits from getAccessibleVariants.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/header.tsx` around lines 37 - 40, The header is using inline
initial/animate/transition props on motion.nav which bypasses the shared
variants and can place the element offscreen on first paint (problematic for
reduced motion); replace the inline props with a named variant (e.g., use a new
or existing fadeInDown variant) and apply it via variants on motion.nav so
getAccessibleVariants from lib/animations can be used (or add a fadeInDown
variant to lib/animations with the same easing constants and ensure
getAccessibleVariants returns the reduced-motion-safe version); remove the
y-offset from initial for reduced motion by relying on
getAccessibleVariants/MotionConfig reducedMotion handling.

11-11: Unused import: fadeInUp

fadeInUp is imported but never referenced anywhere in this component.

🧹 Remove unused import
 import { motion } from 'framer-motion'
 import { Menu, X } from 'lucide-react'
 import { Button } from "@/components/ui"
 import React from 'react'
 import { cn } from '@/lib/utils'
 import { SOCIAL_LINKS } from '@/components/footer-common'
 import { GitHubIcon,  XIcon } from '@/components/icons'
 import { ModeToggle } from './ModeToggle'
-import { fadeInUp } from '@/lib/animations'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/header.tsx` at line 11, The import fadeInUp is unused in this
component; remove the unused import statement (fadeInUp) from the top of the
file (components/header.tsx) or, if the animation was intended, apply fadeInUp
to the Header component's animation usage (e.g., where the Header/JSX root or
motion element is defined) and then keep the import—otherwise delete the import
to eliminate the unused symbol.
components/Footer.tsx (1)

8-8: Unused import: fadeInUp

fadeInUp is imported but never used — only staggerContainer, staggerItem, and defaultViewport are consumed.

🧹 Remove unused import
-import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";
+import { staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Footer.tsx` at line 8, The import list in components/Footer.tsx
includes an unused symbol `fadeInUp`; remove `fadeInUp` from the import
statement that currently imports `fadeInUp, staggerContainer, staggerItem,
defaultViewport` so only the used exports (`staggerContainer`, `staggerItem`,
`defaultViewport`) are imported, keeping the file clean and avoiding the
unused-import warning.
lib/animations.ts (2)

103-131: scaleOnHover and cardHover should be explicitly typed

Both objects are exported but lack an explicit type annotation. While they can't be typed as Variants (their keys are rest/hover, not hidden/visible), they should still carry an explicit type to prevent accidental misuse and improve IntelliSense.

♻️ Proposed fix
- export const scaleOnHover = {
+ export const scaleOnHover: { rest: object; hover: object } = {

Or define a local HoverVariant type to be consistent across both exports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/animations.ts` around lines 103 - 131, The exported objects scaleOnHover
and cardHover lack explicit type annotations; define a local HoverVariant type
(e.g., an interface or type alias describing the rest and hover states with
scale/y and transition shapes) and annotate both exports with that type so their
shapes are enforced and IntelliSense works consistently; update the declarations
for scaleOnHover and cardHover to use the new HoverVariant type and ensure the
rest/hover properties match the type signature.

6-6: Consider migrating to the canonical motion/react import

The framer-motion package is still published as a compatibility shim in v12, but the official upgrade path is to uninstall framer-motion and install motion, with the canonical React import now being from "motion/react". There are no breaking changes in Motion for React in version 12, so the migration is low-risk. Keeping from "framer-motion" continues to work but diverges from the package's current recommended pattern.

framer-motion v12 "motion/react" import migration guide backward compatibility
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/animations.ts` at line 6, The import currently pulls Variants from the
legacy package ("framer-motion"); update the canonical React import to use
"motion/react" instead — replace the import statement that references Variants
from "framer-motion" with one that imports Variants from "motion/react" so
functions or variables in this file that use Variants continue to work with the
v12 Motion package.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/Footer.tsx`:
- Around line 1-2: These components (Footer, FaqComponent, LatestArticles, and
the four functions in value-blocks.tsx) are static and should not be "use
client"; remove the "use client" directive from each and keep them as Server
Components, then create a thin client-side animated wrapper (e.g.,
AnimatedSection or AnimatedWrapper in a .client.tsx file that uses
motion/framer-motion and the project's stagger variants) and import that wrapper
into the server components to wrap only the animated markup; update the server
components to pass their static children into that client wrapper so only the
animation shell is bundled to the client.

In `@components/organization-card.tsx`:
- Around line 46-51: The motion.div wrapper (using variants={fadeInUp},
initial="hidden", whileInView="visible", viewport={defaultViewport}) has no
height constraint and breaks equal-height card grids; add a full-height utility
to the animated wrapper and the clickable card container (the nested Link
element) so the animated element and its child stretch to the grid cell height
(e.g., add className="h-full" to the motion.div and to the Link/card container),
ensuring any inner card element also uses h-full so the card fills the available
space.

In `@components/project-card.tsx`:
- Around line 46-51: The motion.div wrapper (variants={fadeInUp}
initial="hidden" whileInView="visible" viewport={defaultViewport}) lacks a
height, so its child CardWrapper (flex flex-col) cannot stretch to uniform row
height; update the motion.div to occupy full height (e.g., add a full-height
className or style such as h-full / height:100%) so CardWrapper can expand and
produce equal-height project cards in the grid.

In `@components/testimonials.tsx`:
- Line 13: The imports staggerContainer and staggerItem are unused in the
Testimonials component; either remove them from the import list in
components/testimonials.tsx or apply them as motion variants on the wrapper and
child elements (e.g., use staggerContainer as the outer motion.div's variants
and staggerItem for each testimonial item) so the identifiers are referenced;
update the import to only include fadeInUp and defaultViewport if you choose
removal, or wire the variants into the JSX (Testimonials component's main
wrapper and testimonial item elements) if you choose to use them.

In `@components/trending-orgs-client.tsx`:
- Line 14: The import list in components/trending-orgs-client.tsx includes an
unused symbol; remove scaleOnHover from the import statement (leave fadeInUp and
defaultViewport) so the import reads only the actually used exports from
"@/lib/animations" and eliminates the unused import warning.

In `@components/waitlist-cta.tsx`:
- Line 82: The className on the element in components/waitlist-cta.tsx uses a
hardcoded color and a non-existent Tailwind class; replace text-black with the
design token class (e.g., text-foreground) and swap border-grey for a valid
Tailwind/design token (e.g., border-gray-200 or your project's border-muted
token) so the final className reads something like "inline-flex items-center
gap-2 rounded-full border border-gray-200 px-4 py-2 text-xs font-semibold
text-foreground"; confirm the chosen token exists in your tailwind.config/design
system and adjust to the correct semantic token if different.

In `@lib/animations.ts`:
- Around line 207-215: Update the misleading inline comments on the exported
constant defaultViewport: remove the redundant comments that restate the obvious
for the properties once and amount, and replace the incorrect comment for margin
with an accurate description of its effect (e.g., explain that the negative
bottom margin shrinks the intersection root so the observer fires only after the
element is at least 100px into the viewport, referencing defaultViewport, once,
margin, and amount to locate the code).
- Around line 221-228: getAccessibleVariants is unused and unreliable; replace
its module-level matchMedia approach with a centralized Framer Motion
reduced-motion solution: remove calls to getAccessibleVariants and instead wrap
the app shell with MotionConfig using useReducedMotion (call useReducedMotion in
a client component such as the root layout to compute shouldReduceMotion and
pass reducedMotion={shouldReduceMotion ? "always" : "never"}), and update
components that pass raw variants (e.g., where variants={fadeInUp}) to rely on
MotionConfig (no per-component reduced-motion guards needed) so user preference
changes are respected and hooks rules are followed.

---

Duplicate comments:
In `@components/faq.tsx`:
- Line 1: Remove the top-level "use client" directive from the FaqComponent and
convert it back to a server (static) component; if any child behavior requires
client-side interactivity, create a minimal client wrapper component (e.g.,
FaqClientWrapper) that imports the server FaqComponent and contains the "use
client" directive, then use the wrapper only where client-side hooks/events are
needed—this mirrors the thin-wrapper pattern used in Footer.tsx and keeps
FaqComponent static.

In `@components/latest-articles.tsx`:
- Line 1: LatestArticles is marked "use client" but only renders static
COMING_SOON_ARTICLES and only needs Framer Motion; convert LatestArticles to a
Server Component by removing "use client" and move any Framer Motion usages into
a tiny Client wrapper (e.g., LatestArticlesClient) that imports motion and wraps
the server-rendered output where animation is required; keep component names
LatestArticles and COMING_SOON_ARTICLES intact and ensure the page imports the
new LatestArticlesClient wrapper only where client-side animation is needed.

In `@components/value-blocks.tsx`:
- Line 1: Remove the top-level "use client" directive from
components/value-blocks.tsx so the four static feature blocks remain a server
component; if any small interactivity is required, implement the thin-wrapper
pattern used in Footer.tsx by exporting a pure server component (e.g.,
ValueBlocks) and creating a separate client wrapper (e.g., ValueBlocksClient)
that imports the server component and adds only the client-only logic/handlers.

---

Nitpick comments:
In `@components/Footer.tsx`:
- Line 8: The import list in components/Footer.tsx includes an unused symbol
`fadeInUp`; remove `fadeInUp` from the import statement that currently imports
`fadeInUp, staggerContainer, staggerItem, defaultViewport` so only the used
exports (`staggerContainer`, `staggerItem`, `defaultViewport`) are imported,
keeping the file clean and avoiding the unused-import warning.

In `@components/header.tsx`:
- Around line 37-40: The header is using inline initial/animate/transition props
on motion.nav which bypasses the shared variants and can place the element
offscreen on first paint (problematic for reduced motion); replace the inline
props with a named variant (e.g., use a new or existing fadeInDown variant) and
apply it via variants on motion.nav so getAccessibleVariants from lib/animations
can be used (or add a fadeInDown variant to lib/animations with the same easing
constants and ensure getAccessibleVariants returns the reduced-motion-safe
version); remove the y-offset from initial for reduced motion by relying on
getAccessibleVariants/MotionConfig reducedMotion handling.
- Line 11: The import fadeInUp is unused in this component; remove the unused
import statement (fadeInUp) from the top of the file (components/header.tsx) or,
if the animation was intended, apply fadeInUp to the Header component's
animation usage (e.g., where the Header/JSX root or motion element is defined)
and then keep the import—otherwise delete the import to eliminate the unused
symbol.

In `@lib/animations.ts`:
- Around line 103-131: The exported objects scaleOnHover and cardHover lack
explicit type annotations; define a local HoverVariant type (e.g., an interface
or type alias describing the rest and hover states with scale/y and transition
shapes) and annotate both exports with that type so their shapes are enforced
and IntelliSense works consistently; update the declarations for scaleOnHover
and cardHover to use the new HoverVariant type and ensure the rest/hover
properties match the type signature.
- Line 6: The import currently pulls Variants from the legacy package
("framer-motion"); update the canonical React import to use "motion/react"
instead — replace the import statement that references Variants from
"framer-motion" with one that imports Variants from "motion/react" so functions
or variables in this file that use Variants continue to work with the v12 Motion
package.

Comment on lines +1 to +2
"use client";

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unnecessary "use client" conversion — also affects faq.tsx, value-blocks.tsx, latest-articles.tsx

Footer, FaqComponent, all four *Block functions in value-blocks.tsx, and LatestArticles are purely display components whose entire content is static. Adding "use client" on each forces the full component tree — static content, constants, markup — into the client JS bundle on every page that imports them, increasing bundle size unnecessarily.

The preferred pattern in the App Router is to keep the parent as a Server Component and extract only the animated wrapper into a thin "use client" boundary:

// animated-section.client.tsx  ← thin CC wrapper
"use client";
import { motion } from "framer-motion";
import { staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";

export function AnimatedSection({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      variants={staggerContainer}
      initial="hidden"
      whileInView="visible"
      viewport={defaultViewport}
    >
      {children}
    </motion.div>
  );
}

Then the Server Component imports the wrapper and passes static children into it, preserving server rendering for everything except the animation shell.

As per coding guidelines, follow existing project design patterns strictly — converting static Server Components to Client Components diverges from the App Router's SC/CC split that the rest of the codebase maintains.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Footer.tsx` around lines 1 - 2, These components (Footer,
FaqComponent, LatestArticles, and the four functions in value-blocks.tsx) are
static and should not be "use client"; remove the "use client" directive from
each and keep them as Server Components, then create a thin client-side animated
wrapper (e.g., AnimatedSection or AnimatedWrapper in a .client.tsx file that
uses motion/framer-motion and the project's stagger variants) and import that
wrapper into the server components to wrap only the animated markup; update the
server components to pass their static children into that client wrapper so only
the animation shell is bundled to the client.

Comment on lines +46 to 51
<motion.div
variants={fadeInUp}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all usages of OrganizationCard to see parent container styling
rg -n "OrganizationCard" --type tsx -A 5 -B 5

Repository: ketankauntia/gsoc-orgs

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Also check the actual component file to see current state
cat -n components/organization-card.tsx | head -80

Repository: ketankauntia/gsoc-orgs

Length of output: 3058


🏁 Script executed:

#!/bin/bash
# Search for grid/flex container patterns that might hold OrganizationCard
rg -n "grid|flex" --type tsx -B 2 -A 2 | grep -A 10 -B 10 "OrganizationCard"

Repository: ketankauntia/gsoc-orgs

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find OrganizationCard usages without file type restriction
rg -n "OrganizationCard" -A 5 -B 5

Repository: ketankauntia/gsoc-orgs

Length of output: 11795


🏁 Script executed:

#!/bin/bash
# Read the full component file
cat -n components/organization-card.tsx

Repository: ketankauntia/gsoc-orgs

Length of output: 5520


🏁 Script executed:

#!/bin/bash
# Look for grid or flex containers holding cards
rg -n "grid|flex" -A 3 -B 1 | head -100

Repository: ketankauntia/gsoc-orgs

Length of output: 12929


🏁 Script executed:

#!/bin/bash
# Find the Grid component in components/ui
find . -name "*.tsx" -o -name "*.ts" | xargs grep -l "export.*Grid" | head -5

Repository: ketankauntia/gsoc-orgs

Length of output: 304


🏁 Script executed:

#!/bin/bash
# Check components/ui directory structure
ls -la components/ui/ 2>/dev/null || ls -la components/ 2>/dev/null | head -20

Repository: ketankauntia/gsoc-orgs

Length of output: 1298


🏁 Script executed:

#!/bin/bash
# Search for Grid export in index files
rg -n "export.*Grid|Grid.*from" components/ui/ 2>/dev/null || rg -n "Grid" components/ | grep -i "export\|from" | head -20

Repository: ketankauntia/gsoc-orgs

Length of output: 407


🏁 Script executed:

#!/bin/bash
# Read the Grid component implementation
cat -n components/ui/grid.tsx

Repository: ketankauntia/gsoc-orgs

Length of output: 1340


🏁 Script executed:

#!/bin/bash
# Also check CardWrapper which is used in tech-stack-detail-client.tsx with h-full
cat -n components/ui/card-wrapper.tsx

Repository: ketankauntia/gsoc-orgs

Length of output: 1181


motion.div wrapper breaks equal-height card layouts without h-full

The motion.div wrapper lacks height constraints. In Tailwind grids, items stretch horizontally but not vertically by default. Since motion.div has no h-full and the nested Link only has block and w-full (not h-full), cards will render at varying heights determined by content, not equal grid cell heights.

🛠️ Proposed fix
 <motion.div
   variants={fadeInUp}
   initial="hidden"
   whileInView="visible"
   viewport={defaultViewport}
+  className="h-full"
 >
   <Link 
     href={`/organizations/${org.slug}`}
     prefetch={true}
     className={cn(
       "block bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md hover:border-gray-300 transition-all w-full",
+      "h-full",
       "dark:bg-card dark:border-border dark:hover:border-gray-600",
       className
     )}
   >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/organization-card.tsx` around lines 46 - 51, The motion.div
wrapper (using variants={fadeInUp}, initial="hidden", whileInView="visible",
viewport={defaultViewport}) has no height constraint and breaks equal-height
card grids; add a full-height utility to the animated wrapper and the clickable
card container (the nested Link element) so the animated element and its child
stretch to the grid cell height (e.g., add className="h-full" to the motion.div
and to the Link/card container), ensuring any inner card element also uses
h-full so the card fills the available space.

Comment on lines +46 to +51
<motion.div
variants={fadeInUp}
initial="hidden"
whileInView="visible"
viewport={defaultViewport}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same motion.div equal-height concern as OrganizationCard

The motion.div wrapper has no height set; CardWrapper inside uses flex flex-col but will only be as tall as its content within the motion.div cell. In a uniform-height grid of project cards, shorter cards won't stretch to fill the row.

🛠️ Proposed fix
 <motion.div
   variants={fadeInUp}
   initial="hidden"
   whileInView="visible"
   viewport={defaultViewport}
+  className="h-full"
 >
-  <CardWrapper padding="md" hover className={cn("flex flex-col relative", className)}>
+  <CardWrapper padding="md" hover className={cn("flex flex-col relative h-full", className)}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/project-card.tsx` around lines 46 - 51, The motion.div wrapper
(variants={fadeInUp} initial="hidden" whileInView="visible"
viewport={defaultViewport}) lacks a height, so its child CardWrapper (flex
flex-col) cannot stretch to uniform row height; update the motion.div to occupy
full height (e.g., add a full-height className or style such as h-full /
height:100%) so CardWrapper can expand and produce equal-height project cards in
the grid.

} from "@/components/ui/carousel";
import { Quote } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

staggerContainer and staggerItem are imported but never used

Both identifiers are imported at line 13 but have no reference anywhere in the component's JSX or logic.

🛠️ Proposed fix
-import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";
+import { fadeInUp, defaultViewport } from "@/lib/animations";
📝 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.

Suggested change
import { fadeInUp, staggerContainer, staggerItem, defaultViewport } from "@/lib/animations";
import { fadeInUp, defaultViewport } from "@/lib/animations";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/testimonials.tsx` at line 13, The imports staggerContainer and
staggerItem are unused in the Testimonials component; either remove them from
the import list in components/testimonials.tsx or apply them as motion variants
on the wrapper and child elements (e.g., use staggerContainer as the outer
motion.div's variants and staggerItem for each testimonial item) so the
identifiers are referenced; update the import to only include fadeInUp and
defaultViewport if you choose removal, or wire the variants into the JSX
(Testimonials component's main wrapper and testimonial item elements) if you
choose to use them.

import type { CarouselApi } from "@/components/ui/carousel";
import { Section, Heading } from "@/components/ui";
import type { FeaturedOrg } from "@/lib/homepage-types";
import { fadeInUp, scaleOnHover, defaultViewport } from "@/lib/animations";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify exports from lib/animations.ts and scaleOnHover usage

echo "=== Checking lib/animations.ts for scaleOnHover export ==="
if [ -f "lib/animations.ts" ]; then
  cat -n lib/animations.ts | grep -E "(export|scaleOnHover)"
else
  echo "lib/animations.ts not found, searching for it..."
  fd animations.ts
fi

echo ""
echo "=== Searching for scaleOnHover usage across the codebase ==="
rg -n "scaleOnHover" --type=ts --type=tsx

echo ""
echo "=== Checking trending-orgs-client.tsx for scaleOnHover usage ==="
if [ -f "components/trending-orgs-client.tsx" ]; then
  cat -n components/trending-orgs-client.tsx | grep -i "scaleOnHover"
  echo "---"
  echo "Full import line:"
  sed -n '14p' components/trending-orgs-client.tsx
fi

Repository: ketankauntia/gsoc-orgs

Length of output: 1076


Remove unused scaleOnHover import

scaleOnHover is imported but never used in this component. Although the export exists in lib/animations.ts, it serves no purpose here and should be removed.

Remove unused import
-import { fadeInUp, scaleOnHover, defaultViewport } from "@/lib/animations";
+import { fadeInUp, defaultViewport } from "@/lib/animations";
📝 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.

Suggested change
import { fadeInUp, scaleOnHover, defaultViewport } from "@/lib/animations";
import { fadeInUp, defaultViewport } from "@/lib/animations";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/trending-orgs-client.tsx` at line 14, The import list in
components/trending-orgs-client.tsx includes an unused symbol; remove
scaleOnHover from the import statement (leave fadeInUp and defaultViewport) so
the import reads only the actually used exports from "@/lib/animations" and
eliminates the unused import warning.

initial="hidden"
whileInView="visible"
viewport={defaultViewport}
className="inline-flex items-center gap-2 rounded-full border border-grey px-4 py-2 text-xs font-semibold text-black"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hardcoded color text-black and potentially invalid border-grey

Two issues in this new className:

  1. text-black is a hardcoded CSS color value. Use the design system's semantic token instead (e.g., text-foreground). As per coding guidelines, only existing color tokens and design variables may be used — no hardcoded colors.

  2. border-grey is not a standard Tailwind CSS utility class; the correct spelling is border-gray-{shade} (e.g., border-gray-200). If no custom grey color token is defined in tailwind.config, this class is silently discarded and the border won't render.

🛠️ Proposed fix
-className="inline-flex items-center gap-2 rounded-full border border-grey px-4 py-2 text-xs font-semibold text-black"
+className="inline-flex items-center gap-2 rounded-full border border-border px-4 py-2 text-xs font-semibold text-foreground"
📝 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.

Suggested change
className="inline-flex items-center gap-2 rounded-full border border-grey px-4 py-2 text-xs font-semibold text-black"
className="inline-flex items-center gap-2 rounded-full border border-border px-4 py-2 text-xs font-semibold text-foreground"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waitlist-cta.tsx` at line 82, The className on the element in
components/waitlist-cta.tsx uses a hardcoded color and a non-existent Tailwind
class; replace text-black with the design token class (e.g., text-foreground)
and swap border-grey for a valid Tailwind/design token (e.g., border-gray-200 or
your project's border-muted token) so the final className reads something like
"inline-flex items-center gap-2 rounded-full border border-gray-200 px-4 py-2
text-xs font-semibold text-foreground"; confirm the chosen token exists in your
tailwind.config/design system and adjust to the correct semantic token if
different.

Comment on lines +207 to +215
/**
* Default viewport options for scroll animations
* Triggers animation when element is 20% visible
*/
export const defaultViewport = {
once: true, // Animate only once
margin: "0px 0px -100px 0px", // Trigger slightly before entering viewport
amount: 0.2 // Trigger when 20% visible
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading defaultViewport comment + guideline-violating inline comments

The comment on line 213 (// Trigger slightly before entering viewport) is factually incorrect. A negative bottom margin ("0px 0px -100px 0px") shrinks the intersection root from the bottom, meaning the element must scroll 100px into the viewport before the observer fires — the opposite of "slightly before entering." Correct it to match the actual behavior (e.g., // Fires when element is ≥100px above the viewport bottom).

Additionally, inline comments on lines 212 and 214 (// Animate only once, // Trigger when 20% visible) restate code that is already self-evident from the property names. As per coding guidelines: "Do NOT add comments that restate the obvious or explain what the code does if clear."

🛠️ Proposed fix
 export const defaultViewport = {
-  once: true, // Animate only once
-  margin: "0px 0px -100px 0px", // Trigger slightly before entering viewport
-  amount: 0.2 // Trigger when 20% visible
+  once: true,
+  margin: "0px 0px -100px 0px", // Fires when element has scrolled 100px above the viewport bottom edge
+  amount: 0.2
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/animations.ts` around lines 207 - 215, Update the misleading inline
comments on the exported constant defaultViewport: remove the redundant comments
that restate the obvious for the properties once and amount, and replace the
incorrect comment for margin with an accurate description of its effect (e.g.,
explain that the negative bottom margin shrinks the intersection root so the
observer fires only after the element is at least 100px into the viewport,
referencing defaultViewport, once, margin, and amount to locate the code).

Comment on lines +221 to +228
export const getAccessibleVariants = (variants: Variants): Variants => {
if (typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
return {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { duration: 0.01 } }
};
}
return variants;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

getAccessibleVariants is dead code — reduced-motion accessibility is unimplemented

The PR description states "All animations respect prefers-reduced-motion for accessibility," but none of the 11 dependent component files call getAccessibleVariants. Every component directly passes raw variants (e.g. variants={fadeInUp}) with no reduced-motion guard. The utility is entirely unused.

Additionally, the synchronous window.matchMedia approach has two shortcomings compared to Framer Motion's own useReducedMotion() hook:

  1. It cannot react to changes in the user's motion preference during the session.
  2. It cannot be called inside JSX (breaking rules of hooks and forcing a module-level call), so it would need to be called every render.

The idiomatic fix is to use useReducedMotion() from Framer Motion in a shared hook or MotionConfig:

♻️ Recommended pattern: centralized reduced-motion via MotionConfig
- export const getAccessibleVariants = (variants: Variants): Variants => {
-   if (typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
-     return {
-       hidden: { opacity: 0 },
-       visible: { opacity: 1, transition: { duration: 0.01 } }
-     };
-   }
-   return variants;
- };

In the root layout (e.g., app/layout.tsx), wrap content with MotionConfig:

import { MotionConfig } from "framer-motion";
import { useReducedMotion } from "framer-motion";

// In the root layout client shell:
const shouldReduceMotion = useReducedMotion();
<MotionConfig reducedMotion={shouldReduceMotion ? "always" : "never"}>
  {children}
</MotionConfig>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/animations.ts` around lines 221 - 228, getAccessibleVariants is unused
and unreliable; replace its module-level matchMedia approach with a centralized
Framer Motion reduced-motion solution: remove calls to getAccessibleVariants and
instead wrap the app shell with MotionConfig using useReducedMotion (call
useReducedMotion in a client component such as the root layout to compute
shouldReduceMotion and pass reducedMotion={shouldReduceMotion ? "always" :
"never"}), and update components that pass raw variants (e.g., where
variants={fadeInUp}) to rely on MotionConfig (no per-component reduced-motion
guards needed) so user preference changes are respected and hooks rules are
followed.

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.

1 participant