-
-
Notifications
You must be signed in to change notification settings - Fork 38
Project restructure as monorepo #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- Set up monorepo structure with v1 (original) and v2 (new) portfolio versions - Create root package.json with workspace configuration - Update documentation for dual-version management - Configure Vercel deployment for multiple projects in single repo
WalkthroughThis update transforms the repository into a monorepo supporting two separate portfolio website versions (v1 and v2), each with its own configuration, dependencies, documentation, and ignore rules. The root and each version gain dedicated README and package files. The v2 directory introduces a comprehensive Next.js 15/React 19 project, featuring a robust UI component library, theming, data modules, and extensive automated tests. Supporting infrastructure includes advanced CI workflows, code quality tools, and deployment documentation. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant Monorepo Root
participant V1 App
participant V2 App
User->>Browser: Accesses repository or site
Browser->>Monorepo Root: Loads README, selects version
alt User chooses v1
Browser->>V1 App: Runs v1 (Next.js 14, React 18)
V1 App-->>Browser: Serves v1 portfolio site
else User chooses v2
Browser->>V2 App: Runs v2 (Next.js 15, React 19)
V2 App-->>Browser: Serves v2 portfolio site
end
sequenceDiagram
participant Dev
participant Monorepo Root
participant V1 Workspace
participant V2 Workspace
participant CI/CD
Dev->>Monorepo Root: Add/commit changes
Monorepo Root->>V1 Workspace: Run v1 scripts/tests
Monorepo Root->>V2 Workspace: Run v2 scripts/tests
Monorepo Root->>CI/CD: Trigger workflows (lint, test, build)
CI/CD->>V1 Workspace: Build/test v1
CI/CD->>V2 Workspace: Build/test v2
CI/CD-->>Dev: Report results
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 48
🧹 Nitpick comments (53)
v2/src/app/robots.txt (2)
1-5: Verify serving ofrobots.txtunder Next.js App Router.
Next.js static assets (likerobots.txt) are typically placed in thepublicdirectory. Files insidesrc/appare treated as route handlers and may not be served as raw files. Please confirm that this file is accessible at/robots.txtin your production build. If not, consider moving it tov2/public/robots.txtor implementing an explicit route handler.
2-3: Group crawl directives for clarity.
It’s more idiomatic to groupAllowandDisallowunder the sameUser-agentblock. For example:User-agent: * Disallow: /private/ Allow: /This ordering avoids ambiguity for some crawler implementations.
v2/src/app/sitemap.xml (3)
2-14: Ensure correct serving ofsitemap.xmlunder Next.js.
Static sitemap files should live in thepublicdirectory to be served at/sitemap.xml. Files undersrc/appmay not be exposed as raw assets. Confirm this in your build output or move it tov2/public/sitemap.xml. As an alternative, implement a route atapp/sitemap.xml/route.tsthat returns the XML.
4-4: Update<lastmod>timestamp.
The hardcoded date2024-06-11T15:02:24.021Zis now outdated. Either update it to the actual last modification timestamp or generate this value dynamically to keep the sitemap accurate.
5-6: Reevaluate<changefreq>and<priority>values.
Both entries are set toyearlyand priority1. Depending on content update patterns and SEO strategy, you may want to adjust these to better reflect page importance and update frequency (e.g.,monthly,0.8).CONTRIBUTING.md (1)
1-4: Suggestion: Update the project description to reflect multiple site versions.The intro still mentions “built with Next.js and Sass,” but this monorepo includes both v1 (Sass) and v2 (Tailwind CSS v4). Consider rephrasing to clarify the multi-version setup, for example:
Built with Next.js (v1 uses Sass, v2 uses Tailwind CSS v4).v2/src/components/about/index.tsx (1)
3-9: LGTM! Consider adding prop support for future extensibility.The component implementation is clean and follows a consistent pattern. The wrapper provides a good abstraction for the about page structure.
Consider making the component more flexible by accepting props:
-export default function AboutPageComponent() { +interface AboutPageComponentProps { + className?: string; +} + +export default function AboutPageComponent({ className }: AboutPageComponentProps = {}) { return ( - <div className="relative w-full"> + <div className={cn("relative w-full", className)}> <MainSection /> </div> ); }This would require importing the
cnutility from@/lib/utils.v2/src/components/home/index.tsx (1)
3-9: LGTM! Consider creating a shared page wrapper component.The implementation is consistent with the about page component, which is good for maintainability.
To reduce duplication between page components, consider creating a shared
PageWrappercomponent:// v2/src/components/common/page-wrapper.tsx interface PageWrapperProps { children: React.ReactNode; className?: string; } export default function PageWrapper({ children, className }: PageWrapperProps) { return ( <div className={cn("relative w-full", className)}> {children} </div> ); }Then update this component:
+import PageWrapper from "@/components/common/page-wrapper"; import HeroSection from "@/components/home/hero-section"; export default function HomePageComponent() { return ( - <div className="relative w-full"> + <PageWrapper> <HeroSection /> - </div> + </PageWrapper> ); }v2/src/app/manifest.ts (2)
5-7: Improve the app description for better user experience.The description should be more descriptive of what the application does rather than just repeating the name.
Consider updating the description to be more informative:
- description: "Nikhil Rajput", + description: "Personal portfolio website of Nikhil Rajput - Software Developer",
12-18: Consider specifying explicit icon sizes for better PWA support.Using
sizes: "any"is valid but specifying explicit sizes can provide better optimization and compatibility.Consider adding specific icon sizes:
icons: [ { src: "/icon.png", - sizes: "any", + sizes: "192x192", type: "image/png", }, + { + src: "/icon.png", + sizes: "512x512", + type: "image/png", + }, ],v1/.gitignore (1)
37-40: Reconsider ignoring lock files for dependency consistency.While ignoring lock files can reduce merge conflicts, it may lead to dependency version inconsistencies across environments and team members. Consider keeping the appropriate lock file for your chosen package manager.
If using npm, consider keeping
package-lock.json:# lock - package-lock.json pnpm-lock.yaml yarn.lockOr if using pnpm, keep
pnpm-lock.yaml:# lock package-lock.json - pnpm-lock.yaml yarn.lockv2/src/components/theme-mode-toggle.tsx (1)
20-28: Consider simplifying button styling and improving consistency.The button styling overrides (
outline-none outline-transparent border-none) might conflict with the design system's focus states and accessibility requirements. Consider using the design system's variant props instead.- <Button - variant="outline" - className="outline-none outline-transparent border-none" - > + <Button variant="ghost" size="icon">Alternatively, if custom styling is needed, consider defining a specific variant in your button component for theme toggles.
v2/src/components/about/section-title.tsx (1)
14-16: Consider making the gradient text configurable.The gradient implementation looks great, but consider making the highlighted text ("Me") configurable for reusability across different sections.
-const SectionTitle = () => { +interface SectionTitleProps { + title: string; + highlightText: string; + description: string; +} + +const SectionTitle = ({ title, highlightText, description }: SectionTitleProps) => { return ( <motion.div // ... motion props > <h2 className="text-3xl md:text-4xl font-bold text-black dark:text-white mb-4"> - About{" "} + {title}{" "} <span className="bg-clip-text text-transparent bg-gradient-to-r from-purple-500 via-violet-500 to-pink-500"> - Me + {highlightText} </span> </h2> - <p className="text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto"> - I'm a passionate full-stack developer with expertise in building - modern web and mobile applications. - </p> + <p className="text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto"> + {description} + </p> </motion.div> ); };v2/src/components/ui/textarea.tsx (1)
9-12: Consider organizing the long className string for better readability.The extensive Tailwind className string could be broken down for better maintainability.
Consider organizing the classes like this:
className={cn( - "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + // Base styles + "flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm", + // Border and background + "border-input dark:bg-input/30", + // Placeholder and text + "placeholder:text-muted-foreground", + // Focus states + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + // Invalid states + "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + // Disabled states + "disabled:cursor-not-allowed disabled:opacity-50", className )}v2/src/components/ui/sonner.tsx (2)
11-11: Improve type safety for theme casting.The theme casting could be more type-safe to prevent potential runtime issues.
Consider this approach for better type safety:
- theme={theme as ToasterProps["theme"]} + theme={theme === "dark" || theme === "light" ? theme : "system"}This ensures only valid theme values are passed to the Sonner component.
6-23: Consider adding forwardRef for component consistency.While not strictly necessary for the Toaster component, adding forwardRef would maintain consistency with other UI components in the library.
Apply this diff for consistency:
-const Toaster = ({ ...props }: ToasterProps) => { +const Toaster = React.forwardRef< + React.ElementRef<typeof Sonner>, + ToasterProps +>(({ ...props }, ref) => { const { theme = "system" } = useTheme() return ( <Sonner + ref={ref} theme={theme === "dark" || theme === "light" ? theme : "system"} className="toaster group" style={ { "--normal-bg": "var(--popover)", "--normal-text": "var(--popover-foreground)", "--normal-border": "var(--border)", } as React.CSSProperties } {...props} /> ) -} +}) +Toaster.displayName = "Toaster"v2/src/components/ui/input.tsx (1)
10-15: Consider organizing className for better maintainability.The multiple className strings could be better organized for readability and maintenance.
Consider organizing the classes like this:
className={cn( - "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", - "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", - "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + // Base styles + "flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm", + // Border and background + "border-input dark:bg-input/30", + // Text and selection + "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground", + // File input styles + "file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium", + // Focus states + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + // Invalid states + "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + // Disabled states + "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", className )}v2/src/components/about/stats-section.tsx (1)
13-47: Refactor repeated Card markup into a data-driven map or dedicated component.
The four<Card>blocks share identical classes and structure. Consider extracting the repeated JSX into a smallStatsCardcomponent and rendering viaarray.map, for example:const stats = [ { value: "5+", label: "Years Experience" }, { value: "50+", label: "Projects Completed" }, { value: "20+", label: "Happy Clients" }, { value: "15+", label: "Open Source Contributions" }, ]; - stats.map((stat) => ( - <Card className="..."> - <CardContent className="..."> - <h3>...</h3> - <p>...</p> - </CardContent> - </Card> - )) + stats.map(({ value, label }) => ( + <StatsCard key={label} value={value} label={label} /> + ))This improves maintainability and reduces duplication.
package.json (1)
5-18: Consider adding aggregated monorepo scripts for ergonomics.
Right now only per-workspace scripts exist. You can add root-level commands to run all workspaces in one go:"scripts": { "dev:v1": "cd v1 && npm run dev", "dev:v2": "cd v2 && npm run dev", + "dev": "npm run dev:v1 & npm run dev:v2", "build:v1": "cd v1 && npm run build", "build:v2": "cd v2 && npm run build", + "build": "npm run build:v1 && npm run build:v2", "start:v1": "cd v1 && npm run start", "start:v2": "cd v2 && npm run start", + "start": "npm run start:v1 && npm run start:v2", "lint:v1": "cd v1 && npm run lint", "lint:v2": "cd v2 && npm run lint", + "lint": "npm run lint:v1 && npm run lint:v2" }v2/tsconfig.json (1)
3-3: Consider updating target to a more modern ES version.The ES2017 target is quite conservative for a modern portfolio website. Consider updating to ES2020 or ES2022 for better performance and access to modern JavaScript features, especially since this is a new project.
- "target": "ES2017", + "target": "ES2022",This would enable better tree-shaking, optional chaining, nullish coalescing, and other modern features that can improve bundle size and performance.
v2/src/components/ui/checkbox.tsx (1)
9-30: Consider adding forwardRef for better form library integration.The component looks well-structured with good accessibility through Radix UI. However, consider adding
forwardRefto improve integration with form libraries like React Hook Form.-function Checkbox({ +const Checkbox = React.forwardRef< + React.ElementRef<typeof CheckboxPrimitive.Root>, + React.ComponentProps<typeof CheckboxPrimitive.Root> +>(({ className, ...props -}: React.ComponentProps<typeof CheckboxPrimitive.Root>) { +}, ref) => { return ( <CheckboxPrimitive.Root + ref={ref} data-slot="checkbox" className={cn( "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} > <CheckboxPrimitive.Indicator data-slot="checkbox-indicator" className="flex items-center justify-center text-current transition-none" > <CheckIcon className="size-3.5" /> </CheckboxPrimitive.Indicator> </CheckboxPrimitive.Root> ) -} +}) +Checkbox.displayName = CheckboxPrimitive.Root.displayNamev2/src/components/nav/custom-nav-bar.tsx (2)
18-43: Consider making navigation items configurable.The navigation items are currently hardcoded, which reduces reusability. Consider accepting them as props or loading from a configuration file.
-const CustomNavBar = ({ className }: { className?: string }) => { - const navItems = [ - { - name: "About", - link: "/about", - }, - // ... other items - ]; +interface NavItem { + name: string; + link: string; +} + +interface CustomNavBarProps { + className?: string; + navItems?: NavItem[]; +} + +const defaultNavItems: NavItem[] = [ + { name: "About", link: "/about" }, + { name: "Services", link: "/services" }, + { name: "Experiences", link: "/experiences" }, + { name: "Skills", link: "/skills" }, + { name: "Projects", link: "/projects" }, + { name: "Contact", link: "/contact" }, +]; + +const CustomNavBar = ({ className, navItems = defaultNavItems }: CustomNavBarProps) => {
51-51: Consider making logo configuration dynamic.The logo source and text are hardcoded. For better maintainability in a portfolio site, consider making these configurable.
You could add logo configuration to the component props:
+interface CustomNavBarProps { + className?: string; + navItems?: NavItem[]; + logoSrc?: string; + logoText?: string; +} +const CustomNavBar = ({ + className, + navItems = defaultNavItems, + logoSrc = "/icon.png", + logoText = "Nikhil Rajput" +}: CustomNavBarProps) => {v2/src/components/ui/accordion.tsx (1)
38-39: Consider extracting long className string to a constant.The long className string in AccordionTrigger could be extracted for better readability and maintainability.
+const accordionTriggerClasses = + "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180" className={cn( - "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180", + accordionTriggerClasses, className )}v2/src/components/about/profile-card.tsx (3)
35-40: Consider making profile information configurable.The name and title are hardcoded, which reduces component reusability. Consider accepting these as props for better flexibility.
-const ProfileCard = () => { +interface ProfileCardProps { + name?: string; + title?: string; + avatarSrc?: string; + avatarAlt?: string; +} + +const ProfileCard = ({ + name = "Nikhil Rajput", + title = "Full Stack Developer", + avatarSrc = "/images/profile.webp", + avatarAlt = "Nikhil Rajput" +}: ProfileCardProps) => { return ( // ... <h3 className="text-2xl font-bold text-black dark:text-white mb-1"> - Nikhil Rajput + {name} </h3> <p className="text-neutral-600 dark:text-neutral-400 mb-4"> - Full Stack Developer + {title} </p>
42-75: Consider making social links configurable.The social media links are hardcoded. For better reusability, consider accepting them as props or from a configuration.
+interface SocialLink { + href: string; + icon: React.ComponentType<{ className?: string }>; + label: string; + external?: boolean; +} + +interface ProfileCardProps { + // ... other props + socialLinks?: SocialLink[]; +} +const defaultSocialLinks: SocialLink[] = [ + { + href: "https://github.com/nixrajput", + icon: IconBrandGithub, + label: "GitHub", + external: true, + }, + // ... other links +];
77-80: Implement resume download functionality.The download button currently has no functionality. Consider adding proper file download or linking to a resume file.
The button needs actual download functionality. You could either:
- Link to a static resume file:
- <Button className="w-full bg-gradient-to-r from-purple-500 via-violet-500 to-pink-500 hover:opacity-90 transition-opacity text-white"> + <Button asChild className="w-full bg-gradient-to-r from-purple-500 via-violet-500 to-pink-500 hover:opacity-90 transition-opacity text-white"> + <a href="/resume.pdf" download="Nikhil_Rajput_Resume.pdf"> <IconDownload className="mr-2 h-4 w-4" /> Download Resume + </a> - </Button> + </Button>
- Or add an onClick handler for dynamic generation
Would you like me to help implement either approach?
v2/src/components/ui/toggle.tsx (1)
10-10: Consider breaking down the long className string.The base className string is quite long. Consider extracting parts of it to constants for better readability.
+const baseToggleClasses = + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground" +const toggleFocusClasses = + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" +const toggleIconClasses = + "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 whitespace-nowrap" const toggleVariants = cva( - "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", + [baseToggleClasses, toggleFocusClasses, toggleIconClasses].join(" "),v2/src/components/about/bio-skills-section.tsx (2)
72-88: Consider staggered animations for better visual effect.The current implementation animates all skill badges simultaneously. Consider adding staggered delays for a more polished animation sequence.
<div className="flex flex-wrap gap-2"> {skills.map((skill, index) => ( <motion.div key={skill.name} initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} - transition={{ duration: 0.3 }} + transition={{ duration: 0.3, delay: index * 0.1 }} > <Badge variant="outline" className="bg-white/50 dark:bg-neutral-800/50 backdrop-blur-sm" > {skill.name} </Badge> </motion.div> ))} </div>
121-167: Consider extracting experience data for better maintainability.The experience timeline is well-designed and informative. For better maintainability, consider extracting the experience data to a separate constant or data file.
+const experiences = [ + { + id: 1, + title: "Senior Software Engineer", + company: "TechCorp Inc.", + period: "2023 - Present", + description: "Leading development of enterprise-level web applications using React, Next.js, and Node.js. Managing a team of 5 developers and implementing CI/CD pipelines." + }, + { + id: 2, + title: "Full Stack Developer", + company: "WebSolutions Ltd.", + period: "2020 - 2023", + description: "Developed and maintained multiple client projects using MERN stack. Implemented responsive designs and optimized application performance." + }, + { + id: 3, + title: "Junior Developer", + company: "StartupHub", + period: "2018 - 2020", + description: "Assisted in developing frontend components using React. Collaborated with designers to implement UI/UX designs." + } +]; <TabsContent value="experience"> <div className="space-y-6"> - <div className="relative pl-8 border-l border-neutral-200 dark:border-neutral-800"> - <div className="absolute w-4 h-4 bg-purple-500 rounded-full -left-2 top-0"></div> - <h3 className="text-lg font-semibold text-black dark:text-white"> - Senior Software Engineer - </h3> - <p className="text-purple-500 font-medium"> - TechCorp Inc. (2023 - Present) - </p> - <p className="text-neutral-700 dark:text-neutral-300 mt-2"> - Leading development of enterprise-level web applications - using React, Next.js, and Node.js. Managing a team of 5 - developers and implementing CI/CD pipelines. - </p> - </div> + {experiences.map((exp) => ( + <div key={exp.id} className="relative pl-8 border-l border-neutral-200 dark:border-neutral-800"> + <div className="absolute w-4 h-4 bg-purple-500 rounded-full -left-2 top-0"></div> + <h3 className="text-lg font-semibold text-black dark:text-white"> + {exp.title} + </h3> + <p className="text-purple-500 font-medium"> + {exp.company} ({exp.period}) + </p> + <p className="text-neutral-700 dark:text-neutral-300 mt-2"> + {exp.description} + </p> + </div> + ))} </div> </TabsContent>v2/src/components/home/hero-section.tsx (1)
9-9: Improve className prop usage.The
classNameprop is accepted but only passed toBackgroundBeamsWithCollision. Consider applying it to the main container for more flexibility.-function HeroSection({ className }: { className?: string }) { +function HeroSection({ className }: { className?: string }) { return ( <div id="hero" - className="relative h-auto min-h-screen bg-gradient-to-b from-white to-neutral-100 dark:from-neutral-950 dark:to-neutral-800 flex items-center w-full max-w-screen justify-center overflow-hidden" + className={cn("relative h-auto min-h-screen bg-gradient-to-b from-white to-neutral-100 dark:from-neutral-950 dark:to-neutral-800 flex items-center w-full max-w-screen justify-center overflow-hidden", className)} > - <BackgroundBeamsWithCollision className={className}> + <BackgroundBeamsWithCollision>Don't forget to import the
cnutility function:+import { cn } from "@/lib/utils";v2/src/components/ui/toggle-group.tsx (1)
10-15: Consider making context default values more explicit.The context provides default values for
sizeandvariant, but these defaults should match the actual default variants defined intoggleVariants.Consider importing the default variants from
toggleVariantsto ensure consistency:+import { toggleVariants } from "@/components/ui/toggle" + const ToggleGroupContext = React.createContext< VariantProps<typeof toggleVariants> >({ - size: "default", - variant: "default", + size: toggleVariants.defaultVariants?.size ?? "default", + variant: toggleVariants.defaultVariants?.variant ?? "default", })v2/src/components/ui/alert.tsx (1)
6-20: Complex CSS grid logic in alertVariants - consider documentation.The grid configuration with
has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr]is quite complex. Consider adding comments to explain the layout logic for maintainability.Add JSDoc comments to explain the grid layout:
+/** + * Alert variants with grid layout that automatically adjusts based on icon presence: + * - Without icon: single column spanning full width + * - With icon: two columns (icon column + content column) + */ const alertVariants = cva( "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",v2/src/components/ui/slider.tsx (1)
52-58: Verify dynamic thumb rendering performance.The component creates a new array and maps over it on each render. While functional, this could be optimized for better performance.
Consider memoizing the thumb elements:
+const thumbs = React.useMemo( + () => Array.from({ length: _values.length }, (_, index) => ( + <SliderPrimitive.Thumb + data-slot="slider-thumb" + key={index} + className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" + /> + )), + [_values.length] +) + -{Array.from({ length: _values.length }, (_, index) => ( - <SliderPrimitive.Thumb - data-slot="slider-thumb" - key={index} - className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" - /> -))} +{thumbs}v2/src/components/ui/calendar.tsx (1)
10-15: Consider explicit typing for better TypeScript support.The component props use
React.ComponentProps<typeof DayPicker>which is correct, but consider adding explicit typing for the custom props to improve developer experience and documentation.+export interface CalendarProps extends React.ComponentProps<typeof DayPicker> { + className?: string + classNames?: Partial<ClassNames> + showOutsideDays?: boolean +} function Calendar({ className, classNames, showOutsideDays = true, ...props -}: React.ComponentProps<typeof DayPicker>) { +}: CalendarProps) {v2/src/components/ui/hover-card.tsx (1)
8-12: Consider adding explicit return type annotation.While TypeScript can infer the return type, adding explicit annotations improves code documentation and catches potential issues early.
function HoverCard({ ...props -}: React.ComponentProps<typeof HoverCardPrimitive.Root>) { +}: React.ComponentProps<typeof HoverCardPrimitive.Root>): JSX.Element { return <HoverCardPrimitive.Root data-slot="hover-card" {...props} /> }v2/src/components/ui/badge.tsx (1)
33-34: Consider simplifying the type intersection.The complex type intersection could be simplified for better readability while maintaining type safety.
+export interface BadgeProps + extends React.ComponentProps<"span">, + VariantProps<typeof badgeVariants> { + asChild?: boolean +} function Badge({ className, variant, asChild = false, ...props -}: React.ComponentProps<"span"> & - VariantProps<typeof badgeVariants> & { asChild?: boolean }) { +}: BadgeProps) {v2/src/components/common/back-button.tsx (1)
17-28: Consider extracting variant types to improve maintainability.The inline type definitions for variant and size could be extracted to improve type reusability and ensure consistency with the Button component.
+import { Button, type ButtonProps } from "@/components/ui/button"; +export interface BackButtonProps { + className?: string; + variant?: ButtonProps['variant']; + size?: ButtonProps['size']; + icon?: JSX.Element; + label?: string; + showIcon?: boolean; + onClick?: () => void; +} const BackButton = ({ /* destructured props */ -}: { - className?: string; - variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"; - size?: "default" | "sm" | "lg" | "icon"; - icon?: JSX.Element; - label?: string; - showIcon?: boolean; - onClick?: () => void; -}) => { +}: BackButtonProps) => {v2/src/components/ui/tabs.tsx (1)
44-46: Consider extracting the long className for better maintainability.The TabsTrigger component has a very long className string that could be difficult to maintain and review. Consider extracting it to a separate variable or breaking it into logical groups.
+const tabsTriggerStyles = [ + "data-[state=active]:bg-background dark:data-[state=active]:text-foreground", + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring", + "dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30", + "text-foreground dark:text-muted-foreground", + "inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5", + "rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap", + "transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1", + "disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm", + "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" +].join(" "); + function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) { return ( <TabsPrimitive.Trigger data-slot="tabs-trigger" className={cn( - "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + tabsTriggerStyles, className )} {...props} /> ) }v2/src/components/ui/button.tsx (1)
7-9: Consider breaking down the long base className for better readability.The base className in
buttonVariantsis quite long and could benefit from being organized into logical groups for easier maintenance and review.+const baseButtonStyles = [ + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md", + "text-sm font-medium transition-all shrink-0 cursor-pointer outline-none", + "disabled:pointer-events-none disabled:opacity-50", + "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0", + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" +].join(" "); + const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer", + baseButtonStyles, { variants: {v2/src/components/ui/resizable.tsx (1)
41-44: Consider organizing the complex className for better maintainability.The ResizableHandle has an extremely long className that handles various states and orientations. While comprehensive, it could be more maintainable if organized into logical groups.
+const resizableHandleStyles = [ + "bg-border focus-visible:ring-ring relative flex w-px items-center justify-center", + "after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2", + "focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden", + "data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full", + "data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1", + "data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2", + "data-[panel-group-direction=vertical]:after:translate-x-0", + "[&[data-panel-group-direction=vertical]>div]:rotate-90" +].join(" "); + function ResizableHandle({ withHandle, className, ...props }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & { withHandle?: boolean }) { return ( <ResizablePrimitive.PanelResizeHandle data-slot="resizable-handle" className={cn( - "bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", + resizableHandleStyles, className )} {...props}v2/src/components/ui/navigation-menu.tsx (1)
61-63: Consider organizing the navigation trigger styles for better readability.The
navigationMenuTriggerStylehas a long className that could benefit from being broken down into logical groups.+const navigationTriggerStyles = [ + "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2", + "text-sm font-medium outline-none transition-[color,box-shadow]", + "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", + "disabled:pointer-events-none disabled:opacity-50", + "data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground", + "data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50", + "focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-1" +].join(" "); + const navigationMenuTriggerStyle = cva( - "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1" + navigationTriggerStyles )v2/src/components/ui/sheet.tsx (1)
75-78: Consider making the close button configurableThe close button is hardcoded inside
SheetContent, which may limit flexibility. Consider making it optional or configurable for cases where you want custom close button placement or styling.function SheetContent({ className, children, side = "right", + showCloseButton = true, + closeButtonClassName, ...props }: React.ComponentProps<typeof SheetPrimitive.Content> & { side?: "top" | "right" | "bottom" | "left" + showCloseButton?: boolean + closeButtonClassName?: string }) { return ( <SheetPortal> <SheetOverlay /> <SheetPrimitive.Content // ... existing props > {children} + {showCloseButton && ( <SheetPrimitive.Close className={cn( "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", + closeButtonClassName )}> <XIcon className="size-4" /> <span className="sr-only">Close</span> </SheetPrimitive.Close> + )} </SheetPrimitive.Content> </SheetPortal> ) }README.md (1)
168-176: Clarify data directory structureThe documentation mentions updating content in
v1/src/dataandv2/src/datadirectories, but notes that v2 uses a different structure. Consider providing more specific guidance about the actual v2 content structure.#### For v2: - 1. Update the content in the `v2/src/data` directory (if applicable) - - The v2 version uses a different structure, so refer to the specific files in the v2 directory + 1. Update the content based on the v2 structure: + - Modify component files directly as v2 may not use a centralized data directory + - Refer to the specific component files in `v2/src/components` and page files in `v2/src/app` - Replace or add images in the `v2/public` directoryv2/src/components/ui/background-beams-with-collision.tsx (2)
17-68: Consider making beam configurations customizable.The beam configurations are hardcoded, limiting reusability. Consider accepting them as props with these defaults.
export const BackgroundBeamsWithCollision = ({ children, className, + beams = defaultBeams, }: { children: React.ReactNode; className?: string; + beams?: BeamConfig[]; }) => { const containerRef = useRef<HTMLDivElement>(null); const parentRef = useRef<HTMLDivElement>(null); - const beams = [ + const defaultBeams = [ // ... existing beam configurations ];Also define the
BeamConfigtype for better type safety:type BeamConfig = { initialX: number; translateX: number; duration: number; repeatDelay: number; delay?: number; className?: string; };
91-94: Extract inline styles for better maintainability.The complex box-shadow style should be extracted to improve readability and maintainability.
+const CONTAINER_BOX_SHADOW = "0 0 24px rgba(34, 42, 53, 0.06), 0 1px 1px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(34, 42, 53, 0.04), 0 0 4px rgba(34, 42, 53, 0.08), 0 16px 68px rgba(47, 48, 55, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1) inset"; <div ref={containerRef} className="absolute bottom-0 bg-neutral-100 w-full inset-x-0 pointer-events-none" style={{ - boxShadow: - "0 0 24px rgba(34, 42, 53, 0.06), 0 1px 1px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(34, 42, 53, 0.04), 0 0 4px rgba(34, 42, 53, 0.08), 0 16px 68px rgba(47, 48, 55, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1) inset", + boxShadow: CONTAINER_BOX_SHADOW, }} ></div>v2/src/components/ui/carousel.tsx (1)
113-114: Simplify redundant orientation logic.Since
orientationhas a default value of "horizontal" in the function parameters, it will never be falsy, making the OR condition unnecessary.- orientation: - orientation || (opts?.axis === "y" ? "vertical" : "horizontal"), + orientation,v2/src/components/ui/dialog.tsx (1)
55-55: Remove redundant data-slot attribute.The
DialogPortalcomponent already addsdata-slot="dialog-portal"internally (line 24), so adding it again here is redundant.- <DialogPortal data-slot="dialog-portal"> + <DialogPortal>v2/src/components/ui/select.tsx (1)
27-51: Consider extracting the complex className for better maintainability.The trigger styling is comprehensive but the inline className is quite long. Consider extracting it to a constant or using a styling solution like cva (class-variance-authority) for better maintainability.
Example refactor using a constant:
const selectTriggerStyles = { base: "border-input data-placeholder:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", sizes: { default: "data-[size=default]:h-9", sm: "data-[size=sm]:h-8" }, value: "*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2", icon: "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" } // Then use: cn(selectTriggerStyles.base, selectTriggerStyles.sizes[size], selectTriggerStyles.value, selectTriggerStyles.icon, className)v2/src/components/ui/table.tsx (1)
73-73: Consider text truncation for long content.Both
TableHeadandTableCellusewhitespace-nowrap, which prevents text wrapping. This could cause layout issues with long content, making cells extremely wide.Consider adding a truncation option:
function TableHead({ className, truncate, ...props }: React.ComponentProps<"th"> & { truncate?: boolean }) { return ( <th data-slot="table-head" className={cn( "text-foreground h-10 px-2 text-left align-middle font-medium", truncate ? "truncate" : "whitespace-nowrap", "[&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className )} {...props} /> ) }This allows developers to choose between nowrap and truncation based on their content needs.
Also applies to: 86-86
v2/src/components/ui/chart.tsx (1)
237-237: Consider consistent number formatting.Using
toLocaleString()without parameters relies on the user's system locale, which might cause inconsistent number formatting across users.Consider accepting a locale prop or using the app's configured locale:
- {item.value.toLocaleString()} + {item.value.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}Or make it configurable:
interface ChartTooltipContentProps { // ... existing props locale?: string; numberFormat?: Intl.NumberFormatOptions; } // Usage: {item.value.toLocaleString(locale, numberFormat)}v2/src/components/ui/alert-dialog.tsx (1)
1-158: LGTM! Well-structured alert dialog implementation.This is a comprehensive and well-implemented alert dialog component suite. The implementation follows React and Radix UI best practices with proper prop forwarding, TypeScript typing, and consistent styling patterns.
Optional enhancement: Consider adding forwardRef support
For better ref handling in advanced use cases, consider wrapping components with
React.forwardRef:-function AlertDialog({ +const AlertDialog = React.forwardRef< + React.ElementRef<typeof AlertDialogPrimitive.Root>, + React.ComponentProps<typeof AlertDialogPrimitive.Root> +>(({ ...props -}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) { +}, ref) => { - return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} /> + return <AlertDialogPrimitive.Root ref={ref} data-slot="alert-dialog" {...props} /> -} +}) +AlertDialog.displayName = "AlertDialog"v2/src/components/ui/sidebar.tsx (1)
1-726: Comprehensive and well-architected sidebar system!Excellent implementation with:
- Robust state management via React Context
- Mobile-responsive design with Sheet component for mobile
- Comprehensive set of composable sub-components
- Good accessibility with ARIA attributes and keyboard navigation
- Proper TypeScript typing throughout
- Smart tooltip handling for collapsed state
Consider documenting the various
collapsiblemodes ("offcanvas", "icon", "none") and sidebar variants ("sidebar", "floating", "inset") to help other developers understand when to use each option.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (90)
bun.lockbis excluded by!**/bun.lockbv1/public/favicon-16x16.icois excluded by!**/*.icov1/public/favicon-32x32.icois excluded by!**/*.icov1/public/favicon-48x48.icois excluded by!**/*.icov1/public/favicon-64x64.icois excluded by!**/*.icov1/public/images/analytical-skills.pngis excluded by!**/*.pngv1/public/images/buy-me-a-coffee.pngis excluded by!**/*.pngv1/public/images/collaboration.pngis excluded by!**/*.pngv1/public/images/ko-fi.pngis excluded by!**/*.pngv1/public/images/logical-thinking.pngis excluded by!**/*.pngv1/public/images/placeholder.pngis excluded by!**/*.pngv1/public/images/razorpay-logo.pngis excluded by!**/*.pngv1/public/skills/aws.svgis excluded by!**/*.svgv1/public/skills/cpp.pngis excluded by!**/*.pngv1/public/skills/css.svgis excluded by!**/*.svgv1/public/skills/dart.svgis excluded by!**/*.svgv1/public/skills/docker.pngis excluded by!**/*.pngv1/public/skills/docker.svgis excluded by!**/*.svgv1/public/skills/express.svgis excluded by!**/*.svgv1/public/skills/firebase.svgis excluded by!**/*.svgv1/public/skills/flutter.svgis excluded by!**/*.svgv1/public/skills/getx.pngis excluded by!**/*.pngv1/public/skills/git.svgis excluded by!**/*.svgv1/public/skills/github.svgis excluded by!**/*.svgv1/public/skills/html.svgis excluded by!**/*.svgv1/public/skills/javascript.svgis excluded by!**/*.svgv1/public/skills/jenkins.svgis excluded by!**/*.svgv1/public/skills/kubernetes.svgis excluded by!**/*.svgv1/public/skills/mongodb.svgis excluded by!**/*.svgv1/public/skills/mysql.svgis excluded by!**/*.svgv1/public/skills/nestjs.svgis excluded by!**/*.svgv1/public/skills/nextjs.pngis excluded by!**/*.pngv1/public/skills/nodejs.svgis excluded by!**/*.svgv1/public/skills/postgresql.svgis excluded by!**/*.svgv1/public/skills/react.svgis excluded by!**/*.svgv1/public/skills/redis.svgis excluded by!**/*.svgv1/public/skills/redux.svgis excluded by!**/*.svgv1/public/skills/sass.svgis excluded by!**/*.svgv1/public/skills/socket-io.pngis excluded by!**/*.pngv1/public/skills/sqlite.svgis excluded by!**/*.svgv1/public/skills/terraform.svgis excluded by!**/*.svgv1/public/skills/typescript.svgis excluded by!**/*.svgv1/public/skills/ubuntu.pngis excluded by!**/*.pngv1/screenshot-desktop.pngis excluded by!**/*.pngv1/src/app/apple-icon.pngis excluded by!**/*.pngv1/src/app/favicon.icois excluded by!**/*.icov2/public/apple-icon.pngis excluded by!**/*.pngv2/public/favicon-16x16.icois excluded by!**/*.icov2/public/favicon-32x32.icois excluded by!**/*.icov2/public/favicon-48x48.icois excluded by!**/*.icov2/public/favicon-64x64.icois excluded by!**/*.icov2/public/icon.pngis excluded by!**/*.pngv2/public/images/analytical-skills.pngis excluded by!**/*.pngv2/public/images/buy-me-a-coffee.pngis excluded by!**/*.pngv2/public/images/collaboration.pngis excluded by!**/*.pngv2/public/images/ko-fi.pngis excluded by!**/*.pngv2/public/images/logical-thinking.pngis excluded by!**/*.pngv2/public/images/placeholder.pngis excluded by!**/*.pngv2/public/images/razorpay-logo.pngis excluded by!**/*.pngv2/public/skills/aws.svgis excluded by!**/*.svgv2/public/skills/cpp.pngis excluded by!**/*.pngv2/public/skills/css.svgis excluded by!**/*.svgv2/public/skills/dart.svgis excluded by!**/*.svgv2/public/skills/docker.pngis excluded by!**/*.pngv2/public/skills/docker.svgis excluded by!**/*.svgv2/public/skills/express.svgis excluded by!**/*.svgv2/public/skills/firebase.svgis excluded by!**/*.svgv2/public/skills/flutter.svgis excluded by!**/*.svgv2/public/skills/getx.pngis excluded by!**/*.pngv2/public/skills/git.svgis excluded by!**/*.svgv2/public/skills/github.svgis excluded by!**/*.svgv2/public/skills/html.svgis excluded by!**/*.svgv2/public/skills/javascript.svgis excluded by!**/*.svgv2/public/skills/jenkins.svgis excluded by!**/*.svgv2/public/skills/kubernetes.svgis excluded by!**/*.svgv2/public/skills/mongodb.svgis excluded by!**/*.svgv2/public/skills/mysql.svgis excluded by!**/*.svgv2/public/skills/nestjs.svgis excluded by!**/*.svgv2/public/skills/nextjs.pngis excluded by!**/*.pngv2/public/skills/nodejs.svgis excluded by!**/*.svgv2/public/skills/postgresql.svgis excluded by!**/*.svgv2/public/skills/react.svgis excluded by!**/*.svgv2/public/skills/redis.svgis excluded by!**/*.svgv2/public/skills/redux.svgis excluded by!**/*.svgv2/public/skills/sass.svgis excluded by!**/*.svgv2/public/skills/socket-io.pngis excluded by!**/*.pngv2/public/skills/sqlite.svgis excluded by!**/*.svgv2/public/skills/terraform.svgis excluded by!**/*.svgv2/public/skills/typescript.svgis excluded by!**/*.svgv2/public/skills/ubuntu.pngis excluded by!**/*.png
📒 Files selected for processing (94)
.github/FUNDING.yml(1 hunks).github/ISSUE_TEMPLATE/bug_report.yml(1 hunks).github/ISSUE_TEMPLATE/feature_request.yml(1 hunks).github/pull_request_template.md(1 hunks).gitignore(2 hunks).vscode/settings.json(0 hunks)CONTRIBUTING.md(2 hunks)README.md(3 hunks)package.json(1 hunks)v1/.gitignore(1 hunks)v1/README.md(1 hunks)v1/package.json(1 hunks)v1/src/app/sitemap.xml(0 hunks)v2/.gitignore(1 hunks)v2/README.md(1 hunks)v2/components.json(1 hunks)v2/eslint.config.mjs(1 hunks)v2/next.config.ts(1 hunks)v2/package.json(1 hunks)v2/postcss.config.mjs(1 hunks)v2/src/app/about/page.tsx(1 hunks)v2/src/app/globals.css(1 hunks)v2/src/app/layout.tsx(1 hunks)v2/src/app/manifest.ts(1 hunks)v2/src/app/not-found.tsx(1 hunks)v2/src/app/page.tsx(1 hunks)v2/src/app/robots.txt(1 hunks)v2/src/app/sitemap.xml(1 hunks)v2/src/components/about/bio-skills-section.tsx(1 hunks)v2/src/components/about/index.tsx(1 hunks)v2/src/components/about/main-section.tsx(1 hunks)v2/src/components/about/profile-card.tsx(1 hunks)v2/src/components/about/section-title.tsx(1 hunks)v2/src/components/about/stats-section.tsx(1 hunks)v2/src/components/common/back-button.tsx(1 hunks)v2/src/components/home/hero-section.tsx(1 hunks)v2/src/components/home/index.tsx(1 hunks)v2/src/components/nav/custom-nav-bar.tsx(1 hunks)v2/src/components/theme-mode-toggle.tsx(1 hunks)v2/src/components/theme-provider.tsx(1 hunks)v2/src/components/ui/accordion.tsx(1 hunks)v2/src/components/ui/alert-dialog.tsx(1 hunks)v2/src/components/ui/alert.tsx(1 hunks)v2/src/components/ui/aspect-ratio.tsx(1 hunks)v2/src/components/ui/avatar.tsx(1 hunks)v2/src/components/ui/background-beams-with-collision.tsx(1 hunks)v2/src/components/ui/badge.tsx(1 hunks)v2/src/components/ui/breadcrumb.tsx(1 hunks)v2/src/components/ui/button.tsx(1 hunks)v2/src/components/ui/calendar.tsx(1 hunks)v2/src/components/ui/card.tsx(1 hunks)v2/src/components/ui/carousel.tsx(1 hunks)v2/src/components/ui/chart.tsx(1 hunks)v2/src/components/ui/checkbox.tsx(1 hunks)v2/src/components/ui/collapsible.tsx(1 hunks)v2/src/components/ui/command.tsx(1 hunks)v2/src/components/ui/context-menu.tsx(1 hunks)v2/src/components/ui/dialog.tsx(1 hunks)v2/src/components/ui/drawer.tsx(1 hunks)v2/src/components/ui/dropdown-menu.tsx(1 hunks)v2/src/components/ui/flip-words.tsx(1 hunks)v2/src/components/ui/form.tsx(1 hunks)v2/src/components/ui/hover-card.tsx(1 hunks)v2/src/components/ui/input-otp.tsx(1 hunks)v2/src/components/ui/input.tsx(1 hunks)v2/src/components/ui/label.tsx(1 hunks)v2/src/components/ui/menubar.tsx(1 hunks)v2/src/components/ui/navigation-menu.tsx(1 hunks)v2/src/components/ui/pagination.tsx(1 hunks)v2/src/components/ui/popover.tsx(1 hunks)v2/src/components/ui/progress.tsx(1 hunks)v2/src/components/ui/radio-group.tsx(1 hunks)v2/src/components/ui/resizable-navbar.tsx(1 hunks)v2/src/components/ui/resizable.tsx(1 hunks)v2/src/components/ui/scroll-area.tsx(1 hunks)v2/src/components/ui/select.tsx(1 hunks)v2/src/components/ui/separator.tsx(1 hunks)v2/src/components/ui/sheet.tsx(1 hunks)v2/src/components/ui/sidebar.tsx(1 hunks)v2/src/components/ui/skeleton.tsx(1 hunks)v2/src/components/ui/slider.tsx(1 hunks)v2/src/components/ui/sonner.tsx(1 hunks)v2/src/components/ui/switch.tsx(1 hunks)v2/src/components/ui/table.tsx(1 hunks)v2/src/components/ui/tabs.tsx(1 hunks)v2/src/components/ui/textarea.tsx(1 hunks)v2/src/components/ui/toggle-group.tsx(1 hunks)v2/src/components/ui/toggle.tsx(1 hunks)v2/src/components/ui/tooltip.tsx(1 hunks)v2/src/hooks/use-mobile.ts(1 hunks)v2/src/lib/utils.ts(1 hunks)v2/tailwind.config.ts(1 hunks)v2/tsconfig.json(1 hunks)vercel.json(1 hunks)
💤 Files with no reviewable changes (2)
- .vscode/settings.json
- v1/src/app/sitemap.xml
🧰 Additional context used
🧬 Code Graph Analysis (14)
v2/src/app/about/page.tsx (1)
v2/src/components/about/index.tsx (1)
AboutPageComponent(3-9)
v2/src/app/page.tsx (1)
v2/src/components/home/index.tsx (1)
HomePageComponent(3-9)
v2/next.config.ts (1)
v1/next.config.js (1)
nextConfig(5-15)
v2/src/components/about/stats-section.tsx (1)
v2/src/components/ui/card.tsx (1)
CardContent(91-91)
v2/src/components/ui/toggle-group.tsx (1)
v2/src/components/ui/toggle.tsx (1)
toggleVariants(47-47)
v2/src/app/layout.tsx (1)
v2/src/components/theme-provider.tsx (1)
ThemeProvider(5-10)
v2/src/components/ui/calendar.tsx (1)
v2/src/components/ui/button.tsx (1)
buttonVariants(59-59)
v2/src/components/ui/navigation-menu.tsx (1)
v2/src/app/layout.tsx (1)
viewport(17-26)
v2/src/components/ui/carousel.tsx (1)
v2/src/components/ui/button.tsx (1)
Button(59-59)
v2/src/components/ui/form.tsx (1)
v2/src/components/ui/label.tsx (1)
Label(24-24)
v2/src/components/ui/pagination.tsx (1)
v2/src/components/ui/button.tsx (2)
Button(59-59)buttonVariants(59-59)
v2/src/components/ui/command.tsx (1)
v2/src/components/ui/dialog.tsx (5)
Dialog(125-125)DialogHeader(130-130)DialogTitle(133-133)DialogDescription(128-128)DialogContent(127-127)
v2/src/components/ui/alert-dialog.tsx (1)
v2/src/components/ui/button.tsx (1)
buttonVariants(59-59)
v2/src/components/home/hero-section.tsx (2)
v2/src/components/ui/background-beams-with-collision.tsx (1)
BackgroundBeamsWithCollision(7-98)v2/src/components/ui/flip-words.tsx (1)
FlipWords(7-94)
🪛 LanguageTool
.github/pull_request_template.md
[style] ~21-~21: Consider using a different verb for a more formal wording.
Context: ...cator customization, etc.) - [ ] I have fixed existing issues (e.g., performance, edg...
(FIX_RESOLVE)
[style] ~22-~22: ‘overall structure’ might be wordy. Consider a shorter alternative.
Context: ..., edge cases) - [ ] I have improved the overall structure or optimized the codebase ### Document...
(EN_WORDINESS_PREMIUM_OVERALL_STRUCTURE)
[style] ~74-~74: To reduce wordiness, try specifying a number or using “many” or “numerous” instead.
Context: ...carousel renders efficiently, even with a large number of items ### How did you verify your code...
(LARGE_NUMBER_OF)
🪛 Biome (1.9.4)
v2/src/components/ui/chart.tsx
[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (104)
.gitignore (2)
20-20: Recursive ignore for macOS system files
Adding**/.DS_Storeensures that.DS_Storefiles in all subdirectories are ignored consistently, complementing the existing root-level.DS_Storerule.
41-43: Include additional lock files in version control ignores
The new entries foryarn.lock,bun.lock, andbun.lockbcover all supported package managers in the monorepo. This aligns with the updated structure and prevents accidental checking in of lock files from various workspaces..github/FUNDING.yml (1)
2-5: Expand funding options
The added Ko-fi, Buy Me a Coffee, Thanks Dev, and Open Collective entries broaden the project's sponsorship avenues. Links appear correctly formatted and consistent with the GitHub Sponsors entry.v2/.gitignore (1)
1-48: Add tailored.gitignorefor the v2 directory
This new file correctly excludes dependencies (node_modules,.pnp,.yarn), build outputs (.next,out,build), cache and debug artifacts (coverage, logs), environment files, lock files (including Bun), TypeScript build info, and system files like.DS_Store. It mirrors best practices for a standalone Next.js project within the monorepo..github/ISSUE_TEMPLATE/feature_request.yml (1)
50-50: Update Code of Conduct link to this repository
The URL now correctly points toportfolio-nextjsinstead of the oldflutter_carousel_widgetrepo, ensuring contributors are guided to the right document..github/ISSUE_TEMPLATE/bug_report.yml (1)
54-54: Align Code of Conduct link with current repo
The updated link referencesportfolio-nextjs, matching the feature request template and overall repository context.v2/src/app/about/page.tsx (1)
1-5: Approve: Simple wrapper for About page.The
Aboutcomponent correctly imports and rendersAboutPageComponent, adhering to Next.js 15 app router conventions.v2/src/app/page.tsx (1)
1-5: Approve: Simple wrapper for Home page.The
Homecomponent cleanly imports and rendersHomePageComponent, following app router best practices.v2/src/lib/utils.ts (1)
4-6: LGTM! Perfect implementation of the standard cn utility.This is a well-established pattern for Tailwind CSS projects. The combination of
clsxfor conditional class handling andtwMergefor Tailwind conflict resolution is exactly what's needed for a robust class name utility.v2/src/components/ui/aspect-ratio.tsx (1)
1-11: LGTM! Excellent wrapper implementation following best practices.The component demonstrates several good practices:
- Proper use of
"use client"directive for browser-specific functionality- Clean forwarding of all props to maintain the full Radix UI API
- Addition of
data-slotattribute for component identification- Consistent naming and export pattern
v2/src/components/theme-provider.tsx (1)
1-10: LGTM! Clean theme provider implementation.This is a well-implemented wrapper component that follows Next.js app directory best practices for client-side providers. The TypeScript typing and prop forwarding are correctly handled.
v2/tailwind.config.ts (1)
3-18: LGTM! Well-structured Tailwind configuration.The Tailwind configuration follows best practices with:
- Proper content paths for class scanning
- CSS variable integration for theming
- Correct TypeScript typing with
satisfies Config- Clean, minimal setup
v2/components.json (1)
1-21: LGTM! Well-structured shadcn/ui configuration.The configuration follows shadcn/ui best practices with appropriate settings for:
- React Server Components support
- TypeScript JSX configuration
- Tailwind CSS integration with neutral base color
- Clear path aliases for better import organization
- Lucide icon library integration
This setup aligns well with the v2 portfolio's modern stack (Next.js 15 + Tailwind CSS v4).
v2/src/components/ui/skeleton.tsx (1)
1-13: LGTM! Clean and well-typed skeleton component.The implementation follows React and TypeScript best practices:
- Proper use of
React.ComponentProps<"div">for type safetycnutility for class merging and conflict resolutiondata-slotattribute for consistent component identification- Flexible prop spreading for extensibility
- Appropriate skeleton styling with pulse animation
This component integrates well with the v2 UI component library structure.
v2/src/components/theme-mode-toggle.tsx (1)
24-26: LGTM! Excellent icon animation implementation.The icon transition animations with proper dark mode handling are well-implemented. The use of
absolutepositioning for the Moon icon with scale and rotation transforms creates a smooth visual transition between light and dark modes.v2/eslint.config.mjs (2)
16-27: LGTM! Comprehensive unused variables configuration.The TypeScript unused variables rule configuration is well-thought-out and follows common conventions:
- Allows underscore-prefixed variables for intentionally unused items
- Covers all scenarios (vars, args, caught errors, destructured arrays)
- Enables
ignoreRestSiblingsfor object destructuring patternsThis configuration strikes a good balance between enforcing clean code and allowing flexibility for common patterns.
1-10: Modern ESLint flat config setup is excellent.The use of
FlatCompatfor compatibility with legacy Next.js configs while using the modern flat config format is the recommended approach. The dynamic directory resolution ensures the config works correctly regardless of the execution context.v2/src/components/about/section-title.tsx (1)
5-11: LGTM! Well-implemented scroll animation.The motion configuration is excellent:
whileInViewwithviewport={{ once: true }}prevents re-triggering and improves performance- Smooth fade and slide animation with appropriate duration
- Good initial offset (y: 20) for subtle entrance effect
v1/README.md (1)
1-44: LGTM! Well-structured documentation.The README provides comprehensive documentation for the v1 version with clear setup instructions, learning resources, and deployment guidance. The monorepo context is well-explained (after fixing the directory reference issue).
v2/README.md (1)
1-44: Documentation looks good and clearly explains the v2 structure.The README effectively documents the v2 portfolio version, clearly distinguishes it from v1 in the monorepo structure, and provides standard Next.js development instructions. The technology stack (Next.js 15 + Tailwind CSS v4) is clearly mentioned.
v2/src/components/ui/avatar.tsx (3)
8-22: Avatar component wrapping: correct default styling and props forwarding.
The component correctly wraps the RadixAvatarPrimitive.Root, appliesdata-slotfor identification, and merges custom classes viacn. Props are forwarded properly.
24-35: AvatarImage component implementation is correct.
The image wrapper fills the container, appliesdata-slot, and merges classes for aspect ratio and sizing.
37-51: AvatarFallback component implementation is correct.
Provides a styled fallback with muted background, centered content, and appropriate class merging.v2/src/components/ui/label.tsx (1)
8-22: Label component wrapping: correct styling and accessibility variants.
The wrapper ofLabelPrimitive.Rootapplies appropriate default classes, forwards props, and addsdata-slotfor slot identification.v2/src/components/ui/collapsible.tsx (3)
5-9: Collapsible Root wrapper implementation is correct.
Forwards props and appliesdata-slot="collapsible"as intended.
11-19: CollapsibleTrigger wrapper is correct.
Forwards props toCollapsiblePrimitive.CollapsibleTriggerwith slot identification.
22-30: CollapsibleContent wrapper is correct.
Forwards props toCollapsiblePrimitive.CollapsibleContentwith slot identification.v2/tsconfig.json (1)
1-27: LGTM! Well-configured TypeScript setup.The TypeScript configuration is well-structured with appropriate settings for a Next.js 15 project:
- Strict mode enabled for better type safety
- Proper Next.js plugin integration
- Correct path aliasing setup
- Appropriate include/exclude patterns
v2/src/components/ui/checkbox.tsx (1)
16-19: Excellent comprehensive styling with accessibility support.The styling classes cover all necessary states including focus, disabled, invalid, and dark mode variants. The use of Radix's data attributes for state management is a best practice.
v2/src/app/globals.css (4)
1-6: Excellent modern Tailwind CSS v4 setup with custom variants.The use of Tailwind CSS v4's new
@importsyntax and custom dark variant definition demonstrates modern CSS architecture. The inclusion oftw-animate-cssprovides enhanced animation capabilities.
7-43: Well-designed theme system with semantic color mapping.The inline theme definition creates a solid foundation for the design system by:
- Using semantic color names that map to CSS custom properties
- Including radius variables for consistent border-radius scaling
- Providing specialized sidebar colors for complex layouts
This approach ensures maintainability and consistency across the application.
45-112: Excellent use of OKLCH color space for precise color definition.The color system demonstrates several best practices:
- OKLCH color space: Provides better perceptual uniformity and color accuracy compared to traditional RGB/HSL
- Comprehensive palette: Covers all semantic use cases including charts, destructive states, and specialized sidebar colors
- Proper dark mode support: Thoughtful color adjustments that maintain contrast and readability
- Semantic naming: Colors are named by purpose rather than appearance, improving maintainability
The color values appear well-balanced with appropriate contrast ratios for accessibility.
114-121: Clean base layer implementation with consistent global styles.The base layer applies consistent styling patterns:
- Global border and outline styles using theme colors
- Proper background and text color application using CSS custom properties
This ensures visual consistency across all elements while leveraging the theme system.
v2/src/components/ui/switch.tsx (1)
8-29: Excellent implementation of accessible switch component.This is a well-crafted switch component that effectively wraps Radix UI primitives with comprehensive Tailwind styling. The implementation includes:
- Proper TypeScript typing with
React.ComponentProps- Comprehensive state management (checked/unchecked, focus, disabled, dark mode)
- Smooth animations with
transition-allandtransition-transform- Accessibility handled by Radix UI primitives
- Clean prop forwarding pattern
The extensive Tailwind classes provide thorough theming support, and the data-slot attributes aid in component identification.
v2/src/components/ui/radio-group.tsx (2)
9-20: Clean RadioGroup container implementation.The RadioGroup component provides a simple, focused wrapper with appropriate grid layout and gap spacing. The implementation correctly forwards all props and maintains consistency with other UI components in the library.
22-43: Comprehensive RadioGroupItem with excellent validation support.The RadioGroupItem implementation is particularly well-done with:
- Comprehensive styling for all states (focus, disabled, validation errors)
- Proper use of
aria-invalidfor form validation- Clean indicator implementation with CircleIcon
- Consistent dark mode support
- Proper aspect ratio and sizing
The validation styling with
aria-invalid:ring-destructiveandaria-invalid:border-destructiveprovides clear visual feedback for form errors.v2/src/components/ui/progress.tsx (1)
8-29: Solid progress component with correct transform logic.This progress component is well-implemented with:
- Proper TypeScript typing including the
valueprop- Correct transform calculation:
translateX(-${100 - (value || 0)}%)- Safe fallback to 0 when value is undefined/null
- Appropriate styling with rounded corners and transition effects
- Consistent use of design system colors (
bg-primary/20,bg-primary)The transform logic correctly moves the indicator from left (0% progress) to right (100% progress).
v2/src/components/ui/separator.tsx (1)
8-26: Well-designed separator with excellent orientation support.This separator component demonstrates clean implementation with:
- Sensible defaults (
orientation = "horizontal",decorative = true)- Proper orientation handling using Tailwind's data attribute selectors
- Consistent styling patterns with other UI components
- Flexible sizing:
h-px w-fullfor horizontal,h-full w-pxfor vertical- Clean prop forwarding and TypeScript typing
The use of
data-[orientation=...]selectors is a modern, maintainable approach to conditional styling.v2/src/components/nav/custom-nav-bar.tsx (1)
45-105: LGTM! Well-structured responsive navigation component.The component demonstrates good React practices with proper state management, responsive design, and accessibility considerations. The mobile menu toggle functionality is correctly implemented.
v2/src/components/about/profile-card.tsx (2)
25-32: LGTM! Well-implemented gradient header with proper avatar positioning.The gradient background and avatar positioning create an attractive profile card design. The use of absolute positioning with proper transforms is correct.
16-22: Good use of motion animations with proper accessibility.The animation implementation with
whileInViewandviewport={{ once: true }}is well done for performance and accessibility.v2/src/components/ui/toggle.tsx (1)
9-29: Excellent use of class-variance-authority for variant management.The toggleVariants implementation is comprehensive and well-structured, providing good flexibility with variant and size options while maintaining consistent styling.
v2/src/app/not-found.tsx (2)
17-25: LGTM: Proper hydration handling implemented.The mounted state pattern correctly prevents hydration mismatches between server and client rendering, which is essential for client-side components in Next.js.
69-101: LGTM: Well-structured error page with proper navigation.The card implementation provides a good user experience with:
- Clear error messaging
- Proper visual hierarchy
- Accessible navigation options (back button and homepage link)
- Consistent styling with the design system
v2/src/components/about/bio-skills-section.tsx (1)
6-21: LGTM: Well-organized skills data structure.The skills array is properly categorized and provides good coverage of full-stack development technologies. The categorization by frontend, backend, mobile, cloud, and devops is logical and comprehensive.
v2/src/components/home/hero-section.tsx (1)
16-23: LGTM: Well-implemented hero heading with smooth animations.The motion animation provides a nice entrance effect with appropriate timing and easing. The text hierarchy and styling are well-executed.
v1/package.json (1)
1-9: LGTM: Well-structured package metadata for monorepo.The package configuration is appropriate for a workspace in a monorepo setup:
- Proper naming convention with v1 suffix
- Private package to prevent accidental publishing
- Custom dev port (4000) to avoid conflicts with v2
- Standard Next.js scripts
v2/package.json (2)
51-53:✅ Verification successful
Verify React 19 compatibility with all dependencies.
React 19.1.0 is a relatively new major version. Ensure all dependencies, especially UI libraries like Radix UI components, are compatible with React 19.
🏁 Script executed:
#!/bin/bash # Check React 19 compatibility for key dependencies echo "Checking React 19 compatibility for key dependencies..." # Check Radix UI components React 19 support curl -s https://registry.npmjs.org/@radix-ui/react-dialog/latest | jq '.peerDependencies.react // "No React peer dependency found"' # Check other critical dependencies for pkg in "react-hook-form" "next-themes" "embla-carousel-react" "react-resizable-panels"; do echo "Checking $pkg..." curl -s https://registry.npmjs.org/$pkg/latest | jq -r "\"$pkg: \" + (.peerDependencies.react // \"No React peer dependency found\")" doneLength of output: 1840
React 19 Compatibility Verified
All key UI and form libraries explicitly list React 19 in their peerDependency ranges, and
^19.1.0falls within^19.0–compatible semver. No changes required.• @radix-ui/react-dialog:
^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
• react-hook-form:^16.8.0 || ^17 || ^18 || ^19
• next-themes:^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
• embla-carousel-react:^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
• react-resizable-panels:^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
71-71:✅ Verification successful
Verify Tailwind CSS v4 stability and compatibility.
Tailwind CSS v4.1.7 appears to be used, which might be a pre-release or alpha version. Ensure this version is stable for production use.
🌐 Web query:
What is the latest stable version of Tailwind CSS and is version 4.1.7 considered stable for production use?💡 Result:
Latest Stable Version of Tailwind CSS
As of May 29, 2025, the 4.x series is the latest version family for Tailwind CSS. The official Tailwind CSS website and upgrade guides confirm that v4.x is the current major release, introduced with significant performance, configuration, and feature improvements for the modern web[1][3][5].
However, the most recent specific version referenced in the official Tailwind Labs GitHub releases is v4.1.5[8], with user discussions mentioning v4.1.7 as being in use as of late May 2025[10]. This strongly suggests that version 4.1.7 exists and is currently available to users.
Is v4.1.7 Stable for Production Use?
All minor and patch updates in the Tailwind CSS 4.x series, including v4.1.7, follow semantic versioning and are part of the stable mainline release—not a preview, beta, or alpha. The 4.x series as a whole is a stable, production-ready framework[1][3][5]. While there is a GitHub issue mentioning performance concerns with v4.1.7[10], this does not indicate that the release is unstable—such feedback is common and addressed through subsequent patch updates.
Key points:
- Version 4.1.7 is a stable minor/patch release in the 4.x series.
- The 4.x series is recommended for production, as evidenced by the official documentation and upgrade guides[3][5].
- If users encounter specific issues (such as slow build times reported in v4.1.7[10]), it is best practice to monitor for patches or report bugs, but this does not make the version unsuitable for production overall.
Summary Table
Version Release Status Production-Ready Notes v4.1.5 (latest official release per GitHub) Stable Yes Documented release, no major blockers[8]. v4.1.7 (in user reports, not yet listed on GitHub release page) Stable Yes No reports of critical instability, though some users mention performance issues[10]. Conclusion
- The latest stable major version of Tailwind CSS is v4.x (v4.1.5 officially listed, v4.1.7 in use by community)[8][10].
- Tailwind CSS v4.1.7 is considered stable and suitable for production use, though, as with any actively developed tool, you should test in your environment and keep watch for patches when specific issues are reported[3][10].
Citations:
- 1: https://tailwindcss.com/blog/tailwindcss-v4
- 2: https://tailwindcss.com/blog
- 3: https://tailwindcss.com/docs/upgrade-guide
- 4: https://www.youtube.com/watch?v=sHnG8tIYMB4
- 5: https://mojoauth.com/blog/tailwind-css-v4-0-everything-you-need-to-know/
- 6: https://tailwindcss.com/docs/optimizing-for-production
- 7: https://docs.hyva.io/hyva-themes/working-with-tailwindcss/supported-versions.html
- 8: https://github.com/tailwindlabs/tailwindcss/releases
- 9: https://dev.to/gerryleonugroho/why-upgrading-to-tailwindcss-414-might-be-the-best-decision-you-make-this-year-4bc5
- 10: tailwindlabs/tailwindcss#18101
Tailwind CSS v4.1.7 is a stable production release
Verified that v4.1.7 is part of Tailwind’s official 4.x stable line (not an alpha/beta) and is safe for production use—no changes required.• File: v2/package.json
Line 71:"tailwindcss": "^4.1.7",v2/src/components/ui/toggle-group.tsx (1)
56-57: Verify data attribute precedence logic.The data attributes use
context.variant || variantwhich means context takes precedence over component props. Ensure this is the intended behavior for the design system.The fallback logic is implemented correctly, giving context values precedence over individual item props, which is appropriate for a toggle group where items should inherit group styling by default.
v2/src/components/ui/alert.tsx (1)
28-34: Excellent accessibility implementation.The Alert component properly implements the
role="alert"attribute for screen readers, ensuring good accessibility for important messages.v2/src/components/ui/calendar.tsx (2)
37-42: Excellent conditional styling for range mode.The conditional styling logic for range selection is well-implemented, properly handling both range and single-date selection modes with appropriate border radius classes.
62-69: Great use of custom icon components.The custom IconLeft and IconRight components provide consistent styling while maintaining flexibility. The size-4 class ensures proper icon sizing across the calendar interface.
v2/src/components/ui/hover-card.tsx (2)
29-29: Good use of Portal with data-slot attribute.The Portal implementation ensures the hover card content renders outside the DOM hierarchy while maintaining proper data attributes for styling and testing.
34-37:❓ Verification inconclusive
Verify animation class compatibility with Tailwind CSS v4.
The component uses extensive animation classes. Ensure these animation utilities are compatible with Tailwind CSS v4, as some animation classes may have changed between versions.
🏁 Script executed:
#!/bin/bash # Check if animation classes are defined in Tailwind CSS v4 config rg -A 3 -B 3 "animate-in|animate-out|fade-in|fade-out|zoom-in|zoom-out|slide-in" v2/tailwind.config.ts v2/src/app/globals.cssLength of output: 127
Double-check Tailwind v4 animation utilities
I didn’t find custom definitions for the classes used here (animate-in,fade-in,zoom-in,slide-in, etc.) in your Tailwind config or global CSS. Please verify that these utilities are available out-of-the-box in Tailwind v4 or that you’ve installed and configured the@tailwindcss/animateplugin.• v2/tailwind.config.ts: confirm the animation plugin is included
• v2/src/app/globals.css: ensure no required overrides are missingv2/src/components/ui/badge.tsx (2)
7-26: Excellent variant system with comprehensive styling.The badge variants are well-defined with consistent patterns across all variants. The styling includes accessibility features like focus-visible states and aria-invalid handling, which is commendable.
35-43: Solid implementation of the asChild pattern.The conditional component rendering with the asChild pattern is correctly implemented, allowing flexible composition while maintaining proper data attributes and styling.
v2/src/components/common/back-button.tsx (1)
45-52: Good conditional rendering pattern for icon and label.The conditional rendering logic for the icon and label is clean and provides good fallback defaults while maintaining flexibility.
v2/src/components/ui/scroll-area.tsx (2)
1-29: LGTM! Well-structured ScrollArea component.The ScrollArea component implementation is clean and follows good practices:
- Proper TypeScript typing with extending Radix UI props
- Good use of the
cnutility for class merging- Appropriate focus ring styling for accessibility
- Semantic data-slot attributes for styling hooks
31-56: LGTM! ScrollBar component with proper orientation support.The ScrollBar component is well-implemented with:
- Support for both vertical and horizontal orientations
- Conditional styling based on orientation
- Proper border and sizing for different orientations
- Clean TypeScript typing
v2/src/components/ui/flip-words.tsx (1)
63-90: LGTM! Excellent staggered animation implementation.The nested animation structure with word and letter-level staggered delays creates a smooth, professional effect. The timing calculations are well thought out with appropriate delays.
v2/src/components/about/main-section.tsx (1)
84-101: LGTM! Clean layout structure with proper responsive design.The layout implementation is well-structured with:
- Proper z-index layering to ensure content appears above background
- Responsive grid that stacks on mobile and arranges side-by-side on large screens
- Good use of container classes and spacing
v2/src/components/ui/card.tsx (5)
1-16: LGTM! Well-structured Card component.The base Card component provides good defaults with:
- Proper semantic data-slot attribute
- Flexible styling with shadow, border, and padding
- Clean TypeScript typing
31-62: LGTM! CardTitle, CardDescription, and CardAction components are well-implemented.These components provide good semantic structure with:
- Appropriate typography classes
- Proper grid positioning for CardAction
- Consistent data-slot attributes
64-83: LGTM! CardContent and CardFooter complete the card system.The remaining components provide consistent:
- Padding and spacing
- Conditional border styling with CSS selectors
- Proper flex layout for footer
84-92: LGTM! Clean exports with all card components.All components are properly exported for use throughout the application.
18-29:❓ Verification inconclusive
Verify complex grid styling in CardHeader.
The CardHeader component uses complex CSS grid classes that might need verification for proper functionality.
Please verify that the grid styling works as expected, particularly the container query and grid column behavior:
🏁 Script executed:
#!/bin/bash # Search for usage examples of CardHeader to understand expected behavior rg -A 10 -B 5 "CardHeader" --type tsx --type tsLength of output: 75
🏁 Script executed:
#!/bin/bash # Search for usage examples of CardHeader across TS/TSX files rg -n -A 5 -B 2 "CardHeader" --glob "*.{ts,tsx}"Length of output: 2804
Verify complex grid styling in CardHeader
I located the only instance of CardHeader in v2/src/app/not-found.tsx (around line 70) and it doesn’t include a
card-actionslot. Please manually verify that:
- By default (no
data-slot="card-action"), the grid produces two auto‐sized rows and a single column (items stack vertically).- When a
CardActionchild is present (i.e. thehas-data-[slot=card-action]condition), the rulegrid-cols-[1fr_auto]correctly switches to a two-column layout.- The container query via
@container/card-headeris applied as intended at the correct width threshold.v2/src/components/ui/tabs.tsx (1)
1-67: Well-structured tabs component with good TypeScript integration.The implementation follows best practices with proper prop forwarding, consistent data-slot attributes, and good use of Radix UI primitives.
v2/src/components/ui/button.tsx (1)
1-60: Excellent button component implementation with comprehensive variant support.The use of class-variance-authority for variant management and the asChild pattern for composition flexibility are both excellent design choices. The TypeScript typing is also well-structured.
v2/src/components/ui/resizable.tsx (1)
1-57: Good implementation of resizable panels with proper styling and accessibility.The components properly wrap the react-resizable-panels library with consistent styling and data attributes. The optional
withHandleprop is a thoughtful addition for UI flexibility.v2/src/components/ui/navigation-menu.tsx (1)
1-169: Comprehensive navigation menu implementation with excellent accessibility features.The component suite provides a complete navigation menu system with proper animations, focus management, and accessibility. The conditional viewport rendering and proper TypeScript integration are well-implemented.
v2/src/components/ui/sheet.tsx (2)
9-11: LGTM: Clean wrapper implementationThe
Sheetcomponent correctly forwards all props to the Radix primitive while adding the data-slot attribute for styling hooks.
62-69:❓ Verification inconclusive
Verify animation class availability
The styling includes animation classes like
slide-out-to-right,slide-in-from-right, etc. Ensure these animation utilities are available in your Tailwind CSS configuration.
🏁 Script executed:
#!/bin/bash # Description: Check if the required animation classes are defined in Tailwind config # Expected: Find animation class definitions or plugins that provide these utilities rg -A 5 -B 5 "slide-(in|out)-(from|to)-(right|left|top|bottom)" v2/tailwind.config.ts v2/src/app/globals.cssLength of output: 110
#!/bin/bash # Check for plugin definitions in Tailwind config echo "🔍 Searching v2/tailwind.config.ts for plugins block:" rg -A5 -B5 "plugins\s*:" v2/tailwind.config.ts || echo "No plugins block found." echo -e "\n🔍 Looking specifically for tailwindcss-animate in config:" rg -A5 -B5 "tailwindcss-animate" v2/tailwind.config.ts || echo "No reference to tailwindcss-animate plugin in Tailwind config." # Locate package.json and inspect dependencies for animation plugins pkg=$(fd -t f package.json | head -n1) if [[ -n "$pkg" ]]; then echo -e "\n📦 Checking $pkg for animate-related dependencies:" rg -A2 -B2 "animate" "$pkg" || echo "No animate-related packages listed in dependencies." else echo "⚠️ package.json not found in repository." fi
#!/bin/bash # 1. Locate Tailwind config files echo "🔍 Locating Tailwind config files..." configs=$(fd --regex '^tailwind\.config\.(js|ts|cjs)$') if [ -z "$configs" ]; then echo "❌ No Tailwind config file found" else for f in $configs; do echo -e "\n📄 Inspecting $f" rg -A5 -B0 "plugins\s*:" "$f" || echo " – No plugins block in $f" rg -A3 -B0 "tailwindcss-animate" "$f" || echo " – No tailwindcss-animate plugin reference in $f" done fi # 2. Check package.json for animate-related dependencies pkg=$(fd --hidden --exact package.json | head -n1) if [[ -n "$pkg" ]]; then echo -e "\n📦 Checking $pkg for animation plugins..." rg -A1 -B1 '"tailwindcss-animate"' "$pkg" || echo " – No tailwindcss-animate listed" rg -A1 -B1 '"animate.css"' "$pkg" || echo " – No animate.css listed" else echo "⚠️ package.json not found" fi
Confirm animation utility classes are available
I wasn’t able to locate definitions for the
slide-in-from-*andslide-out-to-*classes in your Tailwind configuration or global CSS. Please verify that these utilities are provided via a plugin or custom definitions:
- In
v2/tailwind.config.ts: ensure you have a plugin (e.g.tailwindcss-animate) or explicit keyframes/animation entries for these slide classes.- In
package.json: confirm you’re depending on the appropriate animation plugin (e.g.tailwindcss-animate).- In your global stylesheet (
v2/src/app/globals.cssor equivalent): check for custom@keyframesand corresponding utilities for slide animations.v2/src/app/layout.tsx (2)
113-121: LGTM: Proper theme provider integrationThe ThemeProvider configuration looks correct with appropriate attributes for class-based theming, system preference detection, and transition disabling for better UX.
33-54:✅ Verification successful
Verify favicon file existence
The metadata specifies multiple favicon files with different sizes. Ensure these files exist in the public directory to avoid 404 errors.
🏁 Script executed:
#!/bin/bash # Description: Check if favicon files exist in the v2/public directory # Expected: All specified favicon files should exist echo "Checking favicon files..." for file in "favicon-16x16.ico" "favicon-32x32.ico" "favicon-48x48.ico" "favicon-64x64.ico" "apple-icon.png"; do if [ -f "v2/public/$file" ]; then echo "✓ $file exists" else echo "✗ $file is missing" fi doneLength of output: 1126
All favicons confirmed in v2/public – no missing files
Verified thatfavicon-16x16.ico,favicon-32x32.ico,favicon-48x48.ico,favicon-64x64.ico, andapple-icon.pngare all present underv2/public. No further action needed.v2/src/components/ui/input-otp.tsx (1)
69-75: LGTM: Proper separator implementationThe separator component correctly uses semantic markup with the
role="separator"attribute for accessibility.README.md (2)
48-57: LGTM: Clear monorepo structure documentationThe repository structure documentation clearly explains the monorepo layout and helps developers understand the organization.
137-142:✅ Verification successful
Verify npm script commands exist
The documentation references
npm run dev:v1andnpm run dev:v2commands. Ensure these scripts are defined in the root package.json.
🏁 Script executed:
#!/bin/bash # Description: Check if the referenced npm scripts exist in package.json # Expected: Find dev:v1 and dev:v2 scripts defined if [ -f "package.json" ]; then echo "Checking for dev scripts in root package.json:" jq '.scripts | to_entries | map(select(.key | startswith("dev")))' package.json else echo "Root package.json not found" fiLength of output: 345
dev:v1 and dev:v2 npm scripts confirmed
Bothdev:v1anddev:v2are defined in the root package.json as:
- dev:v1:
cd v1 && npm run dev- dev:v2:
cd v2 && npm run devNo changes to the README commands are required.
v2/src/components/ui/popover.tsx (2)
8-18: LGTM! Clean wrapper components.The
PopoverandPopoverTriggercomponents correctly wrap Radix primitives with appropriate data-slot attributes and prop forwarding.
42-46: LGTM! Consistent implementation.The
PopoverAnchorcomponent follows the established wrapper pattern correctly.v2/src/components/ui/pagination.tsx (2)
11-38: LGTM! Well-structured pagination components.Excellent implementation with proper semantic HTML (
nav,ul,li), accessibility attributes, and clean styling.
68-117: LGTM! Excellent accessibility implementation.The navigation components have proper aria-labels, responsive text display, and the ellipsis component correctly uses a non-interactive element with screen reader support.
v2/src/components/ui/tooltip.tsx (1)
8-19: LGTM! Good default configuration.The
TooltipProvidercomponent has a sensible defaultdelayDurationof 0 for immediate tooltip display.v2/src/components/ui/form.tsx (5)
19-19: Clean re-export pattern!Good practice to provide a simpler, more intuitive name for the FormProvider.
76-88: Well-implemented form item container!Good use of
React.useId()for generating unique IDs and proper context provision. The grid layout ensures consistent spacing between form elements.
90-105: Excellent accessibility implementation!The component properly associates the label with its form control using
htmlForand provides visual error feedback through data attributes.
107-123: Outstanding accessibility support!The component provides comprehensive ARIA attributes for screen readers, properly linking the form control with its description and error messages.
125-156: Well-designed form feedback components!Both components handle their responsibilities cleanly - FormDescription for helper text and FormMessage for error display with proper null handling.
v2/src/components/ui/select.tsx (2)
9-25: Clean wrapper implementations!These components effectively wrap Radix UI primitives while adding consistent data-slot attributes for styling hooks.
53-172: Excellent component architecture!The remaining Select components are well-designed with:
- Proper use of Portal for overlay rendering
- Comprehensive accessibility attributes
- Consistent styling patterns
- Good separation of concerns
The component suite provides a complete, accessible select implementation.
v2/src/components/ui/breadcrumb.tsx (2)
7-50: Excellent semantic HTML and accessibility!Great implementation with:
- Proper use of
navwitharia-labelfor screen readers- Semantic
olandlielements for breadcrumb structure- Flexible link rendering with the
asChildpattern- Responsive spacing with
sm:gap-2.5
52-99: Outstanding accessibility implementation!The components demonstrate excellent accessibility practices:
BreadcrumbPagecorrectly indicates the current page witharia-current="page"BreadcrumbSeparatorproperly hidden from screen readers withrole="presentation"BreadcrumbEllipsisincludes hidden text for screen reader contextThe breadcrumb suite provides a complete, accessible navigation pattern.
v2/src/components/ui/resizable-navbar.tsx (2)
260-298: Well-implemented polymorphic button component!The polymorphic pattern with the
asprop and conditional TypeScript types is correctly implemented. The component provides good flexibility for rendering as either a link or button while maintaining type safety.
106-107: 🛠️ Refactor suggestionConsider responsive approach for minimum width.
The fixed
minWidth: "800px"could cause horizontal scrolling on tablets (768px width). Since this component is hidden on mobile (lg:flex), consider using responsive minimum widths or removing the constraint entirely.Consider this responsive approach:
- style={{ - minWidth: "800px", - }} + style={{ + minWidth: "min(800px, 100vw)", + }}Or use Tailwind's responsive min-width classes:
className={cn( - "relative z-[60] mx-auto hidden w-full max-w-screen flex-row items-center justify-between self-start rounded-full bg-transparent px-4 py-2 lg:flex dark:bg-transparent", + "relative z-[60] mx-auto hidden w-full max-w-screen min-w-full lg:min-w-[800px] flex-row items-center justify-between self-start rounded-full bg-transparent px-4 py-2 lg:flex dark:bg-transparent",Likely an incorrect or invalid review comment.
v2/src/components/ui/table.tsx (1)
7-20: Good table wrapper implementation!The table container properly handles horizontal overflow with
overflow-x-auto, which is essential for responsive tables.v2/src/components/ui/chart.tsx (2)
72-103: Consider the security implications of dynamic style injection.While the
dangerouslySetInnerHTMLusage here appears safe since it only injects CSS custom properties from a structured config, ensure that theconfigobject is always from trusted sources and properly validated.The current implementation is reasonable for internal use. If this component will accept external configurations, consider adding validation:
const isValidColor = (color: string): boolean => { // Validate color format (hex, rgb, hsl, etc.) return /^(#[0-9A-Fa-f]{3,8}|rgb|hsl|var\(--[\w-]+\))/.test(color); }; // In the map function: const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color; if (color && !isValidColor(color)) { console.warn(`Invalid color value: ${color}`); return null; }🧰 Tools
🪛 Biome (1.9.4)
[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
37-70: Excellent chart container implementation!The use of React Context for configuration distribution and the unique ID generation for style scoping demonstrates good architectural decisions. The responsive container integration is well-handled.
v2/src/components/ui/drawer.tsx (1)
1-133: Good implementation with comprehensive directional support.The drawer component provides excellent multi-directional support and follows the established patterns from the other UI components. The vaul integration is well-implemented.
v2/src/components/ui/menubar.tsx (1)
1-277: Excellent comprehensive menubar implementation.This is a well-structured and feature-complete menubar component suite. The implementation demonstrates:
- Consistent patterns with other UI components in the library
- Proper Radix UI primitive wrapping with enhanced styling
- Good variant system for items (default/destructive)
- Comprehensive sub-menu support with proper animations
- Accessibility features inherited from Radix UI primitives
- Clean TypeScript typing and prop forwarding
The component follows established conventions and integrates well with the broader UI component library.
v2/src/components/ui/dropdown-menu.tsx (1)
1-258: Excellent dropdown menu implementation with consistent patterns.This dropdown menu component suite maintains excellent consistency with the other UI components in the library. Key strengths:
- Perfect alignment with the menubar component's API and patterns
- Comprehensive feature set including variants, indicators, and sub-menus
- Proper Radix UI primitive integration with enhanced styling
- Good animation support for state transitions
- Clean TypeScript implementation with proper prop forwarding
The implementation demonstrates a well-designed component library with consistent APIs and styling conventions.
v2/src/components/ui/context-menu.tsx (1)
1-252: Well-structured context menu implementation!The component suite provides a comprehensive set of context menu primitives with:
- Consistent data-slot attributes for styling hooks
- Proper TypeScript typing
- Good accessibility features from Radix UI
- Clean component composition pattern
v2/src/components/ui/sidebar.tsx (3)
86-86: Cookie implementation is appropriate for UI state.The cookie storage for sidebar state is well-implemented. Since it only stores UI preferences (expanded/collapsed), the current implementation without httpOnly or secure flags is acceptable.
96-110: Excellent keyboard shortcut implementation!The keyboard shortcut (Cmd/Ctrl + B) is well-implemented with:
- Proper event listener cleanup
- Standard shortcut choice for sidebar toggling
- Correct preventDefault usage
173-173:✅ Verification successful
Verify Tailwind v4 CSS variable syntax throughout the sidebar components.
Multiple instances of the new Tailwind v4 arbitrary value syntax with CSS variables (e.g.,
w-(--sidebar-width),min-w-32). Ensure this syntax is properly supported in your Tailwind configuration.Also applies to: 190-190, 221-221, 232-232, 238-238
🏁 Script executed:
#!/bin/bash # Description: Check if Tailwind CSS v4 is properly configured in the v2 project # Check package.json for Tailwind CSS version cat v2/package.json | jq '.dependencies["tailwindcss"], .devDependencies["tailwindcss"]' # Check for Tailwind config file fd -t f "tailwind.config" v2/Length of output: 156
Tailwind CSS v4 configuration verified
Tailwind CSS v4.1.7 is present in v2/package.json and a v2/tailwind.config.ts file exists. Arbitrary value syntax for CSS variables is supported by default in v4—no further changes needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review continued from previous batch...
- Refactor .gitignore files across v1 and v2 to include next-env.d.ts - Update README.md for clarity on project structure - Revise CONTRIBUTING.md to enhance contribution guidelines - Modify pull request template for better clarity on changes - Adjust package.json scripts for v1 to simplify development commands - Enhance navbar and mobile navigation components for improved user experience - Remove unused theme mode toggle component and related files - Update various components for better responsiveness and performance
- Upgrade motion package to version 12.15.0 in bun.lock and v2/package.json - Revise README.md to clarify technology stacks and improve project descriptions - Remove unused bio-skills-section component and replace with profile-tabs for better organization - Add new CSS class for hidden scrollbars in globals.css - Update various components for improved responsiveness and user experience
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (3)
v2/src/components/home/hero-section.tsx (1)
33-37: Optimization successfully applied from previous review.The current implementation uses a single
FlipWordscomponent with CSS-based drop shadow effect via thefilterproperty, which addresses the performance concern from the previous review about duplicated animation logic.v2/src/components/ui/resizable-navbar.tsx (1)
71-71:⚠️ Potential issueFix invalid Tailwind z-index classes throughout the file.
Multiple instances of the invalid
z-100class are present. Tailwind's default z-index scale only goes up toz-50.Apply this diff to fix all instances:
- className={cn("fixed inset-x-0 z-100 w-full max-w-screen", className)} + className={cn("fixed inset-x-0 z-50 w-full max-w-screen", className)}Also fix lines 180 and 219 with the same issue.
v2/src/components/about/stats-section.tsx (1)
1-1: "Use client" directive correctly added.The previous issue about missing "use client" directive has been resolved.
🧹 Nitpick comments (14)
v2/src/components/home/hero-section.tsx (1)
9-14: Consider extracting constants to a separate config file.The
namesandrolesarrays contain content that might change over time or be reused elsewhere. Consider moving these to a configuration file for better maintainability.Create a new file
v2/src/config/hero-content.ts:export const HERO_NAMES = ["Nikhil Rajput.", "@nixrajput."]; export const HERO_ROLES = [ "Software Development Engineer", "Open Source Contributor", "Full Stack Developer", ];Then update the imports:
+import { HERO_NAMES, HERO_ROLES } from "@/config/hero-content"; -const names: string[] = ["Nikhil Rajput.", "@nixrajput."]; -const roles: string[] = [ - "Software Development Engineer", - "Open Source Contributor", - "Full Stack Developer", -];v2/src/components/ui/container-text-flip.tsx (3)
46-53: Optimize useEffect dependency array.The current dependency array includes
wordswhich could cause unnecessary re-creation of the interval when the words array reference changes, even if the content is the same.Consider using a ref to store the current words or memoizing the array:
+import React, { useEffect, useId, useState, useMemo } from "react"; export function ContainerTextFlip({ words = ["better", "modern", "beautiful", "awesome"], interval = 3000, className, textClassName, animationDuration = 700, }: ContainerTextFlipProps) { const id = useId(); const [currentWordIndex, setCurrentWordIndex] = useState(0); const [width, setWidth] = useState(100); const textRef = React.useRef<HTMLDivElement>(null); + const memoizedWords = useMemo(() => words, [words]); // ... other code ... useEffect(() => { const intervalId = setInterval(() => { - setCurrentWordIndex((prevIndex) => (prevIndex + 1) % words.length); + setCurrentWordIndex((prevIndex) => (prevIndex + 1) % memoizedWords.length); // Width will be updated in the effect that depends on currentWordIndex }, interval); return () => clearInterval(intervalId); - }, [words, interval]); + }, [memoizedWords, interval]);
60-60: Consider animation performance optimization.The width animation uses a division operation that could be pre-calculated for better performance.
+ const animationDurationInSeconds = useMemo(() => animationDuration / 2000, [animationDuration]); <motion.div layout layoutId={`words-here-${id}`} animate={{ width }} - transition={{ duration: animationDuration / 2000 }} + transition={{ duration: animationDurationInSeconds }}
81-98: Optimize letter animation performance.The current implementation creates individual motion components for each letter, which could impact performance with longer words. Consider using a single container animation or limiting the number of animated elements.
Alternative approach using CSS animations:
- <motion.div className="inline-block"> - {words[currentWordIndex].split("").map((letter, index) => ( - <motion.span - key={index} - initial={{ - opacity: 0, - filter: "blur(10px)", - }} - animate={{ - opacity: 1, - filter: "blur(0px)", - }} - transition={{ - delay: index * 0.02, - }} - > - {letter} - </motion.span> - ))} - </motion.div> + <motion.div + className="inline-block" + initial={{ opacity: 0, filter: "blur(10px)" }} + animate={{ opacity: 1, filter: "blur(0px)" }} + transition={{ duration: 0.5 }} + > + {words[currentWordIndex]} + </motion.div>This reduces the number of animated elements from
nletters to 1 container while maintaining visual appeal..github/pull_request_template.md (1)
41-41: Fix markdown heading format.The subsection headers should use proper markdown heading syntax instead of bold text.
-**Functionality Tests** +#### Functionality Tests -**Theme and Styling** +#### Theme and Styling -**Accessibility** +#### Accessibility -**Performance** +#### PerformanceAlso applies to: 47-47, 53-53, 60-60
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
41-41: Emphasis used instead of a heading
null(MD036, no-emphasis-as-heading)
README.md (2)
52-52: Add language specification to code block.The fenced code block should specify the language for better rendering and syntax highlighting.
-``` +```text🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
52-52: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
199-199: Remove trailing punctuation from headings.Markdown headings should not end with punctuation marks.
-#### For v1: +#### For v1 -#### For v2: +#### For v2Also applies to: 205-205
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
199-199: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
v2/src/components/ui/resizable-navbar.tsx (1)
291-333: Consider improving NavbarLogo component props interface.The component has an unused
logoTextFontprop in the interface but useslogoTextFontWeightin implementation.Apply this diff to fix the interface:
{ logoSrc: string; logoText?: string; logoSize?: number; logoTextSize?: string; logoTextColor?: string; - logoTextFont?: string; logoTextFontWeight?: string; showLogoText?: boolean; className?: string; }v2/src/components/common/animated-counter.tsx (1)
17-27: Consider making the value parsing more flexible.The current regex pattern
^(\d+)(\+?)$only supports integers with optional "+". Consider supporting decimal numbers or other common formats like "2.5k" or "1M+".const parseValue = () => { - const match = value.match(/^(\d+)(\+?)$/); + const match = value.match(/^(\d+(?:\.\d+)?)(.*?)$/); if (match) { return { - number: parseInt(match[1], 10), + number: parseFloat(match[1]), suffix: match[2] || "", }; } return { number: 0, suffix: "" }; };v2/src/components/ui/timeline.tsx (1)
23-29: Consider accessibility implications of scroll-based animations.The scroll-based animations may cause issues for users with vestibular disorders or those who prefer reduced motion. Consider respecting the
prefers-reduced-motionmedia query.const { scrollYProgress } = useScroll({ target: containerRef, offset: ["start 10%", "end 50%"], }); -const heightTransform = useTransform(scrollYProgress, [0, 1], [0, height]); -const opacityTransform = useTransform(scrollYProgress, [0, 0.1], [0, 1]); +const heightTransform = useTransform( + scrollYProgress, + [0, 1], + [0, window.matchMedia('(prefers-reduced-motion: reduce)').matches ? height : height] +); +const opacityTransform = useTransform( + scrollYProgress, + [0, 0.1], + [0, window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 1 : 1] +);v2/src/components/about/tabs/experiences-tab.tsx (2)
70-96: Remove commented code or document its purpose.There's a large block of commented code that appears to be an alternative timeline implementation. If this code is no longer needed, it should be removed to improve code cleanliness. If it's kept for reference, consider adding a comment explaining why.
- {/* <div className="space-y-6"> - {experiences.map((experience, index) => ( - // ... rest of commented code - ))} - </div> */}
54-54: Improve key prop for better React performance.While using array index in the key is functional, using a more stable identifier would be better for React's reconciliation process.
- <div key={`experience-${idx}`}> + <div key={`experience-${experience.company}-${experience.date}`}>v2/src/components/ui/animated-tabs.tsx (1)
19-25: Extract interface definitions for better maintainability.The inline type definition makes the component signature verbose. Consider extracting it to improve readability.
+interface AnimatedTabsProps { + tabs: Tab[]; + containerClassName?: string; + activeTabClassName?: string; + tabClassName?: string; + contentClassName?: string; +} -export const AnimatedTabs = ({ - tabs: propTabs, - containerClassName, - activeTabClassName, - tabClassName, - contentClassName, -}: { - tabs: Tab[]; - containerClassName?: string; - activeTabClassName?: string; - tabClassName?: string; - contentClassName?: string; -}) => { +export const AnimatedTabs = ({ + tabs: propTabs, + containerClassName, + activeTabClassName, + tabClassName, + contentClassName, +}: AnimatedTabsProps) => {v2/src/components/about/tabs/skills-tab.tsx (1)
246-247: Use dynamic blur placeholder or handle missing images gracefully.All images use the same hardcoded blur placeholder (
/images/logical-thinking.png), which may not be appropriate for all skill icons. Consider using a generic placeholder or handling missing images more gracefully.- placeholder="blur" - blurDataURL="/images/logical-thinking.png" + placeholder="blur" + blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjNGNEY2Ii8+Cjwvc3ZnPg=="Or add error handling:
{skill.icon ? ( <Image src={skill.icon} alt={`logo-${skill.name}`} width={48} height={48} sizes="100%" loading="lazy" placeholder="blur" - blurDataURL="/images/logical-thinking.png" + blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjNGNEY2Ii8+Cjwvc3ZnPg==" className="w-5 lg:w-6 h-auto aspect-square object-cover" + onError={(e) => { + e.currentTarget.style.display = 'none'; + }} /> ) : null}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
bun.lockis excluded by!**/*.lockv1/bun.lockbis excluded by!**/bun.lockbv2/bun.lockis excluded by!**/*.lockv2/public/images/profile.pngis excluded by!**/*.pngv2/public/skills/python.pngis excluded by!**/*.pngv2/public/skills/rails.pngis excluded by!**/*.pngv2/public/skills/ruby.pngis excluded by!**/*.png
📒 Files selected for processing (40)
.github/pull_request_template.md(1 hunks).gitignore(2 hunks)CONTRIBUTING.md(2 hunks)README.md(5 hunks)v1/.gitignore(1 hunks)v1/README.md(1 hunks)v1/package.json(1 hunks)v2/.gitignore(1 hunks)v2/package.json(1 hunks)v2/src/app/globals.css(1 hunks)v2/src/app/layout.tsx(1 hunks)v2/src/app/manifest.ts(1 hunks)v2/src/app/not-found.tsx(1 hunks)v2/src/app/robots.ts(1 hunks)v2/src/app/sitemap.ts(1 hunks)v2/src/components/about/index.tsx(1 hunks)v2/src/components/about/main-section.tsx(1 hunks)v2/src/components/about/profile-card.tsx(1 hunks)v2/src/components/about/profile-tabs.tsx(1 hunks)v2/src/components/about/stats-section.tsx(1 hunks)v2/src/components/about/tabs/bio-tab.tsx(1 hunks)v2/src/components/about/tabs/experiences-tab.tsx(1 hunks)v2/src/components/about/tabs/skills-tab.tsx(1 hunks)v2/src/components/common/animated-counter.tsx(1 hunks)v2/src/components/common/back-button.tsx(1 hunks)v2/src/components/common/page-wrapper.tsx(1 hunks)v2/src/components/common/section-title.tsx(1 hunks)v2/src/components/home/hero-section.tsx(1 hunks)v2/src/components/home/index.tsx(1 hunks)v2/src/components/nav/custom-nav-bar.tsx(1 hunks)v2/src/components/theme/theme-mode-toggle.tsx(1 hunks)v2/src/components/theme/theme-provider.tsx(1 hunks)v2/src/components/ui/animated-tabs.tsx(1 hunks)v2/src/components/ui/background-beams-with-collision.tsx(1 hunks)v2/src/components/ui/container-text-flip.tsx(1 hunks)v2/src/components/ui/flip-words.tsx(1 hunks)v2/src/components/ui/resizable-navbar.tsx(1 hunks)v2/src/components/ui/timeline.tsx(1 hunks)v2/src/hooks/use-mobile.ts(1 hunks)v2/tsconfig.json(1 hunks)
✅ Files skipped from review due to trivial changes (7)
- v2/src/components/common/page-wrapper.tsx
- v2/src/app/robots.ts
- v2/src/components/theme/theme-provider.tsx
- v2/src/components/about/tabs/bio-tab.tsx
- v2/src/components/common/section-title.tsx
- v2/src/components/common/back-button.tsx
- v2/src/app/sitemap.ts
🚧 Files skipped from review as they are similar to previous changes (18)
- CONTRIBUTING.md
- v2/src/components/about/index.tsx
- v2/src/app/manifest.ts
- v1/.gitignore
- v1/README.md
- v2/tsconfig.json
- v2/src/app/globals.css
- v2/.gitignore
- v2/package.json
- v2/src/app/not-found.tsx
- v2/src/components/about/main-section.tsx
- v1/package.json
- v2/src/components/nav/custom-nav-bar.tsx
- v2/src/hooks/use-mobile.ts
- v2/src/components/about/profile-card.tsx
- v2/src/app/layout.tsx
- v2/src/components/ui/background-beams-with-collision.tsx
- v2/src/components/ui/flip-words.tsx
🧰 Additional context used
🧬 Code Graph Analysis (5)
v2/src/components/home/index.tsx (1)
v2/src/components/common/page-wrapper.tsx (1)
PageWrapper(8-10)
v2/src/components/about/stats-section.tsx (1)
v2/src/components/ui/card.tsx (1)
CardContent(91-91)
v2/src/components/ui/timeline.tsx (1)
v1/src/types/index.ts (1)
TimelineEntry(127-130)
v2/src/components/theme/theme-mode-toggle.tsx (2)
v2/src/components/ui/dropdown-menu.tsx (4)
DropdownMenu(242-242)DropdownMenuTrigger(244-244)DropdownMenuContent(245-245)DropdownMenuItem(248-248)v2/src/components/ui/button.tsx (1)
Button(59-59)
v2/src/components/about/tabs/experiences-tab.tsx (1)
v2/src/components/ui/timeline.tsx (1)
Timeline(11-75)
🪛 LanguageTool
.github/pull_request_template.md
[style] ~29-~29: Consider using a different verb for a more formal wording.
Context: ...new features or components - [ ] I have fixed existing issues (e.g., performance, edg...
(FIX_RESOLVE)
[style] ~30-~30: ‘overall structure’ might be wordy. Consider a shorter alternative.
Context: ..., edge cases) - [ ] I have improved the overall structure or optimized the codebase - [ ] I have ...
(EN_WORDINESS_PREMIUM_OVERALL_STRUCTURE)
🪛 markdownlint-cli2 (0.17.2)
.github/pull_request_template.md
41-41: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
47-47: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
53-53: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
60-60: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
README.md
52-52: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
199-199: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
205-205: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
🔇 Additional comments (22)
v2/src/components/home/index.tsx (1)
1-10: Clean and well-structured component composition.The
HomePageComponentfollows React best practices with a simple, focused responsibility of composing the hero section within a page wrapper. The implementation is clean and maintainable.v2/src/components/home/hero-section.tsx (1)
61-68: 🛠️ Refactor suggestionAdd accessibility attributes to the call-to-action button.
The "Book a Call" button should include ARIA attributes to describe its purpose and behavior.
<Button variant="default" size="lg" className="min-w-40 bg-gradient-to-r from-purple-500 via-violet-500 to-pink-500 hover:opacity-90 hover:-translate-y-0.5 transition-all text-white" + aria-label="Book a consultation call" + role="button" > Book a Call <IconArrowRight className="ml-2 h-4 w-4" /> </Button>Likely an incorrect or invalid review comment.
.gitignore (2)
20-20: Good addition for comprehensive .DS_Store handling.The recursive pattern
**/.DS_Storeproperly handles macOS system files across all subdirectories in the monorepo structure.
36-36: Appropriate for monorepo setup.Keeping only
next-env.d.tsin the TypeScript section while removing lock file ignores is correct for a monorepo where dependency consistency across v1 and v2 is important.v2/src/components/theme/theme-mode-toggle.tsx (4)
14-18: Well-designed component interface.The
ThemeModeTogglePropsinterface provides good flexibility with optional className, onClick callback, and configurable icon size with a sensible default.
40-47: Excellent icon transition implementation.The smooth transitions between sun and moon icons using CSS transforms create a polished user experience. The dark mode conditional classes are well-structured.
48-48: Good accessibility practice.Including the screen reader label "Toggle theme" ensures the component is accessible to users with assistive technologies.
52-52: Proper z-index management.Setting
z-[110]on the dropdown content ensures it appears above other UI elements, preventing layout issues..github/pull_request_template.md (2)
37-37: Correct project technology reference.Good correction from the previous version - referencing
package.jsoninstead ofpubspec.yamlis appropriate for a Next.js project.
41-64: Portfolio-relevant testing checklist.The testing section now appropriately covers portfolio-specific concerns like theme toggling, responsive design, accessibility, and performance rather than carousel-specific functionality.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
41-41: Emphasis used instead of a heading
null(MD036, no-emphasis-as-heading)
47-47: Emphasis used instead of a heading
null(MD036, no-emphasis-as-heading)
53-53: Emphasis used instead of a heading
null(MD036, no-emphasis-as-heading)
60-60: Emphasis used instead of a heading
null(MD036, no-emphasis-as-heading)
README.md (4)
1-6: Excellent monorepo documentation structure.The introduction clearly explains the dual-version approach and provides a concise overview of the technology differences between v1 and v2.
48-65: Helpful repository structure visualization.The directory tree provides a clear overview of the monorepo organization, making it easy for contributors to understand the codebase layout.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
52-52: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
159-191: Clear version-specific development instructions.The separate sections for running v1 and v2 with different ports (4000 and 3000) make it easy to work with both versions simultaneously.
215-237: Comprehensive deployment guidance.The detailed Vercel deployment instructions for both versions provide clear, actionable steps for separate domain deployment.
v2/src/components/ui/resizable-navbar.tsx (2)
54-83: Good implementation of scroll-based navbar visibility.The scroll event handling and state management for the navbar visibility is well implemented with proper cleanup and smooth animations.
132-153: Excellent use of Next.js Link component.The navigation items correctly use Next.js Link components for client-side navigation, which addresses the previous feedback about using anchor tags.
v2/src/components/about/profile-tabs.tsx (2)
18-48: Well-structured default tabs configuration.The default tabs configuration is clean and provides good fallback content. The styling is consistent across all tabs with proper responsive design.
50-56: Clean and minimal component implementation.The ProfileTabs component is well-implemented with good prop handling and clear separation of concerns.
v2/src/components/about/stats-section.tsx (1)
23-47: Excellent animated stats implementation.The stats section has well-designed animations with proper viewport detection, responsive grid layout, and good visual feedback with hover effects.
v2/src/components/common/animated-counter.tsx (2)
31-55: Excellent Intersection Observer implementation.The observer setup includes proper cleanup and appropriate threshold settings. The use of a stored reference for cleanup is a good practice to avoid memory leaks.
57-90: Well-designed animation logic with proper cleanup.The counter animation logic handles edge cases well, includes appropriate duration scaling, and properly cleans up intervals to prevent memory leaks.
v2/src/components/ui/animated-tabs.tsx (1)
29-35: 🛠️ Refactor suggestionFix potential array mutation issue.
The
moveSelectedTabToTopfunction creates a new array frompropTabsbut then mutates it withsplice. While this works, it's cleaner to avoid mutation entirely.const moveSelectedTabToTop = (idx: number) => { - const newTabs = [...propTabs]; - const selectedTab = newTabs.splice(idx, 1); - newTabs.unshift(selectedTab[0]); + const selectedTab = propTabs[idx]; + const newTabs = [selectedTab, ...propTabs.filter((_, i) => i !== idx)]; setTabs(newTabs); setActive(newTabs[0]); };Likely an incorrect or invalid review comment.
- Upgrade all dependencies in bun.lock and v2/package.json to their latest versions for improved stability and security - Add comprehensive testing scripts for Jest and Cypress in package.json to streamline testing processes - Revise pull request and issue templates to improve clarity and detail for contributors - Enhance bug report and feature request templates with additional fields for better context and information gathering
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
♻️ Duplicate comments (3)
v2/src/components/ui/flip-words.tsx (1)
15-17:⚠️ Potential issueInitial state can be
undefinedwhenwordsis empty – breaks type safety
useState(words[0])yieldsundefinedfor an empty array, butcurrentWordis treated asstringlater (splitcall). This is a runtime and TypeScript bug.-const [currentWord, setCurrentWord] = useState(words[0]); +const [currentWord, setCurrentWord] = useState( + words.length > 0 ? words[0] : "" +);v2/src/components/ui/timeline.tsx (1)
7-10: Consider reusing the existing TimelineEntry interface.The
TimelineEntryinterface is already defined inv1/src/types/index.tswith identical properties. To maintain consistency and avoid duplication, consider importing it from a shared types location.v2/src/components/ui/resizable-navbar.tsx (1)
71-72:z-100is still an invalid Tailwind class.
This was flagged in the previous review and remains unfixed at three locations. Tailwind’s default scale stops atz-50; the class is silently ignored unless you extended the config.Also applies to: 174-176, 213-215
🧹 Nitpick comments (41)
v2/src/components/__tests__/home/index.test.tsx (2)
20-20: Assert both default wrapper classes, not just one
PageWrapperalways applies"relative w-full". Asserting only"relative"would let a future regression drop"w-full"unnoticed.- expect(container.firstChild).toHaveClass("relative"); + expect(container.firstChild).toHaveClass("relative", "w-full");
12-15: PreferuserEventoverfireEventfor user-level interactions
@testing-library/user-eventshadows the real browser behaviour (focus, typing delay, etc.) and is now the recommended way to simulate user actions. Swapping to it improves test fidelity with zero API breakage.Example:
import userEvent from "@testing-library/user-event"; userEvent.click(screen.getByRole("button"));v2/src/components/__tests__/ui/input.test.tsx (2)
10-19: UseuserEvent.typefor value change tests
fireEvent.changesets the value programmatically, bypassing events the browser fires during real typing.userEvent.typetriggers the correct sequence (keydown,input,keyup) and gives more reliable coverage.-import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; ... -const input = screen.getByRole("textbox"); -fireEvent.change(input, { target: { value: "test" } }); +const input = screen.getByRole("textbox"); +await userEvent.type(input, "test");
1-34: Missing test for ref-forwarding & accessibility
Inputis likely a wrapper around<input>. Consider adding:
- A test that a
refpassed to<Input>receives the DOM node (important for forms / focus management).- A test that an
aria-labelprop is forwarded, ensuring accessibility.These give confidence the wrapper does not block critical props.
v2/src/components/__tests__/common/page-wrapper.test.tsx (2)
23-30: Assertion duplicates default-class testYou already verify default classes in the previous test. Line 29 redundantly re-checks the same condition; if one fails, both fail. Consider dropping one or merging them to keep the suite lean.
4-12: Prefer querying viascreenfor consistencyInstead of destructuring
container, you can rely on RTL queries (getByRole,getByTestId) which are closer to user intent and decouple tests from DOM structure details.Example:
render(<PageWrapper><div data-testid="content"/></PageWrapper>); expect(screen.getByTestId("content").parentElement).toHaveClass("relative");v2/src/components/__tests__/ui/button.test.tsx (2)
20-24: Test asserts internal class names – brittle to refactorsCoupling to implementation-specific classes (
bg-primary) means any design-token or Tailwind colour refactor will break tests without a functional regression. Prefer behaviour-driven assertions:expect(button).toBeVisible(); expect(button).toBeEnabled();If visual style must be asserted, consider data attributes or a helper that maps variant → expected class, keeping the test independent of Tailwind tokens.
44-52: VerifyasChildsemantics more thoroughlyBesides checking the element becomes a link, also assert that:
- No
role="button"remains.- Original button props (e.g.
className) are forwarded.expect(screen.queryByRole("button")).toBeNull(); expect(link).toHaveClass("bg-primary");v2/src/components/__tests__/about/profile-tabs.test.tsx (2)
10-23: Mock is fine but remember to reset it between tests
jest.mockis declared at file scope, so the mock’s internal state (if any) persists for the whole file.
If you later addjest.fn()spies or change the implementation in a test, surprises can leak across test cases.+afterEach(() => { + jest.resetAllMocks(); +});
58-75: Container–class assertion is brittleHard-coding every utility class means a minor style tweak will break the test without changing behaviour.
Prefer asserting on a single stable identifier (e.g.data-testid="profile-tabs-container") or a subset of “critical” classes (flex,relative, etc.).v2/src/components/__tests__/about/main-section.test.tsx (1)
40-47: DOM traversal is fragile
parentElement?.parentElementassumes a very specific markup depth.
Expose a stable test id on the<section>instead and query it directly:// component (MainSection) <section id="about" data-testid="about-section" …> // test expect(screen.getByTestId("about-section")).toHaveClass("relative", …);v2/cypress/e2e/home.cy.ts (1)
15-18: Dark-mode assertion lacks negative checkEnsure the toggle both adds and removes the class to catch regressions:
cy.get("html").should("not.have.class", "dark"); cy.get('[data-testid="theme-toggle"]').click(); cy.get("html").should("have.class", "dark");v2/src/components/__tests__/about/stats-section.test.tsx (2)
3-3: Import path looks wrong
defaultStatslives in@/data/stats, not the barrel@/data. If an index barrel is missing this import will fail in TS-strict mode.
40-54: Deep DOM traversal againRather than climbing DOM levels, render a
data-testid="stats-container"on the grid wrapper to decouple the test from markup depth.v2/src/components/__tests__/about/tabs/skills-tab.test.tsx (2)
29-37: Alt-text expectation is coupled to implementation detailThe
"logo-${skill.name}"convention is replicated in the test.
If the component ever changes its alt-strategy (e.g."${skill.name} logo"), the test fails even though the UI is correct. Consider querying the image byrole="img"withnamematcher:expect( screen.getByRole("img", { name: /skill 1/i }) ).toBeInTheDocument();
68-74: Custom-class assertion could be more explicitInstead of navigating
parentElement?.parentElement, expose a wrapperdata-testidand assert directly, mirroring the advice for other tests.v2/src/components/__tests__/common/section-title.test.tsx (2)
4-15: Duplicatemotion/reactmock appears in several test files – extract to global Jest setupThe same inline mock for
motion/reactis repeated in at least three test suites. Move this mock intov2/jest.setup.ts(or a__mocks__/motion/react.ts) so it is defined exactly once and automatically shared across all tests.
This eliminates duplication and prevents divergence if the mock ever needs to change.
53-64: Over-specifying utility classes will make the test brittleAsserting every tailwind class in the gradient (
bg-clip-text,text-transparent, and each color stop) couples the test to styling details that are free to change without affecting user-visible behaviour. A single semantic assertion is usually enough:-expect(highlightElement).toHaveClass( - "bg-clip-text", - "text-transparent", - "bg-gradient-to-r", - "from-purple-500", - "via-violet-500", - "to-pink-500" -); +expect(highlightElement).toHaveClass("bg-clip-text");Keep visual-regression concerns in Storybook/E2E tests; unit tests should stay resilient.
v2/bun-test-setup.ts (1)
45-49: Useless constructor – drop it to silence linters
IntersectionObserverdefines an emptyconstructor()that does nothing:class IntersectionObserver { constructor() {} // ← redundant observe() {} ⋯ }Removing the constructor keeps the mock behaviour intact and satisfies
noUselessConstructor.- constructor() {}🧰 Tools
🪛 Biome (1.9.4)
[error] 46-46: This constructor is unnecessary.
Unsafe fix: Remove the unnecessary constructor.
(lint/complexity/noUselessConstructor)
v2/src/components/__tests__/common/back-button.test.tsx (1)
48-52:queryByRole("img")may not catch an inline SVGThe icon rendered by the component is likely an
<svg>without an explicit role, meaning the query could yield a false negative. Prefer a test-id orquerySelector("svg").-expect(screen.queryByRole("img")).not.toBeInTheDocument(); +expect(container.querySelector("svg")).toBeNull();v2/src/components/__tests__/home/hero-section.test.tsx (1)
65-69: Over-asserting Tailwind gradient classes – keep the test intent minimalSimilar to the
SectionTitletest, asserting a specific gradient class (bg-gradient-to-r) ties the test to styling implementation instead of behaviour. A looser assertion (e.g. presence of the button with accessible name) is enough.-expect(button).toHaveClass("bg-gradient-to-r");If you really must check style, consider an integration / visual test instead.
v2/src/components/__tests__/about/tabs/experiences-tab.test.tsx (2)
5-9: Avoid duplicating domain models inside tests
TimelineEntryis re-declared here, diverging from the single-source definition that lives with the realTimelinecomponent. Import the type (orExperiencedirectly) to prevent silent drift when fields are added/renamed.
70-76: Fragile selector forclassNameassertionUsing
parentElementties the test to the DOM structure of the mock. Instead, expose a test id on the actual root element inExperiencesTab(e.g.,<div data-testid="experiences-tab-root">) and assert against that. This reduces coupling to implementation details.v2/src/components/theme/__tests__/theme-provider.test.tsx (1)
5-15: Re-implementing DOM mocks by hand is verboseYou can lean on
jest.fn()and the built-inglobalThisobjects:-const mockClassList = { add: mock(() => {}), remove: mock(() => {}) }; +const mockClassList = { add: jest.fn(), remove: jest.fn() };Same for localStorage & matchMedia. This keeps semantics uniform once you switch to Jest.
v2/src/components/__tests__/common/animated-counter.test.tsx (1)
50-67: Potential false-positive: element matching/^\d+$/may select stale text nodesIf another numeric element is rendered (e.g., “2024”), the assertion could grab the wrong node. Add a
data-testid="counter"to the component and assert on that to avoid ambiguity.v2/src/components/about/main-section.tsx (1)
14-18: Animation accessibilityThe entrance animation fires on every mount. Users that prefer reduced motion will still get the effect. Wrap the
motion.divprops withprefers-reduced-motiongating (e.g.,useReducedMotion()from Framer) or providetransition: { …, reduceMotion: "user" }.v2/src/components/__tests__/about/profile-card.test.tsx (1)
71-82: Hard-coding gradient classes couples the test to presentationChecking specific utility classes (
bg-gradient-to-r, etc.) makes the test fragile to design tweaks. Instead, verify semantic intent (e.g.,toHaveClass("primary-button")) or snapshot the element’s class list once design tokens are stable..github/ISSUE_TEMPLATE/bug_report.yml (1)
154-160: Update links to repo-local Code-of-Conduct & Contributing docsThe template points to
portfolio-nextjspaths, but this repo was restructured into a monorepo. Broken links frustrate contributors.-https://github.com/nixrajput/portfolio-nextjs/blob/master/CODE_OF_CONDUCT.md +https://github.com/nixrajput/<current-repo>/blob/main/CODE_OF_CONDUCT.md(Apply the same for the contributing URL.)
v2/src/components/about/tabs/skills-tab.tsx (1)
36-41: Use stable keys to avoid React re-mounting glitches
childIdxis only unique within one category. If two categories have the same number of items, keys repeat across siblings, causing React to re-mount elements unexpectedly.-key={`skill-${childIdx}`} +key={`skill-${idx}-${childIdx}`} // include parent idx +// or, better, key by a stable identifier: +// key={skill.name}v2/src/components/common/futuristic-background.tsx (1)
55-67: SVG id collision risk when multiple instances are rendered
id="fade-out"is hard-coded. If twoFuturisticBackgroundinstances appear on the same page, duplicate IDs break gradients.-<radialGradient id="fade-out" cx="50%" cy="50%" r="50%"> +<radialGradient id={`fade-out-${Math.random().toString(36).slice(2)}`} cx="50%" cy="50%" r="50%">(or use
useId()from React 18.)v2/src/components/ui/flip-words.tsx (1)
28-40: Prefer browser-agnostic timeout typing
NodeJS.Timeoutleaks Node types into client code and can confuse TS configs that target the DOM.-let timeoutId: NodeJS.Timeout; +let timeoutId: ReturnType<typeof setTimeout>;v2/src/components/__tests__/ui/form.test.tsx (1)
109-124: UsewaitForwhen asserting async DOM updatesThe validation message disappears after state updates triggered by
fireEvent.change.
WithoutwaitFor, the assertion may run before React finishes the re-render, causing a flaky failure.- fireEvent.change(usernameInput, { target: { value: "testuser" } }); - expect( - screen.queryByText("Username must be at least 2 characters") - ).not.toBeInTheDocument(); + fireEvent.change(usernameInput, { target: { value: "testuser" } }); + await waitFor(() => + expect( + screen.queryByText("Username must be at least 2 characters") + ).not.toBeInTheDocument() + );v2/src/components/theme/theme-provider.tsx (1)
55-57: Avoid spreading...propsontoContext.Provider
Context.Provideronly acceptsvalueandchildren.
Forwarding arbitrary props risks React warnings (Unknown prop).
Unless you have a concrete need, drop the spread.- <ThemeProviderContext.Provider {...props} value={value}> + <ThemeProviderContext.Provider value={value}>v2/src/components/nav/__tests__/custom-nav-bar.test.tsx (2)
246-256: PrefertoBeInTheDocumentovertoBeDefinedfor DOM queries
toBeDefinedonly checks that the query returned a truthy value; it doesn't assert presence in the rendered DOM.
Switching to@testing-library/jest-dom'stoBeInTheDocumentgives clearer intent and failure messages.- expect(screen.getByText("Home")).toBeDefined(); + expect(screen.getByText("Home")).toBeInTheDocument();(Apply similarly for the other nav-item assertions.)
230-236: Callmock.reset()/mock.clear()in anafterEachCurrently mocks are reset manually for a subset of cases; adding a global
afterEach(cleanup)covering both RTL cleanup and mock resets will make maintenance easier and prevent accidental bleed-through between tests.v2/src/__tests__/performance.test.tsx (1)
1-2: Mixed test runners may slow CI & complicate setupThis suite relies on
bun:test, while the remainder of the repo uses Jest/RTL.
Running two frameworks in parallel increases CI time and cognitive load.
Consider porting the file to Jest (or vice versa) so that a single runner drives all unit tests.README.md (1)
105-108: Add the missing v2 portfolio screenshot.The README includes a placeholder for the v2 portfolio screenshot. Consider capturing and adding the screenshot to complete the documentation.
Would you like me to help create a script to capture the screenshot automatically or open an issue to track this task?
v2/src/components/ui/timeline.tsx (1)
46-46: Misleading comment about cursor following.The comment mentions "follows cursor" but the implementation only tracks hover state, not actual cursor position.
Update the comment to accurately describe the behavior:
- {/* Liquid glass highlight effect - follows cursor with framer motion */} + {/* Liquid glass highlight effect - appears on hover with framer motion */}.github/pull_request_template.md (1)
174-174: Add hyphens to "up-to-date" when used as an adjective.-- [ ] Branch is up to date with main +- [ ] Branch is up-to-date with main🧰 Tools
🪛 LanguageTool
[uncategorized] ~174-~174: It appears that hyphens are missing in the adjective “up-to-date”.
Context: ...omic and well-described - [ ] Branch is up to date with main - [ ] No merge conflicts - [ ...(UP_TO_DATE_HYPHEN)
v2/src/components/common/liquid-glass-button.tsx (1)
46-46: Misleading comment about cursor following.The comment mentions "follows cursor" but the implementation only tracks hover state without cursor position.
- {/* Liquid glass highlight effect - follows cursor with framer motion */} + {/* Liquid glass highlight effect - appears on hover with framer motion */}v2/src/components/ui/resizable-navbar.tsx (1)
301-312: Enable blurred placeholder on Next/Image.
blurDataURLis ignored unlessplaceholder="blur"is set.- <Image - src={logoSrc} - alt="logo" - width={logoSize} - height={logoSize} - loading="eager" - blurDataURL="/images/placeholder.png" - /> + <Image + src={logoSrc} + alt="logo" + width={logoSize} + height={logoSize} + loading="eager" + placeholder="blur" + blurDataURL="/images/placeholder.png" + />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (55)
.github/ISSUE_TEMPLATE/bug_report.yml(1 hunks).github/ISSUE_TEMPLATE/feature_request.yml(1 hunks).github/pull_request_template.md(1 hunks).github/workflows/test.yml(1 hunks)README.md(5 hunks)v2/bun-test-setup.ts(1 hunks)v2/cypress.config.ts(1 hunks)v2/cypress/e2e/home.cy.ts(1 hunks)v2/jest.config.ts(1 hunks)v2/jest.setup.ts(1 hunks)v2/package.json(1 hunks)v2/src/__tests__/performance.test.tsx(1 hunks)v2/src/app/layout.tsx(1 hunks)v2/src/app/not-found.tsx(1 hunks)v2/src/components/__tests__/about/main-section.test.tsx(1 hunks)v2/src/components/__tests__/about/profile-card.test.tsx(1 hunks)v2/src/components/__tests__/about/profile-tabs.test.tsx(1 hunks)v2/src/components/__tests__/about/stats-section.test.tsx(1 hunks)v2/src/components/__tests__/about/tabs/bio-tab.test.tsx(1 hunks)v2/src/components/__tests__/about/tabs/experiences-tab.test.tsx(1 hunks)v2/src/components/__tests__/about/tabs/skills-tab.test.tsx(1 hunks)v2/src/components/__tests__/common/animated-counter.test.tsx(1 hunks)v2/src/components/__tests__/common/back-button.test.tsx(1 hunks)v2/src/components/__tests__/common/page-wrapper.test.tsx(1 hunks)v2/src/components/__tests__/common/section-title.test.tsx(1 hunks)v2/src/components/__tests__/home/hero-section.test.tsx(1 hunks)v2/src/components/__tests__/home/index.test.tsx(1 hunks)v2/src/components/__tests__/ui/button.test.tsx(1 hunks)v2/src/components/__tests__/ui/card.test.tsx(1 hunks)v2/src/components/__tests__/ui/form.test.tsx(1 hunks)v2/src/components/__tests__/ui/input.test.tsx(1 hunks)v2/src/components/about/main-section.tsx(1 hunks)v2/src/components/about/profile-card.tsx(1 hunks)v2/src/components/about/profile-tabs.tsx(1 hunks)v2/src/components/about/stats-section.tsx(1 hunks)v2/src/components/about/tabs/bio-tab.tsx(1 hunks)v2/src/components/about/tabs/experiences-tab.tsx(1 hunks)v2/src/components/about/tabs/skills-tab.tsx(1 hunks)v2/src/components/common/animated-counter.tsx(1 hunks)v2/src/components/common/futuristic-background.tsx(1 hunks)v2/src/components/common/liquid-glass-button.tsx(1 hunks)v2/src/components/home/hero-section.tsx(1 hunks)v2/src/components/nav/__tests__/custom-nav-bar.test.tsx(1 hunks)v2/src/components/nav/custom-nav-bar.tsx(1 hunks)v2/src/components/theme/__tests__/theme-mode-toggle.test.tsx(1 hunks)v2/src/components/theme/__tests__/theme-provider.test.tsx(1 hunks)v2/src/components/theme/theme-mode-toggle.tsx(1 hunks)v2/src/components/theme/theme-provider.tsx(1 hunks)v2/src/components/ui/animated-tabs.tsx(1 hunks)v2/src/components/ui/container-text-flip.tsx(1 hunks)v2/src/components/ui/flip-words.tsx(1 hunks)v2/src/components/ui/resizable-navbar.tsx(1 hunks)v2/src/components/ui/timeline.tsx(1 hunks)v2/src/data/experiences.ts(1 hunks)v2/src/data/index.ts(1 hunks)
✅ Files skipped from review due to trivial changes (6)
- v2/src/data/index.ts
- .github/workflows/test.yml
- v2/src/data/experiences.ts
- v2/cypress.config.ts
- v2/src/components/about/tabs/experiences-tab.tsx
- v2/jest.config.ts
🚧 Files skipped from review as they are similar to previous changes (13)
- v2/src/components/about/profile-tabs.tsx
- v2/src/components/common/animated-counter.tsx
- v2/src/components/about/stats-section.tsx
- v2/src/components/about/tabs/bio-tab.tsx
- v2/src/components/nav/custom-nav-bar.tsx
- v2/src/app/layout.tsx
- v2/src/app/not-found.tsx
- v2/package.json
- v2/src/components/ui/container-text-flip.tsx
- v2/src/components/about/profile-card.tsx
- v2/src/components/ui/animated-tabs.tsx
- v2/src/components/theme/theme-mode-toggle.tsx
- v2/src/components/home/hero-section.tsx
🧰 Additional context used
🧬 Code Graph Analysis (14)
v2/src/components/__tests__/home/index.test.tsx (1)
v2/src/components/home/index.tsx (1)
HomePageComponent(4-10)
v2/src/components/__tests__/common/page-wrapper.test.tsx (1)
v2/src/components/common/page-wrapper.tsx (1)
PageWrapper(8-10)
v2/src/components/__tests__/about/tabs/bio-tab.test.tsx (1)
v2/src/data/intro.ts (3)
bio1(13-14)bio2(16-17)bio3(19-20)
v2/src/components/__tests__/about/stats-section.test.tsx (1)
v2/src/data/stats.ts (1)
defaultStats(3-8)
v2/src/components/__tests__/about/tabs/skills-tab.test.tsx (2)
v2/src/data/skills.ts (1)
defaultSkillLists(3-189)v2/src/types/index.ts (1)
ISkillList(21-24)
v2/src/components/theme/__tests__/theme-provider.test.tsx (1)
v2/src/components/theme/theme-provider.tsx (1)
Theme(6-6)
v2/src/components/__tests__/common/back-button.test.tsx (1)
v2/jest.setup.ts (2)
useRouter(5-14)useRouter(19-26)
v2/src/components/__tests__/about/profile-card.test.tsx (1)
v2/src/data/social-links.ts (1)
defaultSocialLinks(9-34)
v2/src/components/__tests__/about/tabs/experiences-tab.test.tsx (1)
v2/src/data/experiences.ts (1)
defaultExperiences(3-25)
v2/src/components/common/futuristic-background.tsx (1)
v2/src/lib/utils.ts (1)
cn(4-6)
v2/src/components/about/tabs/skills-tab.tsx (2)
v2/src/types/index.ts (1)
ISkillList(21-24)v2/src/data/skills.ts (1)
defaultSkillLists(3-189)
v2/src/components/ui/timeline.tsx (1)
v1/src/types/index.ts (1)
TimelineEntry(127-130)
v2/src/components/theme/theme-provider.tsx (2)
v2/bun-test-setup.ts (1)
root(50-52)v2/src/store/theme.ts (1)
ThemeProviderContext(15-16)
v2/src/components/__tests__/home/hero-section.test.tsx (1)
v2/src/data/intro.ts (2)
names(1-1)roles(7-11)
🪛 Biome (1.9.4)
v2/bun-test-setup.ts
[error] 46-46: This constructor is unnecessary.
Unsafe fix: Remove the unnecessary constructor.
(lint/complexity/noUselessConstructor)
v2/jest.setup.ts
[error] 40-40: expected > but instead found {
Remove {
(parse)
[error] 40-40: Expected an expression but instead found '>'.
Expected an expression here.
(parse)
[error] 40-40: Expected an expression but instead found ';'.
Expected an expression here.
(parse)
🪛 LanguageTool
README.md
[uncategorized] ~3-~3: Possible missing comma found.
Context: ...site. The repository is structured as a monorepo with both versions maintained in a sing...
(AI_HYDRA_LEO_MISSING_COMMA)
.github/pull_request_template.md
[uncategorized] ~174-~174: It appears that hyphens are missing in the adjective “up-to-date”.
Context: ...omic and well-described - [ ] Branch is up to date with main - [ ] No merge conflicts - [ ...
(UP_TO_DATE_HYPHEN)
🔇 Additional comments (7)
v2/src/components/__tests__/about/tabs/bio-tab.test.tsx (1)
3-3: Import path seems incorrect
bio1,bio2,bio3live in@/data/intro. Importing from"@/data"relies on an index barrel file that may not export those constants. If that barrel is removed or tree-shaken, the test will silently compile but throw at runtime.-import { bio1, bio2, bio3 } from "@/data"; +import { bio1, bio2, bio3 } from "@/data/intro";v2/cypress/e2e/home.cy.ts (1)
10-13: 🛠️ Refactor suggestionRace-condition risk after navigation
Immediately asserting on
cy.url()may run before the route finishes.
Chain a wait on a page element (or useshould('include', …)on the location wrapped incy.location()).-cy.get('a[href="/about"]').click(); -cy.url().should("include", "/about"); +cy.get('a[href="/about"]').click(); +cy.location("pathname").should("eq", "/about");Likely an incorrect or invalid review comment.
v2/jest.setup.ts (1)
38-40:React(or the-new JSX runtime) is not imported – tests may crash in older React setupsThe inline component uses JSX but no
import React from "react";.
If the repo isn’t on the automatic JSX runtime yet (React 17+ with"jsx": "react-jsx"), this will throw at runtime. Add the import or switch the tsconfig.🧰 Tools
🪛 Biome (1.9.4)
[error] 40-40: expected
>but instead found{Remove {
(parse)
[error] 40-40: Expected an expression but instead found '>'.
Expected an expression here.
(parse)
[error] 40-40: Expected an expression but instead found ';'.
Expected an expression here.
(parse)
v2/src/components/about/main-section.tsx (1)
13-20: Utility classmax-w-screendoes not exist in Tailwind default presets
max-w-screenwill compile to nothing unless you extended the Tailwind config. Considermax-w-screen-xl/lgetc., or add a custom size intailwind.config.ts.v2/src/components/about/tabs/skills-tab.tsx (1)
3-6: Import path may break tree-shaking / barrel resolution
defaultSkillListslives in@/data/skills. Importing from the folder root ("@/data") assumes anindex.tsbarrel that re-exports it. If that file is missing or later removed, this component will crash.-import { defaultSkillLists } from "@/data"; +import { defaultSkillLists } from "@/data/skills";.github/ISSUE_TEMPLATE/feature_request.yml (1)
1-5: Template looks solid – good structure & required validationsNo YAML or GitHub-template syntax issues detected in the header section.
Nice job tightening contributor guidance.v2/src/components/theme/__tests__/theme-mode-toggle.test.tsx (1)
1-288: Well-structured and comprehensive test suite!The test file demonstrates excellent practices:
- Extensive type definitions for type-safe mocking
- Comprehensive test coverage for all component behaviors
- Clear test descriptions and organization
- Proper mock reset between tests
| jest.mock("@/components/home/hero-section", () => { | ||
| return function MockHeroSection() { | ||
| return <div data-testid="mock-hero-section">Hero Section</div>; | ||
| }; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
jest.mock may not expose a proper ES‐module default export
When the real module is imported using import HeroSection from ..., Jest expects the mock to expose a default export in an ES-module shape. Returning a bare function works only because Jest auto-unwraps CommonJS default exports, but this breaks once the project is switched to "type": "module" or TS esModuleInterop: false.
-jest.mock("@/components/home/hero-section", () => {
- return function MockHeroSection() {
- return <div data-testid="mock-hero-section">Hero Section</div>;
- };
-});
+jest.mock("@/components/home/hero-section", () => ({
+ __esModule: true,
+ default: function MockHeroSection() {
+ return <div data-testid="mock-hero-section">Hero Section</div>;
+ },
+}));#!/bin/bash
Inspect import and mock in the test file
echo "=== Test imports ==="
rg -nE "import\s+HeroSection" -n v2/src/components/tests/home/index.test.tsx
echo
echo "=== jest.mock usage ==="
rg -n "jest.mock" -n v2/src/components/tests/home/index.test.tsx
Locate the real HeroSection component and inspect its exports
COMPONENT_FILE=$(rg -l "hero-section" -t tsx | grep "components/home" | head -n1)
if [[ -n "$COMPONENT_FILE" ]]; then
echo
echo "=== Component file: $COMPONENT_FILE ==="
rg -n "export" -n "$COMPONENT_FILE"
else
echo
echo "
fi
Check TS config for esModuleInterop setting
echo
echo "=== TypeScript config: esModuleInterop ==="
grep -R "esModuleInterop" -n tsconfig*.json || echo "Not set"
#!/usr/bin/env bash
set -euo pipefail
echo "=== Checking for ESM type in package.json ==="
grep -R '"type"\s*:\s*"module"' -n package.json || echo "No ESM "type": "module" field found in package.json"
echo
echo "=== Checking esModuleInterop in TypeScript configs ==="
grep -R '"esModuleInterop"' -n tsconfig*.json || echo ""esModuleInterop" not set or is false in any tsconfig*.json"
#!/usr/bin/env bash
set -euo pipefail
echo "=== Verify default import of HeroSection in test ==="
grep -nE "import\s+HeroSection\s+from" v2/src/components/tests/home/index.test.tsx || echo "No default import found"
echo
echo "=== Verify jest.mock signature ==="
grep -n "jest.mock" v2/src/components/tests/home/index.test.tsx
echo
echo "=== Check ESM type in package.json ==="
grep -n '"type"\s*:\s*"module"' package.json || echo "No "type": "module" in package.json"
echo
echo "=== Check esModuleInterop setting ==="
grep -n '"esModuleInterop"' tsconfig*.json || echo ""esModuleInterop" not explicitly set"
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "=== 1. Check default import in the test file ==="
grep -nE "import\s+HeroSection\s+from" -n v2/src/components/__tests__/home/index.test.tsx || echo "No default import of HeroSection found in test"
echo
echo "=== 2. Inspect jest.mock call ==="
grep -n "jest.mock" -n v2/src/components/__tests__/home/index.test.tsx || echo "No jest.mock call found in test"
echo
echo "=== 3. Locate the real hero-section component file ==="
COMP=$(rg -l "hero-section" -g "*.tsx" | grep -E "components/home/hero-section\\.tsx$" | head -1)
if [[ -n "$COMP" ]]; then
echo "Found component file: $COMP"
echo
echo "=== 4. Check how HeroSection is exported ==="
grep -nE "export\s+(default|const|function)" -n "$COMP"
else
echo "⚠️ hero-section component file not found"
fi
echo
echo "=== 5. Check if project is using ESM ==="
grep -n '"type"\s*:\s*"module"' package.json || echo "package.json does not declare \"type\": \"module\""
echo
echo "=== 6. Check esModuleInterop setting ==="
grep -n '"esModuleInterop"' tsconfig*.json || echo "\"esModuleInterop\" not explicitly set in tsconfig"Length of output: 758
🏁 Script executed:
#!/usr/bin/env bash
# Continue on errors to gather all info
echo "=== 1. Locate import of HeroSection in code under test ==="
grep -R -nE "import\s+HeroSection" v2/src/components/home || echo "No HeroSection import found in home directory"
echo
echo "=== 2. Find hero-section component file(s) ==="
find v2/src/components/home -type f | grep -i hero-section || echo "No files matching 'hero-section' in home directory"
echo
for file in $(find v2/src/components/home -type f | grep -i hero-section); do
echo "=== Exports in $file ==="
grep -nE "export\s+(default|const|function|class)" "$file" || echo "No export statements in $file"
done
echo
echo "=== 3. Check package.json for ESM type ==="
grep -n '"type"\s*:\s*"module"' package.json || echo "'type: module' not set in package.json"
echo
echo "=== 4. Check tsconfig for esModuleInterop ==="
grep -n '"esModuleInterop"' tsconfig*.json || echo "'esModuleInterop' not set in tsconfig"Length of output: 1427
Future-proof the HeroSection Jest mock for ES modules
- File: v2/src/components/tests/home/index.test.tsx (lines 5–9)
- The project currently uses CommonJS (no
"type": "module"in package.json, and noesModuleInteropin any tsconfig), sojest.mockreturning a bare function works via Jest’s CJS default-unwrap. - However, once you switch to real ESM (
"type": "module") or disableesModuleInterop, the mock will break—ESM imports won’t pick up a bare function as the default export.
Update the mock to export an ES-module shape with __esModule and default:
-jest.mock("@/components/home/hero-section", () => {
- return function MockHeroSection() {
- return <div data-testid="mock-hero-section">Hero Section</div>;
- };
-});
+jest.mock("@/components/home/hero-section", () => ({
+ __esModule: true,
+ default: function MockHeroSection() {
+ return <div data-testid="mock-hero-section">Hero Section</div>;
+ },
+}));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| jest.mock("@/components/home/hero-section", () => { | |
| return function MockHeroSection() { | |
| return <div data-testid="mock-hero-section">Hero Section</div>; | |
| }; | |
| }); | |
| jest.mock("@/components/home/hero-section", () => ({ | |
| __esModule: true, | |
| default: function MockHeroSection() { | |
| return <div data-testid="mock-hero-section">Hero Section</div>; | |
| }, | |
| })); |
🤖 Prompt for AI Agents
In v2/src/components/__tests__/home/index.test.tsx around lines 5 to 9, the
jest.mock for HeroSection returns a bare function which works under CommonJS but
will break with ES modules or when esModuleInterop is disabled. To fix this,
update the mock to return an object with __esModule: true and a default property
set to the mock function, ensuring it properly mimics an ES module default
export.
| it("has correct text styling", () => { | ||
| render(<BioTab />); | ||
|
|
||
| const paragraphs = screen.getAllByText(/.*/); | ||
| paragraphs.forEach((p) => { | ||
| if (p.textContent !== "Hello there! 👋") { | ||
| expect(p).toHaveClass( | ||
| "text-neutral-700", | ||
| "dark:text-neutral-300", | ||
| "leading-relaxed", | ||
| "text-base", | ||
| "font-normal" | ||
| ); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
getAllByText(/.*/) matches every text node – risk of false positives
The regex catches all elements with non-empty text, including hidden or unrelated ones. Filtering later in a loop is fragile. Target specific paragraphs instead, e.g.:
const bios = [bio1, bio2, bio3];
bios.forEach(text => {
expect(screen.getByText(text)).toHaveClass(
"text-neutral-700",
"dark:text-neutral-300",
"leading-relaxed",
"text-base",
"font-normal"
);
});🤖 Prompt for AI Agents
In v2/src/components/__tests__/about/tabs/bio-tab.test.tsx around lines 24 to
38, the test uses getAllByText(/.*/) which matches all elements with any text,
causing potential false positives and fragile filtering. Replace this with a
targeted approach by defining an array of expected bio texts and iterating over
it, using getByText for each specific text to assert the correct classes. This
ensures only relevant elements are tested and avoids matching unrelated or
hidden text nodes.
| jest.mock("./profile-card", () => { | ||
| return function MockProfileCard() { | ||
| return <div data-testid="profile-card">Profile Card</div>; | ||
| }; | ||
| }); | ||
|
|
||
| jest.mock("./profile-tabs", () => { | ||
| return function MockProfileTabs() { | ||
| return <div data-testid="profile-tabs">Profile Tabs</div>; | ||
| }; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relative jest.mock() paths do not match the paths used inside the component
MainSection (located in src/components/about/) imports its children with ./profile-card, ./profile-tabs, … relative to its own directory.
In this test file the same specifier resolves to
src/components/__tests__/about/profile-card – a different module – therefore the mock is ignored and the real component is loaded.
-jest.mock("./profile-card", () => { … });
+jest.mock("@/components/about/profile-card", () => { … });Apply the same fix for the other three mocks.
📝 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.
| jest.mock("./profile-card", () => { | |
| return function MockProfileCard() { | |
| return <div data-testid="profile-card">Profile Card</div>; | |
| }; | |
| }); | |
| jest.mock("./profile-tabs", () => { | |
| return function MockProfileTabs() { | |
| return <div data-testid="profile-tabs">Profile Tabs</div>; | |
| }; | |
| }); | |
| jest.mock("@/components/about/profile-card", () => { | |
| return function MockProfileCard() { | |
| return <div data-testid="profile-card">Profile Card</div>; | |
| }; | |
| }); | |
| jest.mock("@/components/about/profile-tabs", () => { | |
| return function MockProfileTabs() { | |
| return <div data-testid="profile-tabs">Profile Tabs</div>; | |
| }; | |
| }); |
🤖 Prompt for AI Agents
In v2/src/components/__tests__/about/main-section.test.tsx around lines 5 to 15,
the jest.mock() calls use relative paths that do not match the import paths used
by MainSection, causing the mocks to be ignored. Update the mock paths to be
relative to the MainSection component directory (e.g.,
'../../about/profile-card' instead of './profile-card') so they correctly mock
the intended modules. Apply this fix consistently to all four mocks in the test
file.
| defaultStats.forEach((stat) => { | ||
| expect(screen.getByText(stat.label)).toBeInTheDocument(); | ||
| expect(screen.getByTestId("animated-counter")).toHaveTextContent( | ||
| stat.count.toString() | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getByTestId always returns the first match – loop is validating the same node
Inside the forEach you keep re-selecting the first counter, so only the first stat is actually asserted.
-defaultStats.forEach((stat) => {
- expect(screen.getByText(stat.label)).toBeInTheDocument();
- expect(screen.getByTestId("animated-counter")).toHaveTextContent(
- stat.count.toString()
- );
-});
+defaultStats.forEach((stat, idx) => {
+ expect(screen.getByText(stat.label)).toBeInTheDocument();
+ expect(screen.getAllByTestId("animated-counter")[idx]).toHaveTextContent(
+ stat.count.toString()
+ );
+});📝 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.
| defaultStats.forEach((stat) => { | |
| expect(screen.getByText(stat.label)).toBeInTheDocument(); | |
| expect(screen.getByTestId("animated-counter")).toHaveTextContent( | |
| stat.count.toString() | |
| ); | |
| }); | |
| defaultStats.forEach((stat, idx) => { | |
| expect(screen.getByText(stat.label)).toBeInTheDocument(); | |
| expect(screen.getAllByTestId("animated-counter")[idx]).toHaveTextContent( | |
| stat.count.toString() | |
| ); | |
| }); |
🤖 Prompt for AI Agents
In v2/src/components/__tests__/about/stats-section.test.tsx around lines 16 to
21, the test loop uses getByTestId which always returns the first matching
element, causing the assertions to check the same node repeatedly. To fix this,
update the test to select each animated counter element uniquely, for example by
using getAllByTestId to get all counters and then iterate over them in parallel
with defaultStats, asserting each counter's text content matches the
corresponding stat count.
| jest.mock("next/image", () => ({ | ||
| __esModule: true, | ||
| default: (props: any) => { | ||
| // eslint-disable-next-line @next/next/no-img-element | ||
| return <img {...props} />; | ||
| }, | ||
| })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSX inside a .ts file breaks TypeScript compilation
next/image mock returns <img … />, but the file is saved with a .ts extension.
TS will refuse JSX in .ts unless the rarely-used allowJsx flag is enabled. Two straight-forward fixes:
-// v2/jest.setup.ts ❌ contains JSX
+// v2/jest.setup.tsx ✅ or rename…or
default: (props: any) => {
- return <img {...props} />;
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore – avoid JSX in .ts file
+ return React.createElement("img", props);
},Renaming to .tsx is the cleanest route and also removes the Biome parse errors reported.
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Biome (1.9.4)
[error] 40-40: expected > but instead found {
Remove {
(parse)
[error] 40-40: Expected an expression but instead found '>'.
Expected an expression here.
(parse)
[error] 40-40: Expected an expression but instead found ';'.
Expected an expression here.
(parse)
🤖 Prompt for AI Agents
In v2/jest.setup.ts around lines 36 to 42, the mock for next/image returns JSX
but the file has a .ts extension, causing TypeScript compilation errors. To fix
this, rename the file from .ts to .tsx so that JSX syntax is properly supported
and parsed without errors.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Style
Tests
Chores
Refactor