feat: add motion animations to enhance website UX#101
feat: add motion animations to enhance website UX#101KrishSharma4406 wants to merge 1 commit intoketankauntia:masterfrom
Conversation
- 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
|
@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. |
|
Warning
|
| 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 | 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 | 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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (5)
components/header.tsx (2)
37-40: Inconsistent animation approach and reduced-motion gapEvery other component in this PR uses
variantsfromlib/animations. The header instead uses bareinitial/animate/transitioninline props. Beyond the inconsistency, this approach has an accessibility problem: even if aMotionConfig 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 amotion.nav-compatible named variant) so that thegetAccessibleVariantsutility exported fromlib/animationscan be applied, or at minimum start fromopacity: 0without 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
fadeInDowndoesn't exist inlib/animations, add it alongside the other variants using the same easing constants so it benefits fromgetAccessibleVariants.🤖 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
fadeInUpis 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
fadeInUpis imported but never used — onlystaggerContainer,staggerItem, anddefaultViewportare 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:scaleOnHoverandcardHovershould be explicitly typedBoth objects are exported but lack an explicit type annotation. While they can't be typed as
Variants(their keys arerest/hover, nothidden/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
HoverVarianttype 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 canonicalmotion/reactimportThe
framer-motionpackage is still published as a compatibility shim in v12, but the official upgrade path is to uninstallframer-motionand installmotion, with the canonical React import now beingfrom "motion/react". There are no breaking changes in Motion for React in version 12, so the migration is low-risk. Keepingfrom "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.
| "use client"; | ||
|
|
There was a problem hiding this comment.
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.
| <motion.div | ||
| variants={fadeInUp} | ||
| initial="hidden" | ||
| whileInView="visible" | ||
| viewport={defaultViewport} | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all usages of OrganizationCard to see parent container styling
rg -n "OrganizationCard" --type tsx -A 5 -B 5Repository: 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 -80Repository: 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 5Repository: ketankauntia/gsoc-orgs
Length of output: 11795
🏁 Script executed:
#!/bin/bash
# Read the full component file
cat -n components/organization-card.tsxRepository: 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 -100Repository: 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 -5Repository: 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 -20Repository: 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 -20Repository: ketankauntia/gsoc-orgs
Length of output: 407
🏁 Script executed:
#!/bin/bash
# Read the Grid component implementation
cat -n components/ui/grid.tsxRepository: 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.tsxRepository: 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.
| <motion.div | ||
| variants={fadeInUp} | ||
| initial="hidden" | ||
| whileInView="visible" | ||
| viewport={defaultViewport} | ||
| > |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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.
| 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"; |
There was a problem hiding this comment.
🧩 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
fiRepository: 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.
| 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" |
There was a problem hiding this comment.
Hardcoded color text-black and potentially invalid border-grey
Two issues in this new className:
-
text-blackis 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. -
border-greyis not a standard Tailwind CSS utility class; the correct spelling isborder-gray-{shade}(e.g.,border-gray-200). If no customgreycolor token is defined intailwind.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.
| 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.
| /** | ||
| * 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 | ||
| }; |
There was a problem hiding this comment.
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).
| 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; |
There was a problem hiding this comment.
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:
- It cannot react to changes in the user's motion preference during the session.
- 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.
Summary by CodeRabbit