Refactor landing#30
Conversation
… UI sheet component
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary by BeetleThis PR represents a comprehensive modernization and redesign of the Leadlly landing page, upgrading the entire tech stack to React 19, Next.js 16, and Tailwind CSS v4, while migrating from Radix UI to Base UI components and implementing a complete visual overhaul with new design system tokens, improved animations, and enhanced mobile responsiveness. 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 96 files changed, +1,200/-900 lines (estimated) 🗺️ Walkthrough:graph TD
A[PR: Modernization & Redesign] --> B[Infrastructure Upgrades]
A --> C[Component Migration]
A --> D[Design System Overhaul]
A --> E[Developer Experience]
B --> B1[React 19 & Next.js 16]
B --> B2[Tailwind CSS v4]
B --> B3[ESLint Flat Config]
B --> B4[Motion Library]
C --> C1[Radix UI → Base UI]
C --> C2[framer-motion → motion/react]
C --> C3[New Button Component]
C --> C4[Sheet Component Rewrite]
D --> D1[OKLCH Color System]
D --> D2[New Typography Fonts]
D --> D3[Design Tokens in CSS]
D --> D4[Hero Section Redesign]
E --> E1[Prettier Setup]
E --> E2[Import Sorting]
E --> E3[AI Agent Docs]
E --> E4[Path Consolidation]
style A fill:#8B4CF4,stroke:#5900D9,color:#fff
style B fill:#6298D5,stroke:#5900D9,color:#fff
style C fill:#6298D5,stroke:#5900D9,color:#fff
style D fill:#6298D5,stroke:#5900D9,color:#fff
style E fill:#6298D5,stroke:#5900D9,color:#fff
🎯 Key Changes:🚀 Major Framework Upgrades
🎨 Design System Transformation
🧩 Component Architecture
📱 Navigation Improvements
🛠️ Developer Experience
⚡ Performance Optimizations
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughProject-wide modernization: add Prettier/ESLint configs and docs, upgrade Next/React/Tailwind and dependencies, migrate global CSS to Tailwind v4 tokens, remove Tailwind config, consolidate Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant NavBar as NavBar
participant MobileMenu as MobileMenu
participant Dialog as Sheet/Dialog (Base UI)
participant Backdrop as Backdrop
User->>NavBar: clicks menu trigger
NavBar->>MobileMenu: render/open trigger (Button)
MobileMenu->>Dialog: open Dialog (Popup)
Dialog->>Backdrop: render Backdrop
User->>Dialog: interacts / clicks close
Dialog->>MobileMenu: close
Dialog->>Backdrop: remove Backdrop
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
| <div className="flex justify-start w-full"> | ||
| <div> | ||
| <h1 className="text-3xl md:text-4xl lg:text-5xl my-10 text-left font-bold bg-gradient-to-r from-purple-700 via-orange-400 to-purple-700 bg-clip-text text-transparent"> | ||
| <h1 className="text-3xl md:text-4xl lg:text-5xl my-10 text-left font-bold bg-linear-to-r from-purple-700 via-orange-400 to-purple-700 bg-clip-text text-transparent"> |
There was a problem hiding this comment.
Invalid Tailwind CSS class name: bg-linear-to-r is not a valid Tailwind utility class. This will break the gradient styling on the testimonials heading.
Confidence: 5/5
Suggested Fix
| <h1 className="text-3xl md:text-4xl lg:text-5xl my-10 text-left font-bold bg-linear-to-r from-purple-700 via-orange-400 to-purple-700 bg-clip-text text-transparent"> | |
| <h1 className="text-3xl md:text-4xl lg:text-5xl my-10 text-left font-bold bg-gradient-to-r from-purple-700 via-orange-400 to-purple-700 bg-clip-text text-transparent"> | |
The correct Tailwind CSS class for a left-to-right linear gradient is bg-gradient-to-r, not bg-linear-to-r. This change appears to be an accidental typo that will cause the gradient effect to not render.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/Home/Testimonals.tsx at line 9, there is an invalid Tailwind CSS class name "bg-linear-to-r" which should be "bg-gradient-to-r". Change "bg-linear-to-r" back to "bg-gradient-to-r" to restore the correct gradient styling on the testimonials heading.
| <div className="flex flex-col"> | ||
| <Reveal> | ||
| <h1 className="inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | ||
| <h1 className="inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-linear-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> |
There was a problem hiding this comment.
Same issue as Comment #1 - Invalid Tailwind CSS class name: bg-linear-to-r is not a valid Tailwind utility class. This will break the gradient styling on the "Track it, chart it, Celebrate it" heading.
Confidence: 5/5
Suggested Fix
| <h1 className="inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-linear-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | |
| <h1 className="inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | |
The correct Tailwind CSS class for a left-to-right linear gradient is bg-gradient-to-r, not bg-linear-to-r. This appears to be the same typo that occurred in the Testimonials component.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/growthMeter/Main.tsx at line 14, there is an invalid Tailwind CSS class name "bg-linear-to-r" which should be "bg-gradient-to-r". Change "bg-linear-to-r" back to "bg-gradient-to-r" to restore the correct gradient styling on the heading.
| <div className="my-20 md:my-40"> | ||
| <Reveal> | ||
| <h1 className=" inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | ||
| <h1 className=" inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-linear-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> |
There was a problem hiding this comment.
Same issue as Comment #1 - Invalid Tailwind CSS class name: bg-linear-to-r is not a valid Tailwind utility class. This will break the gradient styling on the "Visualize your progress" heading.
Confidence: 5/5
Suggested Fix
| <h1 className=" inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-linear-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | |
| <h1 className=" inline-block text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-[#7D3EFF] via-[#FFB35C] to-[#7D3EFF]"> | |
The correct Tailwind CSS class for a left-to-right linear gradient is bg-gradient-to-r, not bg-linear-to-r. This is the same typo that occurred in the Testimonials component and the first heading in this file.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/growthMeter/Main.tsx at line 42, there is an invalid Tailwind CSS class name "bg-linear-to-r" which should be "bg-gradient-to-r". Change "bg-linear-to-r" back to "bg-gradient-to-r" to restore the correct gradient styling on the heading.
| </h2> | ||
| <h1 className="text-4xl sm:text-5xl lg:text-7xl font-bold"> | ||
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | ||
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> |
There was a problem hiding this comment.
Same issue as Comments #1, #2, and #3 - Invalid Tailwind CSS class name: bg-linear-to-r is not a valid Tailwind utility class. This will break the gradient styling on the "As Mentor" heading. This appears to be a systematic typo across multiple files in this PR.
Confidence: 5/5
Suggested Fix
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
The correct Tailwind CSS class for a left-to-right linear gradient is bg-gradient-to-r, not bg-linear-to-r. This is the same typo that occurred in the Testimonials component and the Growth Meter component.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/mentor/BentoSection.tsx at line 145, there is an invalid Tailwind CSS class name "bg-linear-to-r" which should be "bg-gradient-to-r". Change "bg-linear-to-r" back to "bg-gradient-to-r" to restore the correct gradient styling on the "As Mentor" heading. Note: This same typo appears in multiple files across the PR and should be fixed in all locations.
| </h2> | ||
| <h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-7xl font-bold mb-4 md:mb-6"> | ||
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | ||
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> |
There was a problem hiding this comment.
Same issue as Comments #1, #2, #3, and #4 - Invalid Tailwind CSS class name: bg-linear-to-r should be bg-gradient-to-r. This systematic typo appears across multiple components in this PR and will break gradient styling.
Confidence: 5/5
Suggested Fix
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
Change bg-linear-to-r back to bg-gradient-to-r to restore the correct Tailwind CSS gradient utility class.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/mentor/Hero.tsx at line 14, there is an invalid Tailwind CSS class name "bg-linear-to-r" which should be "bg-gradient-to-r". Change "bg-linear-to-r" back to "bg-gradient-to-r" to restore the correct gradient styling. Note: This same typo appears in at least 5 files across the PR (Testimonals.tsx, growthMeter/Main.tsx twice, mentor/BentoSection.tsx, and mentor/Hero.tsx) and should be fixed in all locations with a global find-and-replace.
| </h2> | ||
| <h1 className="text-4xl sm:text-5xl lg:text-7xl font-bold"> | ||
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | ||
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> |
There was a problem hiding this comment.
Same issue as Comments #1-5 - Invalid Tailwind CSS class bg-linear-to-r should be bg-gradient-to-r. See Comment #5 for full details and recommendation for global find-and-replace across all affected files.
Confidence: 5/5
Suggested Fix
| <span className="bg-linear-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
| <span className="bg-gradient-to-r from-purple-600/80 via-amber-400 to-purple-600/80 bg-clip-text text-transparent"> | |
Change bg-linear-to-r back to bg-gradient-to-r.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/mentor/PerksSection.tsx at line 54, change "bg-linear-to-r" to "bg-gradient-to-r". This is part of a systematic typo across at least 6 files in the PR. Consider using a global find-and-replace to fix all occurrences of "bg-linear-to-r" to "bg-gradient-to-r" across the entire codebase.
| @@ -71,7 +74,7 @@ const MarqueeMotionDiv = ({ | |||
| controls, | |||
There was a problem hiding this comment.
Type mismatch: controls prop is typed as TargetAndTransition, but this appears incorrect for animation controls. The useAnimation() hook returns animation control objects (with methods like .start(), .stop()), not target/transition values. TargetAndTransition is a type for animation target values, not control objects.
Confidence: 4/5
Suggested Fix
Based on the Motion library migration, the correct type for animation controls from useAnimation() should likely be AnimationControls from motion/react, or you may need to check the Motion library documentation for the correct return type of useAnimation(). The type should match what useAnimation() returns, not a target/transition object.
If useAnimation() in the new Motion library returns a different type, import and use that type instead of TargetAndTransition.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/Home/TestimonialsMarquee.tsx at line 74, the controls prop is typed as TargetAndTransition, but this appears to be incorrect. The useAnimation() hook returns animation control objects with methods like .start() and .stop(), not target/transition values. Check the Motion library documentation for the correct return type of useAnimation() and update the type annotation accordingly. The type should match what useAnimation() returns from "motion/react".
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/Icons/GrowthMeterIcon.tsx (1)
1-8:⚠️ Potential issue | 🟡 MinorPass the incoming
classNamethroughcn.Line 3 accepts
className, but Line 8 drops it, so callers cannot style this icon.Proposed fix
- className={cn("w-5 h-5 fill-[`#2B2B2B`] stroke-none")} + className={cn("w-5 h-5 fill-[`#2B2B2B`] stroke-none", className)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Icons/GrowthMeterIcon.tsx` around lines 1 - 8, The component GrowthMeterIcon accepts a className prop but currently ignores it; update the SVG's className to pass the incoming className through the cn helper (e.g., cn("w-5 h-5 fill-[`#2B2B2B`] stroke-none", className)) so callers can style the icon; locate the className usage in the GrowthMeterIcon function and modify the cn(...) invocation to include the className argument.src/components/shared/MobileMenu.tsx (1)
47-53:⚠️ Potential issue | 🟠 MajorGive the sheet dialog an accessible title.
This sheet is a modal navigation surface, but it currently has no rendered
SheetTitle/SheetDescription, leaving the dialog without a clear accessible name.♿ Proposed fix
<SheetContent className="min-w-screen pt-20 bg-[`#F1E9F6`]"> + <SheetHeader className="sr-only"> + <SheetTitle>Navigation menu</SheetTitle> + <SheetDescription>Use these links to navigate the page.</SheetDescription> + </SheetHeader> <motion.div🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/MobileMenu.tsx` around lines 47 - 53, The Sheet dialog rendered by MobileMenu lacks an accessible name; inside the SheetContent component (before the motion.div) add a rendered SheetTitle (and optional SheetDescription) or a visible heading element to provide a clear accessible name for the modal; import/use the SheetTitle component (or a semantic <h2> within the MobileMenu component) and ensure it is placed at the top of the sheet so assistive tech can announce the dialog (reference SheetContent and the surrounding motion.div/sheetVariants to locate where to insert the title).
🧹 Nitpick comments (7)
AGENTS.md (1)
3-5: Improve specificity and documentation references.The content has several issues:
- "This version" is vague—specify the Next.js version (e.g., "Next.js 15")
- Recommending
node_modules/next/dist/docs/is problematic becausenode_modulesis typically gitignored and can vary between installations- Better to reference official documentation or include a link to the Next.js 15 upgrade guide
📝 Suggested improvements
-# This is NOT the Next.js you know +# Next.js 15 Breaking Changes -This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. +This repository uses Next.js 15, which includes breaking changes to APIs, conventions, and file structure. Before writing any code: +- Review the [Next.js 15 upgrade guide](https://nextjs.org/docs/app/building-your-application/upgrading/version-15) +- Pay attention to async Request APIs (cookies, headers, params, etc.) +- Note changes to caching defaults +- Heed all deprecation notices🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 3 - 5, Update the header text to name the specific Next.js release (e.g., "Next.js 15") instead of "This version", remove the recommendation to read docs from node_modules (do not reference node_modules/next/dist/docs/), and replace it with a link or pointer to the official Next.js docs/upgrade guide (for example the Next.js 15 upgrade guide or https://nextjs.org/docs) so readers have a stable canonical reference; change the sentence in AGENTS.md (the header block) to explicitly call out the version and the official docs/upgrade guide URL.CLAUDE.md (1)
1-1: Clarify the purpose and content of this file.The file contains only a reference to
AGENTS.mdwithout context or explanation. Consider:
- Adding a brief description of the file's purpose
- Explaining why this is separate from
AGENTS.md- Providing usage instructions if this is meant as an agent configuration file
Alternatively, if this file simply points to agent instructions, consider consolidating the content into a single file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CLAUDE.md` at line 1, The file currently only references AGENTS.md ("@AGENTS.md"); update CLAUDE.md to either (a) add a one-paragraph description explaining the file's purpose and why it exists separately from AGENTS.md, (b) include brief usage instructions or configuration examples if CLAUDE.md is meant to be an agent config, and (c) add a short note linking to AGENTS.md (e.g., "See AGENTS.md for full agent instructions") or consolidate the content into AGENTS.md and remove the separate file; locate and edit the CLAUDE.md file (the lone symbol "@AGENTS.md" in the diff) to implement one of these options so the intent and usage are clear.src/components/Home/Tracker.tsx (1)
13-14: Dead code:isInView+useRefonly feed a commented-outFloatButton.Since the
FloatButtonblock at lines 82–85 is commented out,ref,useInView, andisInVieware no longer used for anything. Consider either restoring theFloatButtonusage or removing the unused ref/hook (and theuseInViewimport) to keep the component tidy.Also applies to: 82-85
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Tracker.tsx` around lines 13 - 14, The variables ref, isInView and the useInView import are dead since the FloatButton block (FloatButton component usage) is commented out; either re-enable the FloatButton usage (restore the commented block that references ref and isInView) so the hook and ref are used, or remove the unused ref/const isInView = useInView(ref, { amount: 0.4 }) and the useInView import from the Tracker component to eliminate dead code and imports (search for ref, isInView, useInView and the commented FloatButton block to apply one of the two fixes).src/lib/utils.ts (1)
4-6: Nit: passinputstoclsxvia spread.
clsxaccepts a variadicClassValue[]signature. Passing the array directly works (clsx flattens arrays), but spreading is the idiomatic form and matches the shadcn/ui reference implementation.♻️ Proposed tweak
export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(...inputs)) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils.ts` around lines 4 - 6, The cn helper currently calls clsx with the inputs array directly; change it to call clsx using a spread of the variadic arguments so it matches clsx's idiomatic signature and the shadcn/ui reference (i.e., replace the direct array call in the cn function with a spread of inputs), keeping the twMerge(clsx(...)) composition intact and ensuring the function still accepts ClassValue[].src/components/shared/FloatButton.tsx (1)
61-61: Avoid renderingundefinedinto the class string.When
additionalStylesis omitted, this template literal produces a literalundefinedclass token. It is harmless visually, but easy to avoid.Proposed fix
- className={`bg-[`#bec1e3`]/20 size-[60px] overflow-hidden backdrop-blur-lg text-slate-700 font-medium p-2 rounded-full shadow-2xl hover:border-4 ${additionalStyles}`} + className={`bg-[`#bec1e3`]/20 size-[60px] overflow-hidden backdrop-blur-lg text-slate-700 font-medium p-2 rounded-full shadow-2xl hover:border-4 ${additionalStyles ?? ""}`}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/FloatButton.tsx` at line 61, The className template in FloatButton.tsx interpolates additionalStyles directly, which will render "undefined" if it's not passed; update the class assembly for the element that uses the additionalStyles variable so it only includes additionalStyles when defined (e.g., use a null-coalescing fallback or join/filter pattern) — locate the className string inside the FloatButton component and replace the raw `${additionalStyles}` interpolation with a conditional inclusion like additionalStyles ?? '' or a filtered join of [baseClasses, additionalStyles].filter(Boolean).join(' ').src/components/Icons/LogoFull.tsx (1)
16-63: SVG gradient IDs lack component-level scoping.Figma-generated IDs like
paint0_linear_13869_15324are document-global. While no collisions currently exist, explicitly scoping them to the component (e.g.,logo-full-paint0-linear) improves maintainability and prevents accidental reuse of identical ID values if similar logos are added in the future.Suggested approach
Replace all
paint*_linear_*IDs and their correspondingurl(#paint*_linear_*)references with component-scoped IDs:- fill="url(`#paint0_linear_13869_15324`)" + fill="url(`#logo-full-paint0`)" @@ - fill="url(`#paint1_linear_13869_15324`)" + fill="url(`#logo-full-paint1`)" @@ - fill="url(`#paint2_linear_13869_15324`)" + fill="url(`#logo-full-paint2`)" @@ - id="paint0_linear_13869_15324" + id="logo-full-paint0" @@ - id="paint1_linear_13869_15324" + id="logo-full-paint1" @@ - id="paint2_linear_13869_15324" + id="logo-full-paint2"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Icons/LogoFull.tsx` around lines 16 - 63, The SVG uses global Figma IDs (e.g., paint0_linear_13869_15324, paint1_linear_13869_15324, paint2_linear_13869_15324) which can collide; rename each <linearGradient> id to a component-scoped name (for example logo-full-paint0-linear, logo-full-paint1-linear, logo-full-paint2-linear) and update every corresponding fill="url(#...)" reference in the <path> elements to match the new IDs, ensuring all id attributes inside <defs> and their url(#...) usages are changed consistently within the LogoFull component.src/app/globals.css (1)
18-26: Orphaned Tailwind v3→v4 border-color compatibility comment.The comment describes the compatibility styles that keep pre-v4 default border color behavior, but the actual rule it refers to (
* {@applyborder-border outline-ring/50; }) now lives at lines 278–280. As-is, this comment sits above unrelated declarations (.MarqueeGradient,.gridGradient, scrollbar CSS), which is misleading. Either move it next to the@layer baserule at line 278 or drop it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/globals.css` around lines 18 - 26, The top-of-file Tailwind v3→v4 border-color compatibility comment is orphaned and misleading; move that comment to sit immediately above the `@layer` base rule that contains the selector with the compatibility rule (* { `@apply` border-border outline-ring/50; }) so the comment directly describes the rule, or remove the comment entirely if you prefer not to document it; update the comment placement near the `@layer` base/@apply rule in src/app/globals.css and ensure no unrelated declarations (e.g., .MarqueeGradient, .gridGradient, scrollbar CSS) remain between the comment and the compatibility rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.prettierignore:
- Around line 16-18: Fix the typos in the .prettierignore file patterns: replace
the invalid "*. env" with "*.env" and replace ".env .*" with ".env.*" (keep the
existing ".env" line as well if you want to ignore the exact file); ensure the
three patterns present are ".env", "*.env", and ".env.*" so they correctly match
environment files.
In @.prettierrc:
- Around line 11-23: The importOrder array uses literal space strings (" ") as
group separators which the plugin `@ianvs/prettier-plugin-sort-imports` does not
recognize; replace each " " entry with an empty string "" in the "importOrder"
configuration so the plugin interprets them as blank-line separators between
groups (leave other entries like "^react", "<THIRD_PARTY_MODULES>", "^@/(.*)$",
and "^[./]" unchanged).
In `@AGENTS.md`:
- Around line 1-7: The AGENTS.md currently contains only a Next.js warning and
must be replaced with documented agent implementations and responsibilities:
update AGENTS.md to list each AI agent/assistant used in this repo
(name/identifier), specify for each its concrete responsibilities and tasks,
include configuration/behavioral guidelines (environment variables, rate limits,
retry logic, prompt templates), and add a Next.js-specific subsection under the
relevant agent(s) describing conventions and caveats from the existing Next.js
note; ensure headings like "Agents", "Responsibilities", "Configuration", and
"Next.js guidance" are present so reviewers can quickly find each agent's role
and settings.
In `@components.json`:
- Around line 2-21: components.json contains unsupported keys/values that break
shadcn generation: change "style": "base-mira" to a supported value (e.g.,
"new-york" or remove the key to use default), set "iconLibrary": to a supported
option ("lucide" or "radix"), and remove or relocate non-standard keys "rtl" and
"menuAccent" (delete them from components.json or move them to your own app
config) so the file conforms to the shadcn schema; validate the file after edits
to ensure generation succeeds.
In `@package.json`:
- Around line 22-24: Add an "engines" field to package.json to enforce Node
>=20.9.0 required by Next.js 16; update the root package.json (where "next":
"16.2.4", "react": "19.2.5", "react-dom": "19.2.5" appear) to include an
"engines" object specifying "node": ">=20.9.0" so installs/deploys fail fast on
older Node versions; optionally include an advisory "engines" entry for your
package manager (e.g., "npm" or "pnpm") if you want to be explicit.
- Around line 27-29: Update the tailwind-merge dependency to a v3 release to
ensure compatibility with Tailwind CSS v4; specifically change the
"tailwind-merge" entry in package.json from "^2.6.1" to a v3 range (e.g.
"^3.5.0" or "^3.0.0") and then run your package manager install (npm/yarn/pnpm)
to update lockfiles and verify cn(...) behavior; target the "tailwind-merge"
package name to locate and modify the dependency.
In `@src/app/globals.css`:
- Around line 121-122: The :root CSS custom properties --tracking-normal and
--spacing are not picked up by Tailwind v4 and therefore won’t alter utility
scales; move any intended custom values into the Tailwind theme block (use
`@theme` or `@theme` inline) or mirror them into `@theme` inline (e.g., re-expose
--spacing inside `@theme` inline) so Tailwind reads them, otherwise remove them to
avoid implying a theme override; target the :root declaration that defines
--tracking-normal and --spacing and either relocate their values into the `@theme`
block or add corresponding re-exposure inside `@theme` inline.
- Around line 98-100: Rename the CSS custom property --font-sans-serif to
--font-serif in the root and dark theme declarations so the theme mapping uses a
real serif variable; specifically, change the definitions currently named
--font-sans-serif (e.g., in :root and the .dark block) to --font-serif so the
`@theme` inline mapping that references --font-serif resolves to MuseoModerno;
also update any remaining references that still use --font-sans-serif to the new
--font-serif name.
In `@src/components/Home/Contact.tsx`:
- Around line 22-27: The inputs in the Contact component remove the default
keyboard focus cue via the class "outline-hidden" and don’t provide a
replacement; in src/components/Home/Contact.tsx locate the input elements in the
Contact component and remove or replace "outline-hidden" with visible focus
styles such as adding Tailwind focus-visible utilities (e.g.,
focus-visible:ring-2 focus-visible:ring-offset-1
focus-visible:ring-<brand-color>) so each field shows a clear focus ring when
tabbing; verify by tabbing through the form that each input and the textarea
receive a visible focus indicator.
In `@src/components/Home/Hero.tsx`:
- Around line 30-35: The hero CTA Link currently uses a placeholder href ("#")
which provides no navigation; update the Link (in Hero.tsx) to point to a
meaningful route or make it configurable—either replace href={"#"} with the real
destination (e.g., "/get-started" or "/signup") or accept a prop like ctaHref
and use that value; keep the existing className/buttonVariants usage
(buttonVariants({ size: "lg" })) and ensure the prop default is set if you make
it configurable.
- Around line 48-59: The motion.div wrapper around the Image (the element with
className "relative flex-1 h-full") must provide an explicit mobile height so
Next/Image with fill has a sizing context: change the wrapper's classes to
include a mobile min-height (e.g., replace or augment "h-full" with something
like "min-h-[320px] md:h-[636px]") and keep it "relative"; also add a responsive
sizes prop to the Image component (for example sizes="(max-width: 768px) 100vw,
50vw") to improve loading and responsive behavior.
In `@src/components/Home/TestimonialsMarquee.tsx`:
- Line 5: The prop type is incorrect: `TargetAndTransition` (a target state) was
used for the `controls` prop but `useAnimation()` returns animation controls;
change the prop type to `LegacyAnimationControls` (the type returned by
`useAnimation`) and import it from "motion/react" instead of
`TargetAndTransition`; update any places referencing `controls` (e.g., the prop
on TestimonialsMarquee and the internal call to `useAnimation()`) so the prop
and return types match (`useAnimation(): LegacyAnimationControls` and `controls:
LegacyAnimationControls`).
In `@src/components/shared/FloatButton.tsx`:
- Around line 20-55: The animate sequence in the useEffect (animateButton) can
overlap when isInView flips; fix it by capturing the control objects returned by
each animate(...) call (from animate) and storing them in a local array (or
variable), call control.stop() or control.cancel() on each before starting a new
sequence, and ensure the effect returns a cleanup function that cancels any
in-flight controls when the effect re-runs or unmounts; apply these changes
around the animate calls inside animateButton and the outer useEffect so
animateButton, animate, isInView and the cleanup properly coordinate.
In `@src/components/shared/FullPageCard.tsx`:
- Around line 1-31: Add the "use client" directive at the top of the
FullPageCard component file and improve the close button accessibility: mark the
component as client-side (add "use client") because it uses motion and the
setVisibility state setter, change the Image alt text to a meaningful label
(e.g., "Close" or "Close dialog") and ensure the button has an accessible name
(either via aria-label="Close" on the button or by using a visually hidden
label) so the onClick handler tied to setVisibility remains usable by assistive
tech; update references in this file to FullPageCard, the close button, and the
Image alt accordingly.
In `@src/components/shared/MobileMenu.tsx`:
- Around line 40-46: The SheetTrigger/Button in MobileMenu is an icon-only
control and needs an accessible name; update the SheetTrigger/Button render so
the icon button has an explicit accessible label (e.g., add aria-label or
aria-labelledby) on the Button used in the render prop (referencing
SheetTrigger, Button, MobileMenu, and Menu) so screen readers announce its
purpose (for example aria-label="Open menu" or similar).
- Around line 55-66: The current map renders a SheetClose button inside an
anchor (in the menuItems.map where motion.a is used and itemVariants applied),
producing invalid <a><button/></a> markup; change this by removing the nested
SheetClose and instead use SheetClose's render prop with nativeButton={false} so
the close control renders as the anchor itself—move href, className, animation
props/variants from motion.a into the SheetClose render output (or have
SheetClose render the motion element) so the element returned is a single anchor
that both navigates and closes the sheet.
In `@src/components/shared/NavBar.tsx`:
- Line 54: The MobileMenu component (used as <MobileMenu menuItems={NavLinks}
/>) is rendered on all screen sizes, causing duplicate navigation with the
desktop nav; wrap or modify the MobileMenu render so it is hidden at desktop
breakpoints (e.g., apply a responsive hiding class like "md:hidden" to the
MobileMenu wrapper or its trigger) so MobileMenu only appears on small screens
while the existing desktop nav remains visible at md and above.
In `@src/helpers/constants/index.ts`:
- Around line 19-24: NavLinks defines four hash targets (`#plans`, `#reviews`,
`#faqs`, `#become-a-mentor`) but the landing page lacks matching section IDs; add
corresponding elements with id="plans", id="reviews", id="faqs", and
id="become-a-mentor" in the landing page component (or layout) so the NavLinks
array works correctly — ensure each section is a semantic container (e.g.,
<section> or equivalent in your framework) with readable headings and content,
and verify the IDs exactly match the strings in NavLinks.
In `@tsconfig.json`:
- Line 36: Remove the suspicious include entry "src/components/shared/dw" from
the tsconfig.json includes array (or replace it with the correct intended path),
since it's a non-existent/truncated path; locate the "includes" array that
contains "src/components/shared/dw" and either delete that element or correct it
to the proper directory (e.g., the intended shared component folder) so
TypeScript doesn't carry dead config.
---
Outside diff comments:
In `@src/components/Icons/GrowthMeterIcon.tsx`:
- Around line 1-8: The component GrowthMeterIcon accepts a className prop but
currently ignores it; update the SVG's className to pass the incoming className
through the cn helper (e.g., cn("w-5 h-5 fill-[`#2B2B2B`] stroke-none",
className)) so callers can style the icon; locate the className usage in the
GrowthMeterIcon function and modify the cn(...) invocation to include the
className argument.
In `@src/components/shared/MobileMenu.tsx`:
- Around line 47-53: The Sheet dialog rendered by MobileMenu lacks an accessible
name; inside the SheetContent component (before the motion.div) add a rendered
SheetTitle (and optional SheetDescription) or a visible heading element to
provide a clear accessible name for the modal; import/use the SheetTitle
component (or a semantic <h2> within the MobileMenu component) and ensure it is
placed at the top of the sheet so assistive tech can announce the dialog
(reference SheetContent and the surrounding motion.div/sheetVariants to locate
where to insert the title).
---
Nitpick comments:
In `@AGENTS.md`:
- Around line 3-5: Update the header text to name the specific Next.js release
(e.g., "Next.js 15") instead of "This version", remove the recommendation to
read docs from node_modules (do not reference node_modules/next/dist/docs/), and
replace it with a link or pointer to the official Next.js docs/upgrade guide
(for example the Next.js 15 upgrade guide or https://nextjs.org/docs) so readers
have a stable canonical reference; change the sentence in AGENTS.md (the header
block) to explicitly call out the version and the official docs/upgrade guide
URL.
In `@CLAUDE.md`:
- Line 1: The file currently only references AGENTS.md ("@AGENTS.md"); update
CLAUDE.md to either (a) add a one-paragraph description explaining the file's
purpose and why it exists separately from AGENTS.md, (b) include brief usage
instructions or configuration examples if CLAUDE.md is meant to be an agent
config, and (c) add a short note linking to AGENTS.md (e.g., "See AGENTS.md for
full agent instructions") or consolidate the content into AGENTS.md and remove
the separate file; locate and edit the CLAUDE.md file (the lone symbol
"@AGENTS.md" in the diff) to implement one of these options so the intent and
usage are clear.
In `@src/app/globals.css`:
- Around line 18-26: The top-of-file Tailwind v3→v4 border-color compatibility
comment is orphaned and misleading; move that comment to sit immediately above
the `@layer` base rule that contains the selector with the compatibility rule (* {
`@apply` border-border outline-ring/50; }) so the comment directly describes the
rule, or remove the comment entirely if you prefer not to document it; update
the comment placement near the `@layer` base/@apply rule in src/app/globals.css
and ensure no unrelated declarations (e.g., .MarqueeGradient, .gridGradient,
scrollbar CSS) remain between the comment and the compatibility rule.
In `@src/components/Home/Tracker.tsx`:
- Around line 13-14: The variables ref, isInView and the useInView import are
dead since the FloatButton block (FloatButton component usage) is commented out;
either re-enable the FloatButton usage (restore the commented block that
references ref and isInView) so the hook and ref are used, or remove the unused
ref/const isInView = useInView(ref, { amount: 0.4 }) and the useInView import
from the Tracker component to eliminate dead code and imports (search for ref,
isInView, useInView and the commented FloatButton block to apply one of the two
fixes).
In `@src/components/Icons/LogoFull.tsx`:
- Around line 16-63: The SVG uses global Figma IDs (e.g.,
paint0_linear_13869_15324, paint1_linear_13869_15324, paint2_linear_13869_15324)
which can collide; rename each <linearGradient> id to a component-scoped name
(for example logo-full-paint0-linear, logo-full-paint1-linear,
logo-full-paint2-linear) and update every corresponding fill="url(#...)"
reference in the <path> elements to match the new IDs, ensuring all id
attributes inside <defs> and their url(#...) usages are changed consistently
within the LogoFull component.
In `@src/components/shared/FloatButton.tsx`:
- Line 61: The className template in FloatButton.tsx interpolates
additionalStyles directly, which will render "undefined" if it's not passed;
update the class assembly for the element that uses the additionalStyles
variable so it only includes additionalStyles when defined (e.g., use a
null-coalescing fallback or join/filter pattern) — locate the className string
inside the FloatButton component and replace the raw `${additionalStyles}`
interpolation with a conditional inclusion like additionalStyles ?? '' or a
filtered join of [baseClasses, additionalStyles].filter(Boolean).join(' ').
In `@src/lib/utils.ts`:
- Around line 4-6: The cn helper currently calls clsx with the inputs array
directly; change it to call clsx using a spread of the variadic arguments so it
matches clsx's idiomatic signature and the shadcn/ui reference (i.e., replace
the direct array call in the cn function with a spread of inputs), keeping the
twMerge(clsx(...)) composition intact and ensuring the function still accepts
ClassValue[].
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7585a9e8-abd1-4ae2-bac5-54ac724068de
⛔ Files ignored due to path filters (3)
package-lock.jsonis excluded by!**/package-lock.jsonpublic/assets/images/hero-bg.svgis excluded by!**/*.svgpublic/assets/images/hero-illustration.svgis excluded by!**/*.svg
📒 Files selected for processing (78)
.prettierignore.prettierrcAGENTS.mdCLAUDE.mdcomponents.jsoneslint.config.mjsnext.config.mjspackage.jsonpostcss.config.mjssrc/app/(root)/(home)/page.tsxsrc/app/globals.csssrc/app/layout.tsxsrc/components/Home/Contact.tsxsrc/components/Home/Download.tsxsrc/components/Home/GrowthMeter.tsxsrc/components/Home/Hero.tsxsrc/components/Home/Marquee.tsxsrc/components/Home/Mentor.tsxsrc/components/Home/Planner.tsxsrc/components/Home/Testimonals.tsxsrc/components/Home/TestimonialsMarquee.tsxsrc/components/Home/Tracker.tsxsrc/components/Icons/ChatIcon.tsxsrc/components/Icons/DashboardIcon.tsxsrc/components/Icons/ErrorBookIcon.tsxsrc/components/Icons/FacebookIcon.tsxsrc/components/Icons/GrowthMeter/DailyMotivation.tsxsrc/components/Icons/GrowthMeter/GamifyLearning.tsxsrc/components/Icons/GrowthMeter/HealthyCompetetion.tsxsrc/components/Icons/GrowthMeter/Progress.tsxsrc/components/Icons/GrowthMeterIcon.tsxsrc/components/Icons/InstagramIcon.tsxsrc/components/Icons/LibertyIcon.tsxsrc/components/Icons/LinkedInIcon.tsxsrc/components/Icons/Logo.tsxsrc/components/Icons/LogoFull.tsxsrc/components/Icons/Mentor/ConnectAnytime.tsxsrc/components/Icons/Mentor/DiscussProblem.tsxsrc/components/Icons/Mentor/PersonalizedGuidance.tsxsrc/components/Icons/Mentor/ValuableInsights.tsxsrc/components/Icons/Menu.tsxsrc/components/Icons/Planner/DailyEvaluation.tsxsrc/components/Icons/Planner/DailyTask.tsxsrc/components/Icons/Planner/Efficiency.tsxsrc/components/Icons/Planner/ZeroProcrastination.tsxsrc/components/Icons/PlannerIcon.tsxsrc/components/Icons/Questionicon.tsxsrc/components/Icons/QuizIcon.tsxsrc/components/Icons/StudyRoomIcon.tsxsrc/components/Icons/Tracker/Efficiency.tsxsrc/components/Icons/Tracker/KnowledgeGap.tsxsrc/components/Icons/Tracker/RevisionFrequency.tsxsrc/components/Icons/Tracker/Tracking.tsxsrc/components/Icons/TrackerIcon.tsxsrc/components/Icons/TwitterIcon.tsxsrc/components/Icons/WorkshopIcon.tsxsrc/components/growthMeter/Card.tsxsrc/components/growthMeter/Main.tsxsrc/components/growthMeter/progessAnalytics.tsxsrc/components/illustrations/hero-illustration.tsxsrc/components/mentor/BentoSection.tsxsrc/components/mentor/Hero.tsxsrc/components/mentor/MentorNavbar.tsxsrc/components/mentor/PerksSection.tsxsrc/components/shared/FloatButton.tsxsrc/components/shared/FullPageCard.tsxsrc/components/shared/MobileMenu.tsxsrc/components/shared/NavBar.tsxsrc/components/shared/PricingCard.tsxsrc/components/shared/Reveal.tsxsrc/components/shared/RevealImage.tsxsrc/components/ui/button.tsxsrc/components/ui/sheet.tsxsrc/helpers/constants/index.tssrc/lib/utils.tssrc/utils/cn.tstailwind.config.tstsconfig.json
💤 Files with no reviewable changes (2)
- src/utils/cn.ts
- tailwind.config.ts
| *. env | ||
| .env | ||
| .env .* No newline at end of file |
There was a problem hiding this comment.
Fix typos in env ignore patterns.
*. env and .env .* contain embedded spaces and will not match any real files. The intent is presumably *.env and .env.*.
Proposed fix
-*. env
.env
-.env .*
+.env.*🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.prettierignore around lines 16 - 18, Fix the typos in the .prettierignore
file patterns: replace the invalid "*. env" with "*.env" and replace ".env .*"
with ".env.*" (keep the existing ".env" line as well if you want to ignore the
exact file); ensure the three patterns present are ".env", "*.env", and ".env.*"
so they correctly match environment files.
| "importOrder": [ | ||
| "^react", | ||
| "^next", | ||
| " ", | ||
| "<THIRD_PARTY_MODULES>", | ||
| " ", | ||
| "^@/(.*)$", | ||
| "^[./]" | ||
| ], | ||
| "importOrderSeparation": true, | ||
| "importOrderSortSpecifiers": true, | ||
| "importOrderGroupNamespaceSpecifiers": true, | ||
| "importOrderCaseInsensitive": true |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@ianvs/prettier-plugin-sort-imports importOrder empty string separator blank line
💡 Result:
In @ianvs/prettier-plugin-sort-imports, empty strings ("") in the importOrder array act as separators to insert blank lines between import groups. This replaces the removed importOrderSeparation option from previous versions. Example configuration from official docs: { "plugins": ["@ianvs/prettier-plugin-sort-imports"], "importOrder": ["<BUILTIN_MODULES>", "", "<THIRD_PARTY_MODULES>", "", "^[.]"] } This produces output like: import fs from 'fs'; import { debounce, reduce } from 'lodash'; import MyApp from './MyApp'; The maintainer confirmed in GitHub issue #117 that adding blank groups like "" is the intended way to achieve separation between sorted import groups.
Citations:
- 1: importOrderSeparation IanVS/prettier-plugin-sort-imports#117
- 2: https://www.npmjs.com/package/@ianvs/prettier-plugin-sort-imports
- 3: https://github.com/IanVS/prettier-plugin-sort-imports
🏁 Script executed:
# Check if .prettierrc exists and read its content
if [ -f ".prettierrc" ]; then
cat -n ".prettierrc"
else
echo ".prettierrc file not found"
fiRepository: Leadlly/leadlly.in
Length of output: 719
Use empty strings as importOrder separators, not spaces.
@ianvs/prettier-plugin-sort-imports treats an empty string "" as the blank-line separator between groups. A single-space string " " is not a recognized separator and is parsed as a regex group matching a literal space, which won't do what you expect.
Proposed fix
"importOrder": [
"^react",
"^next",
- " ",
+ "",
"<THIRD_PARTY_MODULES>",
- " ",
+ "",
"^@/(.*)$",
"^[./]"
],📝 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.
| "importOrder": [ | |
| "^react", | |
| "^next", | |
| " ", | |
| "<THIRD_PARTY_MODULES>", | |
| " ", | |
| "^@/(.*)$", | |
| "^[./]" | |
| ], | |
| "importOrderSeparation": true, | |
| "importOrderSortSpecifiers": true, | |
| "importOrderGroupNamespaceSpecifiers": true, | |
| "importOrderCaseInsensitive": true | |
| "importOrder": [ | |
| "^react", | |
| "^next", | |
| "", | |
| "<THIRD_PARTY_MODULES>", | |
| "", | |
| "^@/(.*)$", | |
| "^[./]" | |
| ], | |
| "importOrderSeparation": true, | |
| "importOrderSortSpecifiers": true, | |
| "importOrderGroupNamespaceSpecifiers": true, | |
| "importOrderCaseInsensitive": true |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.prettierrc around lines 11 - 23, The importOrder array uses literal space
strings (" ") as group separators which the plugin
`@ianvs/prettier-plugin-sort-imports` does not recognize; replace each " " entry
with an empty string "" in the "importOrder" configuration so the plugin
interprets them as blank-line separators between groups (leave other entries
like "^react", "<THIRD_PARTY_MODULES>", "^@/(.*)$", and "^[./]" unchanged).
| <!-- BEGIN:nextjs-agent-rules --> | ||
|
|
||
| # This is NOT the Next.js you know | ||
|
|
||
| This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. | ||
|
|
||
| <!-- END:nextjs-agent-rules --> |
There was a problem hiding this comment.
Content doesn't align with coding guidelines.
As per coding guidelines, this file should "Document agent implementations and responsibilities in AGENTS.md file." The current content warns about Next.js breaking changes but doesn't document agent implementations or responsibilities.
Consider restructuring to include:
- Which AI agents/assistants are used in this repository
- Their specific responsibilities and tasks
- Configuration or behavioral guidelines for each agent
- Next.js-specific guidance can be a subsection within agent responsibilities
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` around lines 1 - 7, The AGENTS.md currently contains only a
Next.js warning and must be replaced with documented agent implementations and
responsibilities: update AGENTS.md to list each AI agent/assistant used in this
repo (name/identifier), specify for each its concrete responsibilities and
tasks, include configuration/behavioral guidelines (environment variables, rate
limits, retry logic, prompt templates), and add a Next.js-specific subsection
under the relevant agent(s) describing conventions and caveats from the existing
Next.js note; ensure headings like "Agents", "Responsibilities",
"Configuration", and "Next.js guidance" are present so reviewers can quickly
find each agent's role and settings.
| "$schema": "https://ui.shadcn.com/schema.json", | ||
| "style": "new-york", | ||
| "style": "base-mira", | ||
| "rsc": true, | ||
| "tsx": true, | ||
| "tailwind": { | ||
| "config": "tailwind.config.ts", | ||
| "css": "src/app/globals.css", | ||
| "baseColor": "slate", | ||
| "baseColor": "neutral", | ||
| "cssVariables": true, | ||
| "prefix": "" | ||
| }, | ||
| "iconLibrary": "hugeicons", | ||
| "rtl": false, | ||
| "aliases": { | ||
| "components": "@/components", | ||
| "utils": "@/lib/utils" | ||
| } | ||
| }, | ||
| "menuColor": "default", | ||
| "menuAccent": "subtle", | ||
| "registries": {} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Validate components.json against the declared remote JSON schema without modifying the repo.
set -euo pipefail
python - <<'PY'
import json
from urllib.request import urlopen
try:
import jsonschema
except ImportError:
raise SystemExit("jsonschema is not installed in this sandbox; install locally or validate in your editor/CI.")
with open("components.json", "r", encoding="utf-8") as f:
config = json.load(f)
schema_url = config["$schema"]
with urlopen(schema_url) as response:
schema = json.load(response)
jsonschema.Draft7Validator(schema).validate(config)
print("components.json validates against", schema_url)
PYRepository: Leadlly/leadlly.in
Length of output: 2644
🏁 Script executed:
fd -t f "schema" --iglob "*schema*" | head -20Repository: Leadlly/leadlly.in
Length of output: 264
🌐 Web query:
shadcn components.json valid configuration schema baseColor iconLibrary menuColor
💡 Result:
The valid configuration schema for shadcn/ui's components.json is defined at https://ui.shadcn.com/schema.json, which provides IDE validation and autocompletion. Key relevant properties: - $schema: "https://ui.shadcn.com/schema.json" - style: "new-york" (default deprecated) - tailwind.baseColor: Valid values include "neutral", "stone", "zinc", "mauve", "olive", "mist", "taupe", "slate", "gray" (varies slightly by version/docs, but these are explicitly listed in official docs). - iconLibrary: "lucide" or "radix". Defaults to "lucide" for default style, "radix" for new-york style. Used to determine icon library for components. - menuColor: Options include "default", "inverted", "default-translucent", "inverted-translucent". Controls menu color mode (solid/translucent variants). Other common properties: rsc (true/false), tsx (true/false), tailwind.config, tailwind.css, tailwind.cssVariables (true/false), aliases (object with utils, components, ui, etc.), registries (object). Example full config: { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "tailwind": { "baseColor": "neutral", "cssVariables": true }, "iconLibrary": "lucide", "menuColor": "default", "rsc": true, "tsx": true, "aliases": { "components": "@/components", "utils": "@/lib/utils" } } Official docs recommend running npx shadcn@latest init to generate with prompts. Changes to baseColor after init require re-init or manual CSS updates if using cssVariables.
Citations:
- 1: https://ui.shadcn.com/docs/components-json
- 2: https://v4.shadcn.com/docs/components-json
- 3: https://ui.shadcn.de/docs/components-json
- 4: https://github.com/shadcn-ui/registry-template/blob/main/components.json
- 5: https://shadcn-ui-ui.mintlify.app/components-json
- 6: https://github.com/LuanRoger/electron-shadcn/blob/main/components.json
- 7: https://github.com/shadcn-ui/ui/blob/31dbc6fc/apps/v4/app/(create)/components/menu-picker.tsx
🏁 Script executed:
fd -type f --glob "*schema*"Repository: Leadlly/leadlly.in
Length of output: 232
🏁 Script executed:
grep -r "base-mira" --include="*.json" --include="*.ts" --include="*.tsx" --include="*.js" | head -10Repository: Leadlly/leadlly.in
Length of output: 101
🏁 Script executed:
cat components.jsonRepository: Leadlly/leadlly.in
Length of output: 529
Fix unsupported config values in components.json that will break component generation.
The config references values not in the official shadcn schema:
"style": "base-mira"— not a standard style (only"new-york"or default documented)"iconLibrary": "hugeicons"— unsupported; only"lucide"or"radix"are valid"rtl"and"menuAccent"— not documented standard fields
These will cause failures when running shadcn's component generation CLI. Align with supported values or verify against the actual generator your project uses.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components.json` around lines 2 - 21, components.json contains unsupported
keys/values that break shadcn generation: change "style": "base-mira" to a
supported value (e.g., "new-york" or remove the key to use default), set
"iconLibrary": to a supported option ("lucide" or "radix"), and remove or
relocate non-standard keys "rtl" and "menuAccent" (delete them from
components.json or move them to your own app config) so the file conforms to the
shadcn schema; validate the file after edits to ensure generation succeeds.
| "next": "16.2.4", | ||
| "react": "19.2.5", | ||
| "react-dom": "19.2.5", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Expect: package.json declares node >=20.9.0, and repo docs/runtime files do not contradict it.
jq '.engines' package.json
fd -a '(^\.nvmrc$|^\.node-version$|package.json$)' -x sh -c 'printf "\n--- %s ---\n" "$1"; sed -n "1,80p" "$1"' sh {}Repository: Leadlly/leadlly.in
Length of output: 1361
🌐 Web query:
Next.js 16 Node.js minimum version requirement
💡 Result:
Next.js 16 requires Node.js version 20.9.0 or higher. Node.js 18 is no longer supported.
Citations:
- 1: https://nextjs.org/docs/app/guides/upgrading/version-16
- 2: https://nextjs.org/blog/next-16
- 3: https://nextjs.org/docs/app/getting-started/installation
Add engines field to enforce Next.js 16's Node.js 20.9.0+ requirement.
Next.js 16 requires Node.js 20.9.0 or newer. Without an engines guard in package.json, installs and deployments on Node 18 or older can fail late in CI or production.
🛠️ Proposed fix
"private": true,
+ "engines": {
+ "node": ">=20.9.0"
+ },
"scripts": {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` around lines 22 - 24, Add an "engines" field to package.json to
enforce Node >=20.9.0 required by Next.js 16; update the root package.json
(where "next": "16.2.4", "react": "19.2.5", "react-dom": "19.2.5" appear) to
include an "engines" object specifying "node": ">=20.9.0" so installs/deploys
fail fast on older Node versions; optionally include an advisory "engines" entry
for your package manager (e.g., "npm" or "pnpm") if you want to be explicit.
| <SheetTrigger | ||
| render={ | ||
| <Button variant={"ghost"} size={"icon-lg"}> | ||
| <Menu /> | ||
| </Button> | ||
| } | ||
| /> |
There was a problem hiding this comment.
Add an accessible name to the menu trigger.
The trigger is icon-only, so screen readers may announce it as an unnamed button unless Menu provides its own label.
♿ Proposed fix
<SheetTrigger
render={
- <Button variant={"ghost"} size={"icon-lg"}>
+ <Button variant="ghost" size="icon-lg" aria-label="Open navigation menu">
<Menu />
</Button>
}
/>📝 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.
| <SheetTrigger | |
| render={ | |
| <Button variant={"ghost"} size={"icon-lg"}> | |
| <Menu /> | |
| </Button> | |
| } | |
| /> | |
| <SheetTrigger | |
| render={ | |
| <Button variant="ghost" size="icon-lg" aria-label="Open navigation menu"> | |
| <Menu /> | |
| </Button> | |
| } | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/shared/MobileMenu.tsx` around lines 40 - 46, The
SheetTrigger/Button in MobileMenu is an icon-only control and needs an
accessible name; update the SheetTrigger/Button render so the icon button has an
explicit accessible label (e.g., add aria-label or aria-labelledby) on the
Button used in the render prop (referencing SheetTrigger, Button, MobileMenu,
and Menu) so screen readers announce its purpose (for example aria-label="Open
menu" or similar).
| {menuItems.map((item, index) => ( | ||
| <motion.a | ||
| key={item.name} | ||
| key={item.href} | ||
| href={item.href} | ||
| initial="hidden" | ||
| animate="visible" | ||
| transition={{ delay: index * 0.2, type: "spring", stiffness: 50 }} | ||
| variants={itemVariants} | ||
| className="mb-8 block text-center text-xl font-semibold text-black/50 hover:text-black focus:text-black" | ||
| > | ||
| <SheetClose>{item.name}</SheetClose> | ||
| <SheetClose>{item.label}</SheetClose> | ||
| </motion.a> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the MobileMenu.tsx file to understand the context and imports
cat -n src/components/shared/MobileMenu.tsx | head -80Repository: Leadlly/leadlly.in
Length of output: 2912
🏁 Script executed:
# Check what SheetClose is - find imports and usage
rg "SheetClose|from.*sheet" src/components/shared/MobileMenu.tsx -A 2 -B 2Repository: Leadlly/leadlly.in
Length of output: 493
🌐 Web query:
Base UI Dialog Close component render API documentation
💡 Result:
The Base UI Dialog Close component render API allows fully customizing the rendered element via the render prop. ## Key Details - Type: ReactElement | ((props: HTMLProps, state: Dialog.Close.State) => ReactElement) | undefined - Description: Accepts a ReactElement or a function that returns the element to render. The function receives props (HTML attributes) and state (including isOpen: boolean). - Default Behavior: Renders a native element. - Related Prop: nativeButton (boolean, default true) - Determines if a native is rendered when using the render prop. Set to false if rendering a non-button element (e.g.,
Citations:
🏁 Script executed:
# Find the sheet component file
fd -t f "sheet\." src/components/ui/Repository: Leadlly/leadlly.in
Length of output: 89
🏁 Script executed:
# Check the content of the sheet component
cat src/components/ui/sheet.tsx | head -150Repository: Leadlly/leadlly.in
Length of output: 4580
Avoid rendering a close button inside each link.
SheetClose renders a native <button> by default, creating invalid <a><button>…</button></a> markup. This can also trigger the close action without navigating the link. Use Base UI's render prop with nativeButton={false} to render the close control as the anchor itself.
Proposed fix
{menuItems.map((item, index) => (
- <motion.a
+ <SheetClose
key={item.href}
- href={item.href}
- initial="hidden"
- animate="visible"
- transition={{ delay: index * 0.2, type: "spring", stiffness: 50 }}
- variants={itemVariants}
- className="mb-8 block text-center text-xl font-semibold text-black/50 hover:text-black focus:text-black"
- >
- <SheetClose>{item.label}</SheetClose>
- </motion.a>
+ nativeButton={false}
+ render={(props) => (
+ <motion.a
+ {...props}
+ href={item.href}
+ initial="hidden"
+ animate="visible"
+ transition={{ delay: index * 0.2, type: "spring", stiffness: 50 }}
+ variants={itemVariants}
+ className="mb-8 block text-center text-xl font-semibold text-black/50 hover:text-black focus:text-black"
+ >
+ {item.label}
+ </motion.a>
+ )}
+ />
))}📝 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.
| {menuItems.map((item, index) => ( | |
| <motion.a | |
| key={item.name} | |
| key={item.href} | |
| href={item.href} | |
| initial="hidden" | |
| animate="visible" | |
| transition={{ delay: index * 0.2, type: "spring", stiffness: 50 }} | |
| variants={itemVariants} | |
| className="mb-8 block text-center text-xl font-semibold text-black/50 hover:text-black focus:text-black" | |
| > | |
| <SheetClose>{item.name}</SheetClose> | |
| <SheetClose>{item.label}</SheetClose> | |
| </motion.a> | |
| {menuItems.map((item, index) => ( | |
| <SheetClose | |
| key={item.href} | |
| nativeButton={false} | |
| render={(props) => ( | |
| <motion.a | |
| {...props} | |
| href={item.href} | |
| initial="hidden" | |
| animate="visible" | |
| transition={{ delay: index * 0.2, type: "spring", stiffness: 50 }} | |
| variants={itemVariants} | |
| className="mb-8 block text-center text-xl font-semibold text-black/50 hover:text-black focus:text-black" | |
| > | |
| {item.label} | |
| </motion.a> | |
| )} | |
| /> | |
| ))} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/shared/MobileMenu.tsx` around lines 55 - 66, The current map
renders a SheetClose button inside an anchor (in the menuItems.map where
motion.a is used and itemVariants applied), producing invalid <a><button/></a>
markup; change this by removing the nested SheetClose and instead use
SheetClose's render prop with nativeButton={false} so the close control renders
as the anchor itself—move href, className, animation props/variants from
motion.a into the SheetClose render output (or have SheetClose render the motion
element) so the element returned is a single anchor that both navigates and
closes the sheet.
| export const NavLinks = [ | ||
| { label: "Plans", href: "#plans" }, | ||
| { label: "Reviews", href: "#reviews" }, | ||
| { label: "FAQs", href: "#faqs" }, | ||
| { label: "Become a mentor", href: "#become-a-mentor" }, | ||
| ]; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Compare NavLinks hash hrefs against ids declared in TSX/JSX files.
set -euo pipefail
python - <<'PY'
import re
from pathlib import Path
constants = Path("src/helpers/constants/index.ts").read_text(encoding="utf-8")
hrefs = re.findall(r'href:\s*"([^"]+)"', constants)
hashes = [h[1:] for h in hrefs if h.startswith("#")]
ids = set()
for path in list(Path(".").rglob("*.tsx")) + list(Path(".").rglob("*.jsx")):
if "node_modules" in path.parts:
continue
text = path.read_text(encoding="utf-8", errors="ignore")
ids.update(re.findall(r'id=["\']([^"\']+)["\']', text))
missing = [target for target in hashes if target not in ids]
print("NavLink hash targets:", hashes)
print("Found ids:", sorted(ids))
if missing:
raise SystemExit(f"Missing target ids: {missing}")
PYRepository: Leadlly/leadlly.in
Length of output: 584
Add missing anchor IDs on the landing page to match NavLinks targets.
All four navigation hash targets are missing from the codebase: #plans, #reviews, #faqs, and #become-a-mentor. The navigation will not function until these corresponding sections are added with matching id attributes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/helpers/constants/index.ts` around lines 19 - 24, NavLinks defines four
hash targets (`#plans`, `#reviews`, `#faqs`, `#become-a-mentor`) but the landing page
lacks matching section IDs; add corresponding elements with id="plans",
id="reviews", id="faqs", and id="become-a-mentor" in the landing page component
(or layout) so the NavLinks array works correctly — ensure each section is a
semantic container (e.g., <section> or equivalent in your framework) with
readable headings and content, and verify the IDs exactly match the strings in
NavLinks.
| "**/*.tsx", | ||
| ".next/types/**/*.ts", | ||
| "src/components/shared/dw" | ||
| "src/components/shared/dw", |
There was a problem hiding this comment.
Suspicious include entry: src/components/shared/dw.
This path looks like an accidental/truncated entry (no such directory referenced elsewhere in the PR). TypeScript will silently ignore non-existent includes, but it's dead config. Please remove it or replace with the intended path.
Proposed fix
".next/types/**/*.ts",
- "src/components/shared/dw",
".next/dev/types/**/*.ts"📝 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.
| "src/components/shared/dw", | |
| ".next/types/**/*.ts", | |
| ".next/dev/types/**/*.ts" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tsconfig.json` at line 36, Remove the suspicious include entry
"src/components/shared/dw" from the tsconfig.json includes array (or replace it
with the correct intended path), since it's a non-existent/truncated path;
locate the "includes" array that contains "src/components/shared/dw" and either
delete that element or correct it to the proper directory (e.g., the intended
shared component folder) so TypeScript doesn't carry dead config.
| *. env | ||
| .env | ||
| .env .* No newline at end of file |
There was a problem hiding this comment.
Invalid .env ignore patterns due to incorrect spacing. Lines 16 and 18 contain spaces that break the glob patterns (*. env and .env .*), which means .env files and their variants may NOT be properly ignored by Prettier. This creates a security risk where environment files containing sensitive credentials (API keys, database passwords, secrets) might be formatted and accidentally committed to version control.
Confidence: 5/5
Suggested Fix
| *. env | |
| .env | |
| .env .* | |
| *.env | |
| .env | |
| .env.* | |
Remove the spaces in the glob patterns:
- Line 16: Change
*. envto*.env(remove space before.env) - Line 18: Change
.env .*to.env.*(remove space after.env)
This ensures all.envfiles and their variants (.env.local,.env.production, etc.) are properly ignored by Prettier, preventing accidental exposure of sensitive credentials.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In .prettierignore at lines 16-18, there are invalid glob patterns for .env files due to incorrect spacing. On line 16, change "*. env" to "*.env" (remove the space between * and .env). On line 18, change ".env .*" to ".env.*" (remove the space between .env and .*). These spaces break the ignore patterns and create a security risk where environment files containing sensitive credentials might not be properly ignored by Prettier, potentially leading to accidental commits of secrets to version control.
📍 This suggestion applies to lines 16-18
Summary by BeetleThis PR introduces a comprehensive refactoring of the home page layout with a focus on creating reusable shared components and improving the overall design system. The changes establish a more maintainable component architecture by extracting common patterns into shared components ( 📁 File Changes Summary
Total Changes: 10 files changed, +388 additions, -231 deletions 🗺️ Walkthroughgraph TD
A["Home Page Layout"] --> B["SectionContainer Wrapper"]
B --> C["Container Component"]
C --> D["Content Layout"]
D --> E["Hero Section"]
D --> F["Planner Section"]
D --> G["Marquee Section"]
E --> H["LinkButton Component"]
F --> H
G --> I["CSS Scroll Animation"]
I --> J["Duplicate Items for Loop"]
I --> K["Configurable Speed/Direction"]
L["Global Styles"] --> M["Font Variable Fix"]
M --> N["--font-serif"]
L --> O["Marquee Keyframes"]
O --> P["@keyframes scroll"]
P --> Q["transform: translate"]
R["Shared Components"] --> S["Container Max-width + Border"]
R --> T["SectionContainer Padding Wrapper"]
R --> U["LinkButton Styled Link + Icon"]
style R fill:#e1f5ff
style L fill:#fff4e1
style A fill:#f0e1ff
🎯 Key Changes
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
✅ You're good to merge this PR! No issues found. Great job! Settings⚙️ SettingsSeverity Threshold: 📖 User Guide
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
src/components/Home/Hero.tsx (2)
33-35:⚠️ Potential issue | 🟠 MajorReplace the placeholder CTA destination.
href="#"is still a no-op; this was flagged previously and remains in the new code. Point to the real login/signup route (or accept via prop).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Hero.tsx` around lines 33 - 35, The CTA in Hero uses a placeholder href="#" on the LinkButton which is a no-op; update the LinkButton in the Hero component to point to the real auth route (e.g., "/login" or "/signup") or make the target configurable by adding a prop (e.g., ctaHref) to the Hero component and passing it into LinkButton, replacing the hardcoded href="#" reference so navigation works correctly.
45-50:⚠️ Potential issue | 🟡 MinorAdd
sizesto thefillimage for responsive optimization.
next/imagewithfillrequires asizesprop to avoid downloading the full-resolution source on every viewport and to silence Next.js's runtime warning. Mobile height is now provided by the parenth-screen md:h-[636px], so onlysizesremains.🛠️ Proposed fix
<Image src="/assets/illustrations/svg_5.svg" alt="hero-illustration" fill + sizes="(min-width: 768px) 50vw, 100vw" className="object-contain" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Hero.tsx` around lines 45 - 50, The Image component using the fill layout (Image) is missing a sizes prop which causes Next.js to download full-resolution images and emit a runtime warning; add a sizes prop to the Image element (the one with src "/assets/illustrations/svg_5.svg" and className "object-contain") that reflects your responsive layout—for example a string like "(max-width: 767px) 100vw, 50vw" or "(max-width: 768px) 100vw, 768px"—so the browser requests appropriately sized images and the Next.js warning is suppressed.src/app/globals.css (1)
121-122:⚠️ Potential issue | 🟡 Minor
--tracking-normaland--spacingunder:rootstill don't feed Tailwind v4's theme.Tailwind v4 only reads tokens declared inside
@theme/@theme inline. These two variables remain under:rootand are not mirrored into the@theme inlineblock, so they don't customize any utility scale. Either move them into@theme(or mirror--spacing: var(--spacing);inside@theme inline) or remove them.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/globals.css` around lines 121 - 122, The CSS variables --tracking-normal and --spacing are defined under :root but Tailwind v4 only reads tokens inside `@theme` / `@theme` inline, so mirror or move them into the theme block; update the file by placing --tracking-normal and --spacing inside the existing `@theme` inline (or move them from :root into `@theme`) so they feed Tailwind's theme tokens (refer to the :root declaration and the `@theme` inline block when making the change).
🧹 Nitpick comments (2)
src/components/shared/LinkButton.tsx (2)
43-53: Mark the decorative chevron icon as aria-hidden.The chevron is purely decorative (the link text already communicates intent), but the wrapper
<span>and the icon are both exposed to assistive tech. Addaria-hidden="true"to avoid duplicate/noisy announcements.♿ Proposed fix
<span + aria-hidden="true" className={cn( "size-10 bg-white/65 flex items-center justify-center rounded-full", iconContainerClassName, )} > <HugeiconsIcon icon={ChevronRight} className={cn("text-primary-foreground size-6", iconClassName)} /> </span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/LinkButton.tsx` around lines 43 - 53, The decorative chevron in LinkButton.tsx is currently exposed to assistive tech; update the span wrapper (the one rendering the chevron) and/or the HugeiconsIcon usage (icon={ChevronRight}) to include aria-hidden="true" so the chevron is not announced by screen readers; ensure you add the attribute to the span or the HugeiconsIcon props (where HugeiconsIcon accepts passthrough attributes) so the decorative icon is ignored by AT.
34-38: Height is hard-coded —sizeprop effectively has no effect on height.
buttonVariants({ size })emits anh-*class, but the component immediately appendsh-14, which wins via Tailwind's last-class-wins merge throughcn. Any consumer passingsize="sm"/size="lg"will see no height change. Consider either dropping thesizeprop from the public API (since this is a fixed-size CTA) or removing the hard-codedh-14/text-*sosizeactually takes effect.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/LinkButton.tsx` around lines 34 - 38, The LinkButton component currently overrides the size-controlled height by appending the hard-coded "h-14" and text classes after buttonVariants({ size, variant }), so pass-through size has no effect; to fix, remove the hard-coded "h-14" and related "text-*" class modifiers from the className composition in LinkButton.tsx so buttonVariants({ size }) can control height and text sizing, or alternatively remove the size prop from the LinkButton public API and update usages and types to reflect a fixed-size CTA (adjust buttonVariants calls and prop definitions accordingly); reference the LinkButton component, the size prop, and buttonVariants to locate where to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Home/Hero.tsx`:
- Line 18: Replace the rigid h-screen on the hero Container with a responsive
minimum/explicit height so the section doesn’t overflow when the parent adds top
padding; in src/components/Home/Hero.tsx target the Container element and swap
the h-screen class for a mobile-safe height such as min-h-[calc(100svh-9rem)]
(or an explicit mobile height like h-[520px]) while keeping md:h-[636px], so
mobile uses the calculated min-height and desktop retains the fixed md height.
In `@src/components/Home/Marquee.tsx`:
- Around line 180-198: The addAnimation effect currently clones and appends
children every time getDirection or getSpeed changes, causing exponential
duplication; modify the logic in the useEffect that calls addAnimation to run
the cloning step only once by introducing a guard (e.g., a ref like
hasClonedRef) or split responsibilities into two effects so that cloning of
scrollerRef.children (inside addAnimation) happens only when the component
mounts, while getDirection/getSpeed and setStart remain in a separate effect
triggered by their callbacks; reference addAnimation, containerRef, scrollerRef,
getDirection, getSpeed, and setStart when making the change.
- Around line 204-214: In the Marquee component update the Tailwind class
conditional that uses the non-existent "hover:paused" utility: inside the ul's
className cn call (where you currently have start && "animate-scroll",
pauseOnHover && "hover:paused") replace the "hover:paused" string with the
arbitrary-property variant "hover:[animation-play-state:paused]" so the
pauseOnHover branch applies a valid Tailwind hover animation-play-state; this
change is in the same cn call that references scrollerRef and the
"animate-scroll" class.
---
Duplicate comments:
In `@src/app/globals.css`:
- Around line 121-122: The CSS variables --tracking-normal and --spacing are
defined under :root but Tailwind v4 only reads tokens inside `@theme` / `@theme`
inline, so mirror or move them into the theme block; update the file by placing
--tracking-normal and --spacing inside the existing `@theme` inline (or move them
from :root into `@theme`) so they feed Tailwind's theme tokens (refer to the :root
declaration and the `@theme` inline block when making the change).
In `@src/components/Home/Hero.tsx`:
- Around line 33-35: The CTA in Hero uses a placeholder href="#" on the
LinkButton which is a no-op; update the LinkButton in the Hero component to
point to the real auth route (e.g., "/login" or "/signup") or make the target
configurable by adding a prop (e.g., ctaHref) to the Hero component and passing
it into LinkButton, replacing the hardcoded href="#" reference so navigation
works correctly.
- Around line 45-50: The Image component using the fill layout (Image) is
missing a sizes prop which causes Next.js to download full-resolution images and
emit a runtime warning; add a sizes prop to the Image element (the one with src
"/assets/illustrations/svg_5.svg" and className "object-contain") that reflects
your responsive layout—for example a string like "(max-width: 767px) 100vw,
50vw" or "(max-width: 768px) 100vw, 768px"—so the browser requests appropriately
sized images and the Next.js warning is suppressed.
---
Nitpick comments:
In `@src/components/shared/LinkButton.tsx`:
- Around line 43-53: The decorative chevron in LinkButton.tsx is currently
exposed to assistive tech; update the span wrapper (the one rendering the
chevron) and/or the HugeiconsIcon usage (icon={ChevronRight}) to include
aria-hidden="true" so the chevron is not announced by screen readers; ensure you
add the attribute to the span or the HugeiconsIcon props (where HugeiconsIcon
accepts passthrough attributes) so the decorative icon is ignored by AT.
- Around line 34-38: The LinkButton component currently overrides the
size-controlled height by appending the hard-coded "h-14" and text classes after
buttonVariants({ size, variant }), so pass-through size has no effect; to fix,
remove the hard-coded "h-14" and related "text-*" class modifiers from the
className composition in LinkButton.tsx so buttonVariants({ size }) can control
height and text sizing, or alternatively remove the size prop from the
LinkButton public API and update usages and types to reflect a fixed-size CTA
(adjust buttonVariants calls and prop definitions accordingly); reference the
LinkButton component, the size prop, and buttonVariants to locate where to
change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aa392314-3499-4b7f-8d58-bd3d57e48b23
⛔ Files ignored due to path filters (6)
public/assets/illustrations/svg_1.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_2.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_3.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_4.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_5.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_6.svgis excluded by!**/*.svg
📒 Files selected for processing (10)
src/app/(root)/(home)/page.tsxsrc/app/globals.csssrc/app/layout.tsxsrc/components/Home/Hero.tsxsrc/components/Home/Marquee.tsxsrc/components/Home/Planner.tsxsrc/components/shared/Container.tsxsrc/components/shared/LinkButton.tsxsrc/components/shared/NavBar.tsxsrc/components/shared/SectionContainer.tsx
✅ Files skipped from review due to trivial changes (1)
- src/components/shared/SectionContainer.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/app/(root)/(home)/page.tsx
- src/components/Home/Planner.tsx
- src/app/layout.tsx
| Say goodbye to one-size-fits-all! We tailor study plans and | ||
| resources to your individual learning style and goals. | ||
| <SectionContainer className="relative pt-36"> | ||
| <Container className="h-screen md:h-[636px] bg-[url(/assets/images/hero-bg.svg)] bg-no-repeat bg-size-[auto_300px] md:bg-size-[auto_493px] bg-left flex flex-col md:flex-row items-center justify-between gap-5"> |
There was a problem hiding this comment.
h-screen on the hero container may cause overflow/viewport issues on mobile.
Combining h-screen with pt-36 on the section means the hero is forced to exactly 100vh while still getting 9rem top padding from the section, pushing content below the fold and likely clipping the CTA on shorter mobile viewports (and mobile browsers that don't account for URL bar). Consider min-h-[calc(100svh-9rem)] or an explicit mobile height that matches the content, similar to the md:h-[636px] fixed value used on desktop.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Hero.tsx` at line 18, Replace the rigid h-screen on the
hero Container with a responsive minimum/explicit height so the section doesn’t
overflow when the parent adds top padding; in src/components/Home/Hero.tsx
target the Container element and swap the h-screen class for a mobile-safe
height such as min-h-[calc(100svh-9rem)] (or an explicit mobile height like
h-[520px]) while keeping md:h-[636px], so mobile uses the calculated min-height
and desktop retains the fixed md height.
| useEffect(() => { | ||
| function addAnimation() { | ||
| if (containerRef.current && scrollerRef.current) { | ||
| const scrollerContent = Array.from(scrollerRef.current.children); | ||
|
|
||
| scrollerContent.forEach((item) => { | ||
| const duplicatedItem = item.cloneNode(true); | ||
| if (scrollerRef.current) { | ||
| scrollerRef.current.appendChild(duplicatedItem); | ||
| } | ||
| }); | ||
|
|
||
| getDirection(); | ||
| getSpeed(); | ||
| setStart(true); | ||
| } | ||
| } | ||
| addAnimation(); | ||
| }, [getDirection, getSpeed]); |
There was a problem hiding this comment.
Children are duplicated on every direction/speed change, compounding indefinitely.
addAnimation unconditionally clones every current child and appends them to the scroller. Because getDirection/getSpeed are wrapped in useCallback with direction/speed deps, they get new identities whenever those props change, which re-runs the effect and appends another full copy of the children. Use a ref/guard so the clone step runs only once (or split direction/speed into a separate effect).
🛠️ Proposed fix
+ const hasCloned = React.useRef(false);
+
useEffect(() => {
function addAnimation() {
- if (containerRef.current && scrollerRef.current) {
+ if (!hasCloned.current && containerRef.current && scrollerRef.current) {
const scrollerContent = Array.from(scrollerRef.current.children);
scrollerContent.forEach((item) => {
const duplicatedItem = item.cloneNode(true);
if (scrollerRef.current) {
scrollerRef.current.appendChild(duplicatedItem);
}
});
-
- getDirection();
- getSpeed();
- setStart(true);
+ hasCloned.current = true;
+ setStart(true);
}
+ getDirection();
+ getSpeed();
}
addAnimation();
}, [getDirection, getSpeed]);📝 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.
| useEffect(() => { | |
| function addAnimation() { | |
| if (containerRef.current && scrollerRef.current) { | |
| const scrollerContent = Array.from(scrollerRef.current.children); | |
| scrollerContent.forEach((item) => { | |
| const duplicatedItem = item.cloneNode(true); | |
| if (scrollerRef.current) { | |
| scrollerRef.current.appendChild(duplicatedItem); | |
| } | |
| }); | |
| getDirection(); | |
| getSpeed(); | |
| setStart(true); | |
| } | |
| } | |
| addAnimation(); | |
| }, [getDirection, getSpeed]); | |
| const hasCloned = React.useRef(false); | |
| useEffect(() => { | |
| function addAnimation() { | |
| if (!hasCloned.current && containerRef.current && scrollerRef.current) { | |
| const scrollerContent = Array.from(scrollerRef.current.children); | |
| scrollerContent.forEach((item) => { | |
| const duplicatedItem = item.cloneNode(true); | |
| if (scrollerRef.current) { | |
| scrollerRef.current.appendChild(duplicatedItem); | |
| } | |
| }); | |
| hasCloned.current = true; | |
| setStart(true); | |
| } | |
| getDirection(); | |
| getSpeed(); | |
| } | |
| addAnimation(); | |
| }, [getDirection, getSpeed]); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Marquee.tsx` around lines 180 - 198, The addAnimation
effect currently clones and appends children every time getDirection or getSpeed
changes, causing exponential duplication; modify the logic in the useEffect that
calls addAnimation to run the cloning step only once by introducing a guard
(e.g., a ref like hasClonedRef) or split responsibilities into two effects so
that cloning of scrollerRef.children (inside addAnimation) happens only when the
component mounts, while getDirection/getSpeed and setStart remain in a separate
effect triggered by their callbacks; reference addAnimation, containerRef,
scrollerRef, getDirection, getSpeed, and setStart when making the change.
| "scroller relative z-20 overflow-hidden mask-[linear-gradient(to_right,transparent,white_20%,white_80%,transparent)]", | ||
| className, | ||
| )} | ||
| > | ||
| {" "} | ||
| {items.map((item, index) => ( | ||
| <div | ||
| key={index} | ||
| className="flex flex-grow items-center gap-3 md:gap-5 pr-14 md:pr-20" | ||
| > | ||
| <div className="size-5"> | ||
| <item.icon className="size-full stroke-[#2B2B2B]" /> | ||
| </div> | ||
| <span className="font-medium text-xl text-[#2B2B2B] text-nowrap"> | ||
| {item.label} | ||
| </span> | ||
| </div> | ||
| ))} | ||
| </motion.div> | ||
| <ul | ||
| ref={scrollerRef} | ||
| className={cn( | ||
| "flex w-max min-w-full shrink-0 flex-nowrap gap-4 py-4", | ||
| start && "animate-scroll", | ||
| pauseOnHover && "hover:paused", | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Tailwind CSS v4 mask-[] arbitrary value utility and paused animation-play-state utility support
💡 Result:
Tailwind CSS v4 mask-[] arbitrary value utility and paused animation-play-state utility support 1) mask-[] arbitrary values (Tailwind v4) - Yes. Tailwind provides mask-image utilities that support arbitrary values using bracket syntax. - The official mask-image docs show “Use the mask-[] syntax to set the mask image of an element” and give the custom-value example via mask-linear-[…]. Example: mask-linear-[70deg,transparent_10%,black,transparent_80%] - Tailwind also documents a dedicated API (mask--from-/mask--to- etc.) specifically for gradients/side masking, and notes that arbitrary mask-* utilities can override the entire mask-image definition. 2) animation-play-state paused utility (Tailwind v4) - Tailwind core does not expose a built-in animation-play-state: paused utility (e.g., there is no documented/standard animation-play-state: paused utility in Tailwind’s official v4 docs for animation). - The Tailwind team has community discussions proposing animation-play-state utilities (running/paused), implying it’s not part of the core set. - Workarounds: use bracket arbitrary property syntax to directly set the property, or define a custom utility in your Tailwind CSS. - Bracket form example: [animation-play-state:paused] (and then combine with variants like hover:…) is commonly used. - Custom utility example: define an @utility or utility class that sets animation-play-state: paused; then use it with variants. - Note: third-party Tailwind plugins exist that provide running/paused/play-state-* utilities, but those are not Tailwind core. Answer summary - mask-[] arbitrary value utilities: supported in Tailwind v4 via mask-image “mask-[]” / mask-linear-[…] syntax. - animation-play-state paused utility: not present as a built-in Tailwind v4 core utility; use arbitrary property syntax or define your own / use a plugin.
Citations:
- 1: https://tailwindcss.com/docs/mask-image
- 2: Add new
mask-imageutilities for Tailwind CSS v4.1 tailwindlabs/tailwindcss#17134 - 3: https://tailwindcss.com/blog/tailwindcss-v4-1
- 4: animation-play-state: paused tailwindlabs/tailwindcss#3654
- 5: `animation-play-state` utilities tailwindlabs/tailwindcss#13774
- 6: https://tailwindcss.com/docs/animation
- 7: https://github.com/jamiebuilds/tailwindcss-animate/blob/main/docs/animation-play-state.md
Use hover:[animation-play-state:paused] instead of hover:paused.
The hover:paused utility doesn't exist in Tailwind v4. While mask-[linear-gradient(...)] is a valid arbitrary-value utility, the animation play-state variant is not part of Tailwind's core utilities. Use the arbitrary property syntax: hover:[animation-play-state:paused].
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Marquee.tsx` around lines 204 - 214, In the Marquee
component update the Tailwind class conditional that uses the non-existent
"hover:paused" utility: inside the ul's className cn call (where you currently
have start && "animate-scroll", pauseOnHover && "hover:paused") replace the
"hover:paused" string with the arbitrary-property variant
"hover:[animation-play-state:paused]" so the pauseOnHover branch applies a valid
Tailwind hover animation-play-state; this change is in the same cn call that
references scrollerRef and the "animate-scroll" class.
Summary by BeetleThis PR introduces a comprehensive redesign of the Tracker component with a modern, visually striking layout featuring custom SVG-based card shapes and enhanced animation capabilities. The changes transform a simple progress tracking display into an engaging, multi-section feature showcase that emphasizes student execution and structured learning. The component now uses a sophisticated layout with custom-shaped SVG cards (stepped L-shapes and tabbed rectangles) to present key value propositions about mentorship, progress tracking, and revision timing. 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 2 files changed, +186 additions, -76 deletions 🗺️ Walkthrough:graph TD
A["Tracker Component"] --> B["SectionContainer with Background"]
B --> C["Top Section: Flex Layout"]
B --> D["Bottom Section: Grid Layout"]
C --> E["Left: Heading Block"]
C --> F["Right: TopCard SVG"]
E --> E1["Reveal: Main Heading"]
E --> E2["Reveal: Subheading"]
F --> F1["Custom Stepped L-Shape"]
F1 --> F2["Speech Bubble Content"]
F1 --> F3["Title + Arrow Button"]
D --> G["Feature Card 1"]
D --> H["Feature Card 2"]
D --> I["Feature Card 3"]
G --> G1["BottomCard SVG"]
H --> H1["BottomCard SVG"]
I --> I1["BottomCard SVG"]
G1 --> G2["Tabbed Rectangle Shape"]
G2 --> G3["Title + Description"]
G2 --> G4["Arrow Button in Tab"]
J["Reveal Component"] -.->|"Wraps all animated elements"| E1
J -.->|"Enhanced with className prop"| E2
J -.->|"Staggered delays"| F
J -.->|"Cascade animation"| G
style A fill:#4F46E5,stroke:#312E81,color:#fff
style F1 fill:#7C3AED,stroke:#5B21B6,color:#fff
style G2 fill:#7C3AED,stroke:#5B21B6,color:#fff
style J fill:#10B981,stroke:#047857,color:#fff
🎯 Key Changes:
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
✅ You're good to merge this PR! No issues found. Great job! Settings⚙️ SettingsSeverity Threshold: 📖 User Guide
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/components/shared/Reveal.tsx (1)
27-33: Minor:elsebranch is effectively dead withonce: true.Since
useInViewis now{ once: true },isInViewtransitions fromfalse→trueand never back. Combined withinitial="hidden"on themotion.div(Line 57), thecontrols.start("hidden")call only runs on initial mount and is redundant. You can simplify to:♻️ Proposed simplification
useEffect(() => { if (isInView) { controls.start("visible"); - } else { - controls.start("hidden"); } - }, [isInView, controls]); + }, [isInView, controls]);Not a functional bug — just removes a no-op path now that the viewport trigger fires only once.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/Reveal.tsx` around lines 27 - 33, The useEffect currently toggles controls between "visible" and "hidden" based on isInView, but with useInView configured as { once: true } and motion.div already using initial="hidden", the else branch is redundant; simplify by removing the else branch and only call controls.start("visible") when isInView is true (update the useEffect referencing isInView and controls), leaving the initial="hidden" on the motion.div to handle the initial state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/components/shared/Reveal.tsx`:
- Around line 27-33: The useEffect currently toggles controls between "visible"
and "hidden" based on isInView, but with useInView configured as { once: true }
and motion.div already using initial="hidden", the else branch is redundant;
simplify by removing the else branch and only call controls.start("visible")
when isInView is true (update the useEffect referencing isInView and controls),
leaving the initial="hidden" on the motion.div to handle the initial state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c3a211b5-d027-4a4e-a5d9-cadc9b47ea47
⛔ Files ignored due to path filters (1)
public/assets/illustrations/svg_7.svgis excluded by!**/*.svg
📒 Files selected for processing (2)
src/components/Home/Tracker.tsxsrc/components/shared/Reveal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/Home/Tracker.tsx
Summary by BeetleThis PR implements a comprehensive redesign of the features section on the home page, transforming the presentation of key product features (Growth Meter, Mentor, Planner, Tracker) with modern animations, improved layouts, and enhanced visual hierarchy. The changes focus on creating a more engaging user experience through refined component architecture, reusable animation utilities, and responsive design patterns. 📁 File Changes Summary
Total Changes: 9 files changed, +286 additions, -288 deletions 🗺️ Walkthroughgraph TD
A["Home Page Features Section"] --> B["GrowthMeter Component"]
A --> C["Mentor Component"]
A --> D["Hero & Planner Components"]
A --> E["Tracker Component"]
B --> F["ImageAnimation Component"]
C --> F
D --> F
B --> G["Reveal Component"]
C --> G
D --> G
E --> G
F --> H["useAnimate Hook"]
F --> I["useInView Hook"]
G --> H
G --> I
B --> J["New: Execution-Focused Layout"]
J --> K["Feature List with Icons"]
J --> L["System-Based Messaging"]
C --> M["New: Interactive Feature Tabs"]
M --> N["5 Feature Categories"]
M --> O["Animated Mockup Display"]
M --> P["Expandable Descriptions"]
style F fill:#4ade80,stroke:#22c55e,stroke-width:3px
style G fill:#4ade80,stroke:#22c55e,stroke-width:3px
style J fill:#60a5fa,stroke:#3b82f6,stroke-width:2px
style M fill:#60a5fa,stroke:#3b82f6,stroke-width:2px
🎯 Key Changes
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
✅ You're good to merge this PR! No issues found. Great job! Settings⚙️ SettingsSeverity Threshold: 📖 User Guide
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/components/shared/ImageAnimation.tsx (1)
39-39: Add asizesprop fornext/imagewithfill.In Next.js 16.2.4, when using
fillwithoutsizes, the browser defaults to assuming 100vw width, causing unnecessarily large images to be downloaded, especially if the image renders smaller than full viewport width. Withoutsizes, Next.js generates a limited srcset (e.g., 1x, 2x) rather than a full responsive srcset, reducing optimization benefits.📦 Suggested fix
const ImageAnimation = ({ src, className, alt, + sizes = "(min-width: 768px) 50vw, 100vw", }: { src: string; className?: string; alt: string; + sizes?: string; }) => { ... - <Image src={src} alt={alt} fill className={cn("object-contain")} /> + <Image + src={src} + alt={alt} + fill + sizes={sizes} + className={cn("object-contain")} + />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/ImageAnimation.tsx` at line 39, The Image with fill is missing a sizes prop which causes the browser to assume 100vw and download oversized images; update the <Image src={src} alt={alt} fill className={cn("object-contain")} /> usage in ImageAnimation to include a suitable sizes prop (for example sizes="100vw" or a more accurate responsive expression like sizes="(max-width: 768px) 100vw, 50vw") so the generated srcset is optimized; ensure you add the sizes prop alongside src/alt/fill/className on the Image component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Home/GrowthMeter.tsx`:
- Around line 52-55: The CTA currently uses a placeholder href="#" in the Reveal
> LinkButton block (component LinkButton) which prevents navigation; update the
LinkButton's href to a real route or URL (for example "/start-planning" or the
app's planning route constant) or wire it to the appropriate navigation
handler/prop if LinkButton expects one, ensuring the Reveal > LinkButton
invocation navigates to the intended planning page instead of "#".
In `@src/components/Home/Mentor.tsx`:
- Around line 87-102: The conditional motion.p that renders item.description
uses an exit animation but is unmounted immediately; wrap the {item ===
activeIndex && ( ... )} block in <AnimatePresence initial={false} mode="wait">
so the exit animation can run, ensure AnimatePresence is imported from
"framer-motion", and keep the key on motion.p (key={`desc-${idx}`}) so
AnimatePresence can track the element.
- Around line 56-70: The list item currently uses motion.li with onClick (in
Mentor.tsx, the motion.li that calls setActiveIndex and compares item ===
activeIndex), which is not keyboard accessible; replace the clickable surface
with a real control (e.g., use motion.button or render a <button type="button">
inside the motion.li) or at minimum add tabIndex, role, and key handlers so
Enter/Space activate setActiveIndex and manage aria attributes (aria-pressed or
aria-selected) to reflect activeIndex; ensure focus styles remain and keep the
existing layout/animation props on the motion element so keyboard users can
toggle items the same way as mouse users.
In `@src/components/Home/Planner.tsx`:
- Around line 27-29: The primary CTA LinkButton currently uses a placeholder
href (href="/#") which keeps users on the same page; update the LinkButton
instance in Planner.tsx to point to a real route (for example "/signup",
"/pricing", or the onboarding path your app uses) or wire it to a prop/route
constant so it navigates correctly; locate the LinkButton JSX in the Planner
component (the element with props href and className="h-11 md:h-14 pl-5") and
replace the placeholder href with the appropriate real destination or a
reference to a route constant/prop.
In `@src/components/Home/Tracker.tsx`:
- Around line 69-80: The decorative Button controls inside the SVG foreignObject
are currently focusable but do nothing; replace those Button components (the
ones wrapping HugeiconsIcon / ArrowDownRightIcon) with non-interactive elements
(e.g., a div or span) so keyboard users won't tab to them, preserve the existing
className styling, and mark them as decorative by adding aria-hidden="true" (or
role="img" + an accessible name if they convey meaning). Update both occurrences
that wrap HugeiconsIcon (the ArrowDownRightIcon instances) and remove any
button-specific props so the elements are not focusable.
---
Nitpick comments:
In `@src/components/shared/ImageAnimation.tsx`:
- Line 39: The Image with fill is missing a sizes prop which causes the browser
to assume 100vw and download oversized images; update the <Image src={src}
alt={alt} fill className={cn("object-contain")} /> usage in ImageAnimation to
include a suitable sizes prop (for example sizes="100vw" or a more accurate
responsive expression like sizes="(max-width: 768px) 100vw, 50vw") so the
generated srcset is optimized; ensure you add the sizes prop alongside
src/alt/fill/className on the Image component.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 139a6d08-ebf8-49d3-b377-f5aada05c8d6
⛔ Files ignored due to path filters (6)
public/assets/illustrations/mockup_1.svgis excluded by!**/*.svgpublic/assets/illustrations/mockup_2.svgis excluded by!**/*.svgpublic/assets/illustrations/mockup_3.svgis excluded by!**/*.svgpublic/assets/illustrations/mockup_4.svgis excluded by!**/*.svgpublic/assets/illustrations/mockup_5.svgis excluded by!**/*.svgpublic/assets/illustrations/svg_8.svgis excluded by!**/*.svg
📒 Files selected for processing (9)
src/components/Home/GrowthMeter.tsxsrc/components/Home/Hero.tsxsrc/components/Home/Mentor.tsxsrc/components/Home/Planner.tsxsrc/components/Home/Tracker.tsxsrc/components/shared/Container.tsxsrc/components/shared/ImageAnimation.tsxsrc/components/shared/PricingCard.tsxsrc/components/shared/Reveal.tsx
✅ Files skipped from review due to trivial changes (1)
- src/components/shared/Container.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/shared/PricingCard.tsx
| <Reveal delay={0.7} className="z-10"> | ||
| <LinkButton href="#" className="pl-5"> | ||
| Start Planning Now | ||
| </LinkButton> |
There was a problem hiding this comment.
Use a real destination for this CTA.
Line 53 still uses href="#", so the action doesn’t navigate meaningfully.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/GrowthMeter.tsx` around lines 52 - 55, The CTA currently
uses a placeholder href="#" in the Reveal > LinkButton block (component
LinkButton) which prevents navigation; update the LinkButton's href to a real
route or URL (for example "/start-planning" or the app's planning route
constant) or wire it to the appropriate navigation handler/prop if LinkButton
expects one, ensuring the Reveal > LinkButton invocation navigates to the
intended planning page instead of "#".
| <motion.li | ||
| key={idx} | ||
| layout | ||
| onClick={() => setActiveIndex(item)} | ||
| initial={{ opacity: 0, x: -20, filter: "blur(10px)" }} | ||
| animate={{ opacity: 1, x: 0, filter: "blur(0px)" }} | ||
| transition={{ | ||
| duration: 0.4, | ||
| ease: [0.4, 0, 0.2, 1], | ||
| }} | ||
| className={cn( | ||
| "bg-background/50 backdrop-blur-lg p-4 flex gap-4 cursor-pointer overflow-hidden rounded-4xl", | ||
| item === activeIndex ? "items-start" : "items-center", | ||
| )} | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify interactive non-button patterns in the reviewed file.
rg -n -C2 'motion\.li|onClick=|onKeyDown=|role=|tabIndex=|<button' src/components/Home/Mentor.tsxRepository: Leadlly/leadlly.in
Length of output: 605
Use a real control for selection (keyboard access is currently broken).
Line 59 binds interaction to motion.li, which is not keyboard-operable by default. This blocks keyboard users from switching items.
♿ Suggested fix
- <motion.li
+ <motion.li
key={idx}
layout
- onClick={() => setActiveIndex(item)}
initial={{ opacity: 0, x: -20, filter: "blur(10px)" }}
animate={{ opacity: 1, x: 0, filter: "blur(0px)" }}
transition={{
duration: 0.4,
ease: [0.4, 0, 0.2, 1],
}}
className={cn(
"bg-background/50 backdrop-blur-lg p-4 flex gap-4 cursor-pointer overflow-hidden rounded-4xl",
item === activeIndex ? "items-start" : "items-center",
)}
>
- <motion.span
+ <button
+ type="button"
+ onClick={() => setActiveIndex(item)}
+ aria-pressed={item === activeIndex}
+ className="flex w-full items-start gap-4 text-left"
+ >
+ <motion.span
layout
className="bg-background/20 backdrop-blur-lg rounded-full size-10 shrink-0 flex items-center justify-center"
>
<HugeiconsIcon
icon={ChevronRight}
color="var(--background)"
/>
</motion.span>
<div className="flex items-start flex-col gap-2 px-3">
...
</div>
+ </button>
</motion.li>📝 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.
| <motion.li | |
| key={idx} | |
| layout | |
| onClick={() => setActiveIndex(item)} | |
| initial={{ opacity: 0, x: -20, filter: "blur(10px)" }} | |
| animate={{ opacity: 1, x: 0, filter: "blur(0px)" }} | |
| transition={{ | |
| duration: 0.4, | |
| ease: [0.4, 0, 0.2, 1], | |
| }} | |
| className={cn( | |
| "bg-background/50 backdrop-blur-lg p-4 flex gap-4 cursor-pointer overflow-hidden rounded-4xl", | |
| item === activeIndex ? "items-start" : "items-center", | |
| )} | |
| > | |
| <motion.li | |
| key={idx} | |
| layout | |
| initial={{ opacity: 0, x: -20, filter: "blur(10px)" }} | |
| animate={{ opacity: 1, x: 0, filter: "blur(0px)" }} | |
| transition={{ | |
| duration: 0.4, | |
| ease: [0.4, 0, 0.2, 1], | |
| }} | |
| className={cn( | |
| "bg-background/50 backdrop-blur-lg p-4 flex gap-4 cursor-pointer overflow-hidden rounded-4xl", | |
| item === activeIndex ? "items-start" : "items-center", | |
| )} | |
| > | |
| <button | |
| type="button" | |
| onClick={() => setActiveIndex(item)} | |
| aria-pressed={item === activeIndex} | |
| className="flex w-full items-start gap-4 text-left" | |
| > | |
| <motion.span | |
| layout | |
| className="bg-background/20 backdrop-blur-lg rounded-full size-10 shrink-0 flex items-center justify-center" | |
| > | |
| <HugeiconsIcon | |
| icon={ChevronRight} | |
| color="var(--background)" | |
| /> | |
| </motion.span> | |
| <div className="flex items-start flex-col gap-2 px-3"> | |
| {/* ... rest of content ... */} | |
| </div> | |
| </button> | |
| </motion.li> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Mentor.tsx` around lines 56 - 70, The list item currently
uses motion.li with onClick (in Mentor.tsx, the motion.li that calls
setActiveIndex and compares item === activeIndex), which is not keyboard
accessible; replace the clickable surface with a real control (e.g., use
motion.button or render a <button type="button"> inside the motion.li) or at
minimum add tabIndex, role, and key handlers so Enter/Space activate
setActiveIndex and manage aria attributes (aria-pressed or aria-selected) to
reflect activeIndex; ensure focus styles remain and keep the existing
layout/animation props on the motion element so keyboard users can toggle items
the same way as mouse users.
| {item === activeIndex && ( | ||
| <motion.p | ||
| key={`desc-${idx}`} | ||
| initial={{ opacity: 0, y: -10 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| exit={{ opacity: 0, y: 10 }} | ||
| transition={{ | ||
| duration: 0.25, | ||
| delay: 0.25, | ||
| ease: "easeOut", | ||
| }} | ||
| className="text-background text-sm md:text-base font-medium max-w-96" | ||
| > | ||
| {item.description} | ||
| </motion.p> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Motion for React require wrapping conditionally removed elements in AnimatePresence for their exit animations to run?
💡 Result:
Yes, Motion for React requires wrapping conditionally removed elements in AnimatePresence for their exit animations to run. Without AnimatePresence, elements unmount immediately without animating the exit prop. AnimatePresence detects when direct children (motion components with unique keys) are removed from the React tree and triggers their exit animations before DOM removal.
Citations:
- 1: https://motion.dev/docs/react-animate-presence
- 2: https://motion.dev/tutorials/react-exit-animation
- 3: [BUG]
AnimatePresencekeeps exiting children in DOM during rapid updates with dynamic variants motiondivision/motion#3541 - 4: https://www.mintlify.com/motiondivision/motion/react/animate-presence
🏁 Script executed:
cat -n src/components/Home/Mentor.tsxRepository: Leadlly/leadlly.in
Length of output: 6488
Wrap the conditional description in AnimatePresence to enable the exit animation.
The description at lines 87–102 defines an exit animation but will unmount immediately without AnimatePresence. Add <AnimatePresence initial={false} mode="wait"> around the conditional block so the exit transition triggers when the condition becomes false.
🎞️ Suggested fix
<AnimatePresence initial={false} mode="wait">
{item === activeIndex && (
<motion.p
key={`desc-${idx}`}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{
duration: 0.25,
delay: 0.25,
ease: "easeOut",
}}
className="text-background text-sm md:text-base font-medium max-w-96"
>
{item.description}
</motion.p>
)}
</AnimatePresence>📝 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.
| {item === activeIndex && ( | |
| <motion.p | |
| key={`desc-${idx}`} | |
| initial={{ opacity: 0, y: -10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: 10 }} | |
| transition={{ | |
| duration: 0.25, | |
| delay: 0.25, | |
| ease: "easeOut", | |
| }} | |
| className="text-background text-sm md:text-base font-medium max-w-96" | |
| > | |
| {item.description} | |
| </motion.p> | |
| )} | |
| <AnimatePresence initial={false} mode="wait"> | |
| {item === activeIndex && ( | |
| <motion.p | |
| key={`desc-${idx}`} | |
| initial={{ opacity: 0, y: -10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: 10 }} | |
| transition={{ | |
| duration: 0.25, | |
| delay: 0.25, | |
| ease: "easeOut", | |
| }} | |
| className="text-background text-sm md:text-base font-medium max-w-96" | |
| > | |
| {item.description} | |
| </motion.p> | |
| )} | |
| </AnimatePresence> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Mentor.tsx` around lines 87 - 102, The conditional
motion.p that renders item.description uses an exit animation but is unmounted
immediately; wrap the {item === activeIndex && ( ... )} block in
<AnimatePresence initial={false} mode="wait"> so the exit animation can run,
ensure AnimatePresence is imported from "framer-motion", and keep the key on
motion.p (key={`desc-${idx}`}) so AnimatePresence can track the element.
| <foreignObject x="475" y="200" width="100" height="100"> | ||
| <Button | ||
| variant={"default"} | ||
| size={"icon-lg"} | ||
| className="size-24 rounded-full border border-background" | ||
| > | ||
| <HugeiconsIcon | ||
| icon={ArrowDownRightIcon} | ||
| className="text-primary-foreground size-10" | ||
| /> | ||
| </Button> | ||
| </foreignObject> |
There was a problem hiding this comment.
Replace decorative Button controls with non-interactive elements.
Line 70 and Line 129 render focusable buttons with no action. Keyboard users can tab to controls that do nothing.
♿ Suggested fix
- <foreignObject x="475" y="200" width="100" height="100">
- <Button
- variant={"default"}
- size={"icon-lg"}
- className="size-24 rounded-full border border-background"
- >
- <HugeiconsIcon
- icon={ArrowDownRightIcon}
- className="text-primary-foreground size-10"
- />
- </Button>
- </foreignObject>
+ <foreignObject x="475" y="200" width="100" height="100">
+ <div
+ aria-hidden="true"
+ className="size-24 rounded-full border border-background grid place-items-center pointer-events-none"
+ >
+ <HugeiconsIcon
+ icon={ArrowDownRightIcon}
+ className="text-primary-foreground size-10"
+ />
+ </div>
+ </foreignObject>
...
- <foreignObject x="320" y="220" width="100" height="100">
- <Button
- size={"icon-lg"}
- className="size-20 rounded-full border border-background"
- >
- <HugeiconsIcon
- icon={ArrowDownRightIcon}
- className="text-primary-foreground size-10"
- />
- </Button>
- </foreignObject>
+ <foreignObject x="320" y="220" width="100" height="100">
+ <div
+ aria-hidden="true"
+ className="size-20 rounded-full border border-background grid place-items-center pointer-events-none"
+ >
+ <HugeiconsIcon
+ icon={ArrowDownRightIcon}
+ className="text-primary-foreground size-10"
+ />
+ </div>
+ </foreignObject>Also applies to: 128-138
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/Tracker.tsx` around lines 69 - 80, The decorative Button
controls inside the SVG foreignObject are currently focusable but do nothing;
replace those Button components (the ones wrapping HugeiconsIcon /
ArrowDownRightIcon) with non-interactive elements (e.g., a div or span) so
keyboard users won't tab to them, preserve the existing className styling, and
mark them as decorative by adding aria-hidden="true" (or role="img" + an
accessible name if they convey meaning). Update both occurrences that wrap
HugeiconsIcon (the ArrowDownRightIcon instances) and remove any button-specific
props so the elements are not focusable.
Summary by BeetleThis PR enhances the home page with three new informational sections designed to improve user engagement and conversion. The changes introduce a "Why Leadlly" section highlighting the platform's core differentiators (Consistency, Accountability, Personalisation), an "Ask AI" section that encourages users to validate Leadlly through popular AI assistants, and updates the Mentor section's Error Book description for better clarity. These additions create a more comprehensive landing page experience that addresses user concerns and showcases the platform's unique value proposition. 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 4 files changed, +156 additions, -1 deletion 🗺️ Walkthrough:graph TD
A["Home Page Entry"] --> B["Hero Section"]
B --> C["Existing Features"]
C --> D["Mentor Section"]
D --> E["WhyLeadlly Component"]
E --> F["AskAI Component"]
F --> G["Testimonials"]
E --> E1["Consistency Message"]
E --> E2["Accountability Message"]
E --> E3["Personalisation Message"]
F --> F1["ChatGPT Button"]
F --> F2["Perplexity Button"]
F --> F3["Claude Button"]
F1 -.->|"Pre-filled query"| F4["External AI Validation"]
F2 -.->|"Pre-filled query"| F4
F3 -.->|"Pre-filled query"| F4
N1["New sections positioned strategically before testimonials to address user concerns and build trust"]
E -.-> N1
🎯 Key Changes:
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
| <Link | ||
| href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`} | ||
| target="_blank" | ||
| key={button.label} | ||
| className={cn( | ||
| buttonVariants({ variant: "outline", size: "xl" }), | ||
| "border-ring bg-background text-xl font-medium py-6", | ||
| )} | ||
| > |
There was a problem hiding this comment.
Security vulnerability: Missing rel="noopener noreferrer" on external links with target="_blank"
Opening external links with target="_blank" without rel="noopener noreferrer" creates a security vulnerability where the opened page can access the window.opener object and potentially redirect your site to a malicious URL (tabnabbing attack). This is especially critical since you're linking to third-party AI services.
Confidence: 5/5
Suggested Fix
| <Link | |
| href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`} | |
| target="_blank" | |
| key={button.label} | |
| className={cn( | |
| buttonVariants({ variant: "outline", size: "xl" }), | |
| "border-ring bg-background text-xl font-medium py-6", | |
| )} | |
| > | |
| <Link | |
| href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| key={button.label} | |
| className={cn( | |
| buttonVariants({ variant: "outline", size: "xl" }), | |
| "border-ring bg-background text-xl font-medium py-6", | |
| )} | |
| > | |
Add rel="noopener noreferrer" to the Link component. This prevents the opened page from accessing window.opener and protects against tabnabbing attacks.
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/Home/AskAI.tsx around line 58, the Link component opens external URLs with target="_blank" but is missing the rel="noopener noreferrer" attribute, which creates a security vulnerability where malicious sites could access window.opener and redirect the parent page; add rel="noopener noreferrer" to the Link component at line 60 (after target="_blank") to prevent tabnabbing attacks and ensure secure external link handling.
📍 This suggestion applies to lines 58-66
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/components/Home/Mentor.tsx (2)
87-102:⚠️ Potential issue | 🟡 MinorDescription
exitanimation will not run — needs anAnimatePresencewrapper.
motion.pis conditionally rendered with{item === activeIndex && (...)}and declaresexit={{ opacity: 0, y: 10 }}, but there is noAnimatePresencearound it. Motion only triggersexitfor directAnimatePresencechildren before unmount; without it, the previous description disappears instantly and only the new one'sinitial → animateplays, producing an asymmetric transition.Wrap the conditional in
<AnimatePresence initial={false} mode="wait">(the import is already present). Keep thekey={desc-${idx}}so AnimatePresence can track identity.🎞️ Proposed fix
- {item === activeIndex && ( - <motion.p - key={`desc-${idx}`} - initial={{ opacity: 0, y: -10 }} - animate={{ opacity: 1, y: 0 }} - exit={{ opacity: 0, y: 10 }} - transition={{ - duration: 0.25, - delay: 0.25, - ease: "easeOut", - }} - className="text-background text-sm md:text-base font-medium max-w-96" - > - {item.description} - </motion.p> - )} + <AnimatePresence initial={false} mode="wait"> + {item === activeIndex && ( + <motion.p + key={`desc-${idx}`} + initial={{ opacity: 0, y: -10 }} + animate={{ opacity: 1, y: 0 }} + exit={{ opacity: 0, y: 10 }} + transition={{ + duration: 0.25, + delay: 0.25, + ease: "easeOut", + }} + className="text-background text-sm md:text-base font-medium max-w-96" + > + {item.description} + </motion.p> + )} + </AnimatePresence>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Mentor.tsx` around lines 87 - 102, The description's exit animation on motion.p (key={`desc-${idx}`}) isn't running because the conditional render keyed by item === activeIndex isn't wrapped in an AnimatePresence; wrap the conditional block that renders motion.p inside <AnimatePresence initial={false} mode="wait"> so AnimatePresence can detect the leaving element and run exit={{ opacity: 0, y: 10 }}, keeping the existing key to preserve identity and using the already-imported AnimatePresence.
56-70:⚠️ Potential issue | 🟠 MajorKeyboard users still cannot switch items —
motion.li+onClickis not focusable.The interactive surface for selection remains a
motion.liwith a bareonClickhandler. There is notabIndex, no key handler, norole="button"/aria-pressed, and no nested<button>. Users navigating by keyboard (Tab/Enter/Space) cannot change the active mentor item, which breaks the whole feature for them and assistive tech.Either render the row's clickable surface as a real
<button type="button">(preferred — gets focus, Enter/Space, andaria-pressedfor free), or usemotion.buttondirectly. Make sure to also setaria-pressed={item === activeItem}and preserve a visible focus ring.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Mentor.tsx` around lines 56 - 70, The list item using motion.li with only onClick is not keyboard-accessible; change the interactive surface to a real focusable control by using motion.button (or wrapping the content in a <button type="button">) instead of motion.li, keep the existing onClick handler that calls setActiveIndex(item), add aria-pressed={item === activeIndex} and ensure a visible focus style (preserve current focus ring classes), and if you must keep a non-button element add tabIndex={0} plus keyDown handler for Enter/Space and role="button" to mirror the same behavior.
🧹 Nitpick comments (5)
src/components/Home/WhyLeadlly.tsx (3)
1-1: DefaultReactimport is unnecessary with the modern JSX transform.With React 19 and Next.js 15's automatic JSX runtime,
import React from "react"isn't required here since noReact.*API is used. Safe to remove.-import React from "react"; - import Container from "../shared/Container";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/WhyLeadlly.tsx` at line 1, Remove the unnecessary default React import from the WhyLeadlly component: the file-level line `import React from "react"` should be deleted since the component (WhyLeadlly) only uses JSX and no React.* APIs and the project uses the automatic JSX runtime; ensure no other references to the React symbol remain in the file after removal.
57-61: Conflicting width constraints onImageAnimation.
flex-1together with explicitw-[486px] h-[502px]mixes a flex-grow allocation with fixed dimensions; depending onImageAnimation's internal handling this can produce unexpected sizing onlgbreakpoints. Consider droppingflex-1(since the column is hidden belowlgand you want the fixed art size) or replacing the fixed width with a max-width.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/WhyLeadlly.tsx` around lines 57 - 61, The ImageAnimation component is given both flex-1 and fixed dimensions (w-[486px] h-[502px]) which conflicts; update the JSX for ImageAnimation to remove the flex-1 class (or, if you need responsive shrink/grow, replace w-[486px] with a max-w like max-w-[486px]) so the component uses the intended fixed art size at lg; locate the ImageAnimation usage in WhyLeadlly.tsx and remove the "flex-1" class (or swap w-[486px] to max-w-[486px] and keep height handling) to resolve the conflicting width constraints.
42-55: Stagger reveals per item (or drop the per-item intent).The AI summary describes "staggered
delayvalues" for the three feature blocks, but the current structure wraps the entire list in a singleReveal delay={0.4}, so all three appear at once. If staggering was the intent, wrap each item individually with incremental delays; otherwise this is fine to leave as-is.♻️ Optional refactor for true stagger
- <Reveal - delay={0.4} - className="flex-1 h-full border-l border-ring px-8 flex flex-col items-start justify-between gap-10" - > - {items.map((item, idx) => ( - <div key={idx}> - <h5 className="font-semibold text-lg md:text-xl"> - {item.title} - </h5> - <p className="text-base md:text-lg">{item.description}</p> - </div> - ))} - </Reveal> + <div className="flex-1 h-full border-l border-ring px-8 flex flex-col items-start justify-between gap-10"> + {items.map((item, idx) => ( + <Reveal key={item.title} delay={0.4 + idx * 0.2}> + <h5 className="font-semibold text-lg md:text-xl"> + {item.title} + </h5> + <p className="text-base md:text-lg">{item.description}</p> + </Reveal> + ))} + </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/WhyLeadlly.tsx` around lines 42 - 55, The current Reveal wrapper (Reveal with delay={0.4}) is surrounding the whole items.map so all feature blocks animate at once; if the intent is a staggered entrance, move the Reveal inside the map so each item is wrapped individually (i.e., within items.map wrap each <div key={idx}> in a <Reveal ...>) and compute an incremental delay using the map index (e.g., delay={baseDelay + idx * step}) otherwise remove the single delay prop from the outer Reveal to avoid misleading documentation; update the Reveal usage and remove/adjust the outer wrapper accordingly.src/components/Home/Mentor.tsx (1)
47-48: RenameactiveIndex— it holds the active item, not an index.
activeIndexis initialized withitems[0]and later compared viaitem === activeIndexand dereferenced asactiveIndex.title/activeIndex.image. The name fights the usage and makes the reference-equality comparison on Line 68/87 easy to misread. Either store an index (and derive the item) or rename toactiveItem.♻️ Proposed rename
- const [activeIndex, setActiveIndex] = useState(items[0]); + const [activeItem, setActiveItem] = useState(items[0]);…and update the three call sites (
setActiveIndex(item),item === activeIndex,activeIndex.title,activeIndex.image) accordingly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/Mentor.tsx` around lines 47 - 48, The variable activeIndex actually holds the active item object, so rename it to activeItem and update all related symbols: change the state hook const [activeIndex, setActiveIndex] to const [activeItem, setActiveItem], update any calls setActiveIndex(item) → setActiveItem(item), replace comparisons item === activeIndex → item === activeItem, and replace property accesses activeIndex.title / activeIndex.image → activeItem.title / activeItem.image so the name matches its usage and avoids confusion with an index.src/components/Home/AskAI.tsx (1)
58-74: Consider a plain<a>for external links.
next/linkis intended for client-side navigation between app routes; for absolute external URLs there's no prefetch or routing benefit, and using a plain anchor makes the intent clearer. Also worth addingrel="noopener noreferrer"explicitly for externaltarget="_blank"links — modern browsers implynoopener, but stating it is conventional and cooperates with linters/CSP audits.♻️ Optional refactor
- <Link - href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`} - target="_blank" - key={button.label} - className={cn( - buttonVariants({ variant: "outline", size: "xl" }), - "border-ring bg-background text-xl font-medium py-6", - )} - > + <a + href={`${button.url}/?q=${encodeURIComponent(AI_PROMPT)}`} + target="_blank" + rel="noopener noreferrer" + key={button.label} + className={cn( + buttonVariants({ variant: "outline", size: "xl" }), + "border-ring bg-background text-xl font-medium py-6", + )} + > {button.icon && ( <HugeiconsIcon icon={button.icon} className="shrink-0 size-6" /> )} {button.label} - </Link> + </a>If you keep
next/link, theimport Link from "next/link"can stay; otherwise drop that import.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Home/AskAI.tsx` around lines 58 - 74, The Link component is being used for absolute external URLs — replace the next/link usage for this external button (the JSX using Link with href={`${button.url}/?q=...`}, key={button.label}, className built from buttonVariants) with a plain anchor element (<a>) that preserves the same href, target="_blank", className, children (HugeiconsIcon when button.icon and {button.label}) and add rel="noopener noreferrer"; also remove the next/link import if no other Link usages remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Home/AskAI.tsx`:
- Around line 57-66: The href construction for each AI button uses an unencoded
literal query string ("q=tell me why leadlly.in...") which can break; update the
mapping in the AIButtons render (the Link elements using button.url and href) to
define the prompt once (e.g., const prompt = "tell me why leadlly.in is a great
choice for me") and URL-encode it (encodeURIComponent) before building href
(`${button.url}/?q=${encodedPrompt}`) so all generated links use a safe, encoded
query value.
In `@src/components/Home/Mentor.tsx`:
- Around line 14-45: The items array in Mentor.tsx contains duplicated
descriptions for four entries; update the items constant so each object (titles:
"Mentorship", "Personalized Curriculum", "Adaptive Quizzes", "Daily Task
Planning", identified in the items array) has a unique, descriptive description
string that explains that specific feature, or alternatively move/populate the
items data from a content source/CMS and reference it instead; ensure the
descriptions are concise, feature-specific, and replace the repeated mentorship
blurb so the carousel communicates distinct value per item.
In `@src/components/Home/WhyLeadlly.tsx`:
- Around line 29-39: The user-facing JSX in the WhyLeadlly component contains
stray spaces before punctuation; update the text nodes inside the <h2> (the
"What Makes Leadlly Different ?" string) and the paragraph <p> (the
"...experience , from your schedule..." string) in the WhyLeadlly component to
remove the extra space before the question mark and the extra space before the
comma so the rendered text reads "What Makes Leadlly Different?" and
"...experience, from your schedule..." respectively.
---
Duplicate comments:
In `@src/components/Home/Mentor.tsx`:
- Around line 87-102: The description's exit animation on motion.p
(key={`desc-${idx}`}) isn't running because the conditional render keyed by item
=== activeIndex isn't wrapped in an AnimatePresence; wrap the conditional block
that renders motion.p inside <AnimatePresence initial={false} mode="wait"> so
AnimatePresence can detect the leaving element and run exit={{ opacity: 0, y: 10
}}, keeping the existing key to preserve identity and using the already-imported
AnimatePresence.
- Around line 56-70: The list item using motion.li with only onClick is not
keyboard-accessible; change the interactive surface to a real focusable control
by using motion.button (or wrapping the content in a <button type="button">)
instead of motion.li, keep the existing onClick handler that calls
setActiveIndex(item), add aria-pressed={item === activeIndex} and ensure a
visible focus style (preserve current focus ring classes), and if you must keep
a non-button element add tabIndex={0} plus keyDown handler for Enter/Space and
role="button" to mirror the same behavior.
---
Nitpick comments:
In `@src/components/Home/AskAI.tsx`:
- Around line 58-74: The Link component is being used for absolute external URLs
— replace the next/link usage for this external button (the JSX using Link with
href={`${button.url}/?q=...`}, key={button.label}, className built from
buttonVariants) with a plain anchor element (<a>) that preserves the same href,
target="_blank", className, children (HugeiconsIcon when button.icon and
{button.label}) and add rel="noopener noreferrer"; also remove the next/link
import if no other Link usages remain.
In `@src/components/Home/Mentor.tsx`:
- Around line 47-48: The variable activeIndex actually holds the active item
object, so rename it to activeItem and update all related symbols: change the
state hook const [activeIndex, setActiveIndex] to const [activeItem,
setActiveItem], update any calls setActiveIndex(item) → setActiveItem(item),
replace comparisons item === activeIndex → item === activeItem, and replace
property accesses activeIndex.title / activeIndex.image → activeItem.title /
activeItem.image so the name matches its usage and avoids confusion with an
index.
In `@src/components/Home/WhyLeadlly.tsx`:
- Line 1: Remove the unnecessary default React import from the WhyLeadlly
component: the file-level line `import React from "react"` should be deleted
since the component (WhyLeadlly) only uses JSX and no React.* APIs and the
project uses the automatic JSX runtime; ensure no other references to the React
symbol remain in the file after removal.
- Around line 57-61: The ImageAnimation component is given both flex-1 and fixed
dimensions (w-[486px] h-[502px]) which conflicts; update the JSX for
ImageAnimation to remove the flex-1 class (or, if you need responsive
shrink/grow, replace w-[486px] with a max-w like max-w-[486px]) so the component
uses the intended fixed art size at lg; locate the ImageAnimation usage in
WhyLeadlly.tsx and remove the "flex-1" class (or swap w-[486px] to max-w-[486px]
and keep height handling) to resolve the conflicting width constraints.
- Around line 42-55: The current Reveal wrapper (Reveal with delay={0.4}) is
surrounding the whole items.map so all feature blocks animate at once; if the
intent is a staggered entrance, move the Reveal inside the map so each item is
wrapped individually (i.e., within items.map wrap each <div key={idx}> in a
<Reveal ...>) and compute an incremental delay using the map index (e.g.,
delay={baseDelay + idx * step}) otherwise remove the single delay prop from the
outer Reveal to avoid misleading documentation; update the Reveal usage and
remove/adjust the outer wrapper accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 61213714-e54d-4ed9-8827-60c8ab110de9
⛔ Files ignored due to path filters (1)
public/assets/illustrations/svg_1.svgis excluded by!**/*.svg
📒 Files selected for processing (4)
src/app/(root)/(home)/page.tsxsrc/components/Home/AskAI.tsxsrc/components/Home/Mentor.tsxsrc/components/Home/WhyLeadlly.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/(root)/(home)/page.tsx
| {AIButtons.map((button) => ( | ||
| <Link | ||
| href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`} | ||
| target="_blank" | ||
| key={button.label} | ||
| className={cn( | ||
| buttonVariants({ variant: "outline", size: "xl" }), | ||
| "border-ring bg-background text-xl font-medium py-6", | ||
| )} | ||
| > |
There was a problem hiding this comment.
URL-encode the prefill query string.
The href contains literal spaces in the query value (q=tell me why leadlly.in...). Browsers will usually fix this up, but emitting unencoded URLs is fragile (validators, server-side parsers, copy/paste, future query additions can all break) and it's trivial to do correctly. Also, defining the prompt once avoids drift across providers.
♻️ Proposed fix
+const AI_PROMPT = "tell me why leadlly.in is a great choice for me";
+
const AIButtons = [
@@
{AIButtons.map((button) => (
<Link
- href={`${button.url}/?q=tell me why leadlly.in is a great choice for me`}
+ href={`${button.url}/?q=${encodeURIComponent(AI_PROMPT)}`}
target="_blank"
key={button.label}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/AskAI.tsx` around lines 57 - 66, The href construction
for each AI button uses an unencoded literal query string ("q=tell me why
leadlly.in...") which can break; update the mapping in the AIButtons render (the
Link elements using button.url and href) to define the prompt once (e.g., const
prompt = "tell me why leadlly.in is a great choice for me") and URL-encode it
(encodeURIComponent) before building href (`${button.url}/?q=${encodedPrompt}`)
so all generated links use a safe, encoded query value.
| What Makes <span className="text-primary">Leadlly</span> Different ? | ||
| </h2> | ||
| </Reveal> | ||
|
|
||
| <Reveal delay={0.2}> | ||
| <p className="text-base md:text-lg lg:text-2xl"> | ||
| At Leadlly, we believe that not every student is the same. | ||
| That's why our platform personalises your study experience , | ||
| from your schedule to your strategy based on your unique learning | ||
| pattern. | ||
| </p> |
There was a problem hiding this comment.
Fix stray spaces before punctuation in user-facing copy.
There are extra spaces before ? on line 29 and before , on line 36 that will render as visible whitespace.
✏️ Proposed fix
- What Makes <span className="text-primary">Leadlly</span> Different ?
+ What Makes <span className="text-primary">Leadlly</span> Different?- At Leadlly, we believe that not every student is the same.
- That's why our platform personalises your study experience ,
- from your schedule to your strategy based on your unique learning
- pattern.
+ At Leadlly, we believe that not every student is the same.
+ That's why our platform personalises your study experience,
+ from your schedule to your strategy based on your unique learning
+ pattern.📝 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.
| What Makes <span className="text-primary">Leadlly</span> Different ? | |
| </h2> | |
| </Reveal> | |
| <Reveal delay={0.2}> | |
| <p className="text-base md:text-lg lg:text-2xl"> | |
| At Leadlly, we believe that not every student is the same. | |
| That's why our platform personalises your study experience , | |
| from your schedule to your strategy based on your unique learning | |
| pattern. | |
| </p> | |
| What Makes <span className="text-primary">Leadlly</span> Different? | |
| </h2> | |
| </Reveal> | |
| <Reveal delay={0.2}> | |
| <p className="text-base md:text-lg lg:text-2xl"> | |
| At Leadlly, we believe that not every student is the same. | |
| That's why our platform personalises your study experience, | |
| from your schedule to your strategy based on your unique learning | |
| pattern. | |
| </p> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Home/WhyLeadlly.tsx` around lines 29 - 39, The user-facing JSX
in the WhyLeadlly component contains stray spaces before punctuation; update the
text nodes inside the <h2> (the "What Makes Leadlly Different ?" string) and the
paragraph <p> (the "...experience , from your schedule..." string) in the
WhyLeadlly component to remove the extra space before the question mark and the
extra space before the comma so the rendered text reads "What Makes Leadlly
Different?" and "...experience, from your schedule..." respectively.
Summary by BeetleThis PR introduces an interactive feature enhancement to the Tracker section of the homepage, transforming static feature cards into clickable, expandable modals with detailed explanations. The update refines messaging across the platform to be more direct and user-focused, replacing generic descriptions with concrete value propositions. A new custom React hook ( 📁 File Changes Summary
Total Changes: 4 files changed, +347 additions, -113 deletions 🗺️ WalkthroughsequenceDiagram
participant User
participant TrackerCard
participant Modal
participant Hook as useOutsideClick
participant DOM
User->>TrackerCard: Clicks arrow button
TrackerCard->>Modal: setCurrentCard(card)
Modal->>DOM: Render backdrop (z-40)
Modal->>DOM: Render modal (z-50)
Note over Modal: Framer Motion layoutId creates smooth transition
Modal->>DOM: Set body overflow: hidden
Modal->>Hook: Initialize click listener
alt User clicks outside modal
User->>DOM: Click outside
Hook->>Modal: Trigger callback
Modal->>TrackerCard: setCurrentCard(null)
Modal->>DOM: Remove modal & backdrop
Modal->>DOM: Restore body overflow
else User clicks close button
User->>Modal: Click Cancel icon
Modal->>TrackerCard: setCurrentCard(null)
Modal->>DOM: Remove modal & backdrop
Modal->>DOM: Restore body overflow
end
🎯 Key Changes
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
✅ You're good to merge this PR! No issues found. Great job! Settings⚙️ SettingsSeverity Threshold: 📖 User Guide
|
…nd adjust navigation items
Summary by BeetleThis PR updates the website's call-to-action strategy by redirecting users from the web platform to the Leadlly mobile app. The changes focus on replacing login/signup links with app download links, updating AI chatbot query parameters with more detailed prompts, and streamlining navigation to emphasize app adoption and mentor recruitment. 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 7 files changed, +22 additions, -12 deletions 🗺️ Walkthrough:graph TD
A["Website Visitor"] --> B{"User Action"}
B -->|"Clicks Hero CTA"| C["Scroll to Download Section"]
B -->|"Clicks Planner/Growth Meter CTA"| D["Redirect to Google Play Store"]
B -->|"Clicks NavBar Download"| D
B -->|"Clicks Mobile Menu Download"| D
B -->|"Uses AI Chatbot"| E["Enhanced Query with Detailed Prompt"]
B -->|"Navigates to Become a Mentor"| F["Route to /mentor Page"]
C --> G["Download Section Visible"]
G --> D
D --> H["Google Play Store App Page"]
H --> I["User Downloads Leadlly App"]
E --> J["AI Evaluates Leadlly Features"]
J --> K["User Gets Comprehensive Analysis"]
K --> L["Informed Decision to Download"]
L --> D
F --> M["Mentor Recruitment Flow"]
style A fill:#e1f5ff
style D fill:#90EE90
style I fill:#FFD700
style H fill:#98FB98
🎯 Key Changes:
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
| <div className="flex items-center justify-between flex-wrap gap-4"> | ||
| {AIButtons.map((button) => ( | ||
| <Link | ||
| href={`${button.url}/?q=I'm a JEE or NEET aspirant looking for a structured study platform. Leadlly.in is an AI-powered personalized learning platform that offers a smart planner, revision tracker, growth meter, and 1-on-1 mentor connect. It personalizes preparation based on my pace, consistency, and accuracy. Can you evaluate whether Leadlly is a good fit for a serious competitive exam aspirant like me? Please cover: 1) How its features compare to self-studying or coaching, 2) The value of having a personal mentor and AI-driven revision tracking, 3) Whether personalized study planning really improves results, and 4) Any honest concerns or things to watch out for.`} |
There was a problem hiding this comment.
The query parameter contains a very long unencoded string (600+ characters) that is directly interpolated into the URL. This creates multiple security and reliability concerns:
- URL Injection/Malformation: Special characters in the query string are not URL-encoded, which could break URL parsing
- Reflected XSS Risk: If the target AI service reflects this query parameter in its response without proper encoding, it could enable XSS attacks
- URL Length Limits: The combined URL may exceed browser/server limits (2048-8192 chars), causing request failures
Confidence: 4/5
Suggested Fix
| href={`${button.url}/?q=I'm a JEE or NEET aspirant looking for a structured study platform. Leadlly.in is an AI-powered personalized learning platform that offers a smart planner, revision tracker, growth meter, and 1-on-1 mentor connect. It personalizes preparation based on my pace, consistency, and accuracy. Can you evaluate whether Leadlly is a good fit for a serious competitive exam aspirant like me? Please cover: 1) How its features compare to self-studying or coaching, 2) The value of having a personal mentor and AI-driven revision tracking, 3) Whether personalized study planning really improves results, and 4) Any honest concerns or things to watch out for.`} | |
| href={`${button.url}/?q=${encodeURIComponent("I'm a JEE or NEET aspirant looking for a structured study platform. Leadlly.in is an AI-powered personalized learning platform that offers a smart planner, revision tracker, growth meter, and 1-on-1 mentor connect. It personalizes preparation based on my pace, consistency, and accuracy. Can you evaluate whether Leadlly is a good fit for a serious competitive exam aspirant like me? Please cover: 1) How its features compare to self-studying or coaching, 2) The value of having a personal mentor and AI-driven revision tracking, 3) Whether personalized study planning really improves results, and 4) Any honest concerns or things to watch out for.")}` | |
Wrap the query string with encodeURIComponent() to properly encode special characters (apostrophes, commas, colons, question marks, etc.) for safe URL transmission. This prevents URL malformation and reduces XSS risk if the parameter is reflected by the target service.
Additionally, consider:
- Storing this long prompt in a constant/config file for better maintainability
- Testing the final URL length to ensure it stays within browser limits
- Validating that
button.urlis from a trusted source
Prompt for AI
Copy this prompt to your AI IDE to fix this issue locally:
In src/components/Home/AskAI.tsx around line 59, the query parameter contains a long unencoded string that could cause URL malformation and potential XSS vulnerabilities; wrap the query string value with encodeURIComponent() to properly encode special characters for safe URL transmission, and consider extracting this long prompt to a constant for better maintainability and testing the final URL length to ensure browser compatibility.
Summary by BeetleThis PR performs a minor refactoring to improve code consistency in the 📁 File Changes Summary (Consolidated across all commits):
Total Changes: 1 file changed, +3 additions, -3 deletions 🎯 Key Changes:
⚙️ SettingsSeverity Threshold: 📖 User Guide
|
|
✅ You're good to merge this PR! No issues found. Great job! Settings⚙️ SettingsSeverity Threshold: 📖 User Guide
|
Summary by CodeRabbit
New Features
Style
Chores