Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughConverted global design tokens to OKLCH, updated fonts and shadows, switched default theme to light, added a Sonner-based Toaster component and dependency, removed the legacy toast hook, adjusted TypeScript JSX config, and changed multiple internal Changes
Sequence Diagram(s)mermaid User->>Layout: load page Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (11)
hooks/use-toast.ts-6-6 (1)
6-6:⚠️ Potential issue | 🟡 MinorVerify the toast removal delay value.
TOAST_REMOVE_DELAY = 1000000is ~16.6 minutes. This is unusually long for a toast notification—typical values are 3,000–10,000ms. If the intent is for toasts to persist until manually dismissed, consider usingInfinityor a more descriptive constant name. Otherwise, this may be a typo.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/use-toast.ts` at line 6, The TOAST_REMOVE_DELAY constant is set to 1000000 (~16.6 minutes), which is likely unintended; confirm the desired behavior and update TOAST_REMOVE_DELAY in hooks/use-toast.ts accordingly: if toasts should auto-dismiss set a typical value between 3000 and 10000 (e.g., 5000), if they should persist until user action use Infinity (or null) and update any runtime checks, or rename the constant to TOAST_PERSISTENT/TOAST_AUTO_REMOVE_DELAY to make intent explicit; ensure any code that reads TOAST_REMOVE_DELAY (e.g., showToast, removeToast timers) handles the new value correctly.components/icons/linkedin-icon.tsx-7-7 (1)
7-7:⚠️ Potential issue | 🟡 MinorIncorrect accessible label for LinkedIn icon.
Line 7 uses
aria-label="website", which does not match the icon meaning.Suggested fix
- aria-label="website" + aria-label="LinkedIn"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/icons/linkedin-icon.tsx` at line 7, The LinkedIn icon component uses an incorrect aria-label ("website"); update the accessible label on the LinkedInIcon component (or the element rendering the SVG/button in linkedin-icon.tsx) to a descriptive value like "LinkedIn" or "LinkedIn profile" so screen readers convey the correct purpose; replace aria-label="website" with the new label and ensure it matches the intent wherever the LinkedInIcon is rendered.components/mobile-nav.tsx-29-35 (1)
29-35:⚠️ Potential issue | 🟡 MinorKeep the list children as
<li>elements.This inserts a
<div>directly under the<ul>, which is invalid list markup and can confuse assistive tech. Wrap the controls in an<li>or move them outside the list.Suggested fix
- <div className="flex flex-col gap-4"> + <li className="flex flex-col gap-4"> <Button variant={'outline'}> Book a meeting </Button> <ModeToggle /> - </div> + </li>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/mobile-nav.tsx` around lines 29 - 35, The JSX places a <div> directly inside a <ul>, which is invalid; replace the <div className="flex flex-col gap-4"> that wraps Button and ModeToggle with an <li> (you can keep an inner div for layout if needed) or move that control block entirely outside the <ul>; update the element wrapping Button and ModeToggle so the list children are proper <li> elements (target the JSX near Button and ModeToggle in the mobile navigation component).components/systaliko-ui/cards/cards-stack.tsx-17-23 (1)
17-23:⚠️ Potential issue | 🟡 MinorSpread
propsbefore the mergedstyle.If a caller passes
style, Line 23 re-applies that original prop after the merge, so the container loses its defaultperspective. That makes the style merge here incorrect for any customized usage.Suggested fix
export const CardsStackContainer = React.forwardRef< HTMLDivElement, React.HTMLProps<HTMLDivElement> ->(({ children, className, ...props }, ref) => { +>(({ children, className, style, ...props }, ref) => { return ( <div ref={ref} + {...props} className={cn('relative w-full', className)} - style={{ perspective: '1000px', ...props.style }} - {...props} + style={{ perspective: '1000px', ...style }} > {children} </div> ); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/systaliko-ui/cards/cards-stack.tsx` around lines 17 - 23, The component spreads {...props} after setting style which allows a caller-provided style to overwrite the default perspective; update the component (the arrow function receiving ({ children, className, ...props }, ref) in cards-stack.tsx or the CardsStack wrapper) to either destructure style from props (e.g., const { style, ...rest } = props) and then render {...rest} before applying style={{ perspective: '1000px', ...style }}, or move {...props} before the style prop so the merged style retains the default perspective while honoring caller overrides.sections/values.tsx-19-26 (1)
19-26:⚠️ Potential issue | 🟡 MinorTypo in value ID:
value-startegy→value-strategy.✏️ Proposed fix
{ - id: 'value-startegy', + id: 'value-strategy', title: 'Strategy first',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/values.tsx` around lines 19 - 26, The value object has a typo in its id: change the id value 'value-startegy' to 'value-strategy' in the values array/object (look for the entry with title 'Strategy first' and icon ListStartIcon) so downstream lookups and keys use the correct spelling; update only the id string to 'value-strategy' to fix references and keep consistency.sections/work.tsx-58-66 (1)
58-66:⚠️ Potential issue | 🟡 MinorImage alt text should be descriptive.
Using
alt="project"for all images is not accessible. Screen reader users won't know what the image depicts. Use descriptive alt text that includes the project name.♿ Proposed fix
<Image className="w-full max-h-full" width={906} height={604} src={project.imageUrl} - alt="project" + alt={`${project.title} project showcase`} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/work.tsx` around lines 58 - 66, The Image usage sets alt="project", which is not descriptive; update the Image component's alt prop to include the specific project's name (e.g., use project.name or project.title) and a short descriptor like "screenshot" or "preview" so screen readers get meaningful context—locate the Image element that reads src={project.imageUrl} and replace the hardcoded alt with a descriptive string derived from project (e.g., project.name).sections/services.tsx-6-43 (1)
6-43:⚠️ Potential issue | 🟡 MinorTypos in service IDs.
Two IDs contain spelling errors:
service-starategy-identity-service→service-strategy-identity-servicegrowth-anilytics-service→growth-analytics-service✏️ Proposed fix
{ - id: 'service-starategy-identity-service', + id: 'service-strategy-identity-service', title: 'Brand Strategy & Identity',{ - id: 'growth-anilytics-service', + id: 'growth-analytics-service', title: 'Growth & Analytics',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/services.tsx` around lines 6 - 43, Update the misspelled id strings in the SERVICES_CARDS array: change "service-starategy-identity-service" to "service-strategy-identity-service" and change "growth-anilytics-service" to "growth-analytics-service" so the unique identifiers in the SERVICE_CARDS constant match expected naming (locate these values in the SERVICES_CARDS array to edit the id fields).sections/values.tsx-67-71 (1)
67-71:⚠️ Potential issue | 🟡 MinorRemove the leading hyphen from the gradient class.
The class
bg-linear-45is a valid Tailwind CSS v4 gradient utility for a 45-degree linear gradient. However, the leading hyphen prefix (-bg-linear-45) is incorrect—gradient utilities do not support negation. Change tobg-linear-45.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/values.tsx` around lines 67 - 71, The element rendering the icon uses an incorrect class string with a leading hyphen in the gradient utility; update the className template in the icon container (the div that currently contains `className={`size-14 flex items-center justify-center -bg-linear-45 rounded ${value.iconBg}`}`) to remove the hyphen so it uses `bg-linear-45` instead of `-bg-linear-45` (ensure any dynamic `${value.iconBg}` remains intact).sections/work.tsx-43-45 (1)
43-45:⚠️ Potential issue | 🟡 MinorButton has no action.
The "Explore all cases" button does nothing when clicked. If the route doesn't exist yet, consider using a disabled state or removing the button until the feature is ready.
Proposed fix to link to a future route
- <Button variant={'secondary'}> + <Button variant={'secondary'} asChild> + <Link href="/work"> Explore all cases + </Link> </Button>This assumes the Button component supports
asChildprop (common in Radix UI-based components) and requires importingLinkfromnext/link.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/work.tsx` around lines 43 - 45, The "Explore all cases" Button in sections/work.tsx currently has no click action; wrap or render it as a Link to the target route (e.g., href="/cases") so it navigates, or if the route isn't ready set the Button to disabled or remove it. Update the Button usage (component named Button) to support navigation by using its asChild prop and a next/link Link or by wrapping Button in Link from next/link, and add the href to point to the intended route (or set disabled={true} and adjust aria-disabled if removing functionality). Ensure you import Link from next/link and update the Button element labeled "Explore all cases" accordingly.sections/testimonials.tsx-77-79 (1)
77-79:⚠️ Potential issue | 🟡 MinorFix broken ARIA references: add
idattributes to matcharia-labelledbyandaria-describedby.The
aria-labelledbyandaria-describedbyattributes reference IDs likecard-${testimonial.id}-titleandcard-${testimonial.id}-content, butcard-testimonial.tsxdoes not render elements with these IDs. Addidprops to the relevant child components or apply the IDs directly to theTestimonialQuoteandTestimonialAuthorelements.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/testimonials.tsx` around lines 77 - 79, The ARIA attributes on the card use aria-labelledby={`card-${testimonial.id}-title`} and aria-describedby={`card-${testimonial.id}-content`} but no matching id attributes are rendered; update the TestimonialQuote and TestimonialAuthor (or the child components that render the title/content) to accept and render id props and pass id={`card-${testimonial.id}-content`} to TestimonialQuote and id={`card-${testimonial.id}-title`} to TestimonialAuthor (or apply those ids directly on the elements that output the title and content) so the ARIA references resolve correctly for each testimonial.components/systaliko-ui/blocks/scroll-animation.tsx-51-58 (1)
51-58:⚠️ Potential issue | 🟡 MinorDocument that
spacerClass="h-0"disables scroll-driven animations.The scroll progress is driven by scrolling through the container including the spacer element (default
h-96). WhenspacerClass="h-0"is used (as insections/process.tsx), thescrollYProgresswill not vary meaningfully since there's no scrollable height to track, effectively disabling animations.If this is intentional for static layouts, consider adding a comment. Otherwise, consumers like
sections/process.tsxthat usespacerClass="h-0"withScrollScaleexpecting animation (inputRange={[0, 0.2]}) will see no animation effect.
🧹 Nitpick comments (15)
hooks/use-toast.ts (1)
155-159: Consider refining theupdateparameter type.The
updatefunction acceptsToasterToastwhich requiresid, but the implementation overridesidanyway. UsePartial<ToasterToast>to match theUPDATE_TOASTaction type and avoid requiring callers to provide anidthat gets ignored.♻️ Suggested improvement
- const update = (props: ToasterToast) => + const update = (props: Partial<ToasterToast>) => dispatch({ type: "UPDATE_TOAST", toast: { ...props, id }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/use-toast.ts` around lines 155 - 159, The update function currently types its parameter as ToasterToast which requires an id that the function then overrides; change the parameter type to Partial<ToasterToast> (or Omit<ToasterToast, 'id'>) so callers aren't forced to pass an id that is ignored, and keep the implementation of update (which spreads props and sets id) and the dispatch to the "UPDATE_TOAST" action unchanged; update the signature for the update function in hooks/use-toast.ts to use the new type and ensure TypeScript no longer errors when callers omit id.data/data.ts (1)
3-3: Make the site URL deployment-specific.
app/layout.tsxfeeds this into metadata/Open Graph, so any non-demo deployment will publish the demo host as its canonical URL. Pull it from env/config instead of hardcoding it here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@data/data.ts` at line 3, The hardcoded url field in data.ts (the "url" property) must be replaced with a deployment-specific value read from environment/config; update data.ts to import or read your runtime config (e.g., process.env.NEXT_PUBLIC_SITE_URL or your existing config helper) and set url to that env value with a sensible fallback (like the demo URL) so app/layout.tsx continues to get a string but uses the deployment's canonical URL. Ensure the symbol "url" in the exported data object is updated and that any helper you use is available at build/runtime.components/ui/button.tsx (1)
38-55: Default native buttons totype="button".Without an explicit type,
<button>submits the nearest form. This is a shared primitive, so it's safer to opt out unless a caller explicitly requestssubmit.💡 Suggested tweak
function Button({ className, variant, size, asChild = false, + type, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & { asChild?: boolean }) { const Comp = asChild ? Slot : "button" return ( <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} + type={asChild ? undefined : type ?? "button"} {...props} /> ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/button.tsx` around lines 38 - 55, The Button component currently leaves the native button type unspecified; update Button to default to type="button" unless a caller passes an explicit type and avoid forcing a type onto a custom child: destructure type from props with a default (e.g. const { type = "button", ...rest } = props), and when rendering use <Comp ... { ...(Comp === "button" ? { type } : {}) } {...rest} /> (referencing the Button function, Comp variable, asChild flag and props) so native buttons default to non-submitting while custom children aren’t given an invalid type prop.components/logo.tsx (1)
1-6: Consider importingSVGPropsexplicitly for clarity.While
React.SVGProps<SVGSVGElement>works without an explicit import in this Next.js + TypeScript setup due to ambient type declarations from@types/react, it's clearer to import the type explicitly:+import type { SVGProps } from "react"; import { cn } from "@/lib/utils"; export const Logo = ({ className, ...props -}: React.SVGProps<SVGSVGElement>) => { +}: SVGProps<SVGSVGElement>) => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/logo.tsx` around lines 1 - 6, Import the SVGProps type explicitly from React and update the Logo component signature to use it; specifically, add an import like "import React, { SVGProps } from 'react'" (or "import type { SVGProps } from 'react'") and change the prop type on the Logo function from "React.SVGProps<SVGSVGElement>" to "SVGProps<SVGSVGElement>" so the component (Logo) uses the explicit SVGProps type for clarity.sections/values.tsx (2)
7-7: Unused import:staggerfrommotion.The
staggerfunction is imported but not used in this file.ContainerStaggerlikely handles staggering internally.🧹 Proposed fix
-import { stagger } from 'motion';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/values.tsx` at line 7, The import of stagger from 'motion' is unused in sections/values.tsx; remove the unused import statement (the symbol stagger) and rely on ContainerStagger for staggering behavior to eliminate the unused-import lint warning and dead code.
50-50: "Request demo" button should be a link.A
Buttonwithvariant="link"that performs navigation should use an anchor element for proper accessibility and SEO. Consider wrapping withLinkor using theasChildpattern.♿ Proposed fix
- <Button variant={'link'}>Request demo</Button> + <Button variant={'link'} asChild> + <Link href="/contact">Request demo</Link> + </Button>Requires importing
Linkfromnext/link.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/values.tsx` at line 50, The "Request demo" Button currently uses variant="link" but must be rendered as an actual anchor for accessibility/SEO; import Link from next/link and replace the Button usage so it renders an <a> with an href (either wrap <Button variant="link"> with <Link href="/your-path">...</Link> or use the UI library's asChild pattern like <Button asChild> and place <Link href="/your-path">Request demo</Link> inside); update the Button call in sections/values.tsx (the Button component) to ensure the href is provided and the element rendered is an anchor.app/(home)/page.tsx (1)
1-21:Worksection is defined but not rendered.The
sections/work.tsxcomponent exists in this PR but is not imported or rendered in the home page. If this is intentional (e.g., work-in-progress), consider adding a comment or removing the file until it's ready to avoid dead code.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(home)/page.tsx around lines 1 - 21, The Home page currently doesn't render the Work section defined in sections/work.tsx; either import the Work component and add <Work /> into the Home component JSX (e.g., alongside Hero/About/Values/etc.) to display it, or if the work component is intentionally not ready, remove sections/work.tsx or add a clear TODO comment near the Work component export to avoid dead code; reference the Work component in sections/work.tsx and the Home function in app/(home)/page.tsx to locate where to change.sections/services.tsx (2)
45-72: Services section lacks a visible heading.Unlike other sections (About, Work, Values), this section has no
<h2>or section title. Adding a heading improves accessibility (screen reader navigation) and provides context for users.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/services.tsx` around lines 45 - 72, The Services component is missing a visible section heading which harms accessibility; add a semantic heading (e.g., an <h2> with appropriate text like "Services") inside the Services() return, placed before the CardsStackContainer so screen readers and users can identify the section; ensure the heading uses the same styling pattern as other sections (matching size/utility classes used elsewhere) and keep it outside each CardSticky so it appears once for the whole SERVICES_CARDS list.
53-54: Remove commented-out code.The commented className is dead code. Either use it or remove it to keep the codebase clean.
🧹 Proposed fix
<CardSticky key={service.id} index={index} - // className="min-h-[50vh] py-8 px-12 flex flex-wrap md:flex-nowrap md:gap-8 justify-between items-start even:border bg-card odd:bg-muted" className="min-h-[50vh] py-8 px-12 flex gap-6 justify-between flex-wrap even:border bg-card odd:bg-muted"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/services.tsx` around lines 53 - 54, Remove the dead commented-out className in sections/services.tsx: delete the line starting with "// className="min-h-[50vh] py-8 px-12 flex flex-wrap md:flex-nowrap md:gap-8 justify-between items-start even:border bg-card odd:bg-muted"" so only the active className remains (className="min-h-[50vh] py-8 px-12 flex gap-6 justify-between flex-wrap even:border bg-card odd:bg-muted"); no behavioral changes required, just remove the commented code to keep the file clean.sections/footer.tsx (1)
25-42: Consider consolidating navigation links withdata/constants.ts.The footer defines its own
LINKSarray whiledata/constants.tshasNAV_LINKSwith the same labels but placeholderhref: '/'values. Consider using a single source of truth for navigation links to prevent inconsistencies.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/footer.tsx` around lines 25 - 42, The footer has a duplicated LINKS constant that conflicts with the canonical NAV_LINKS; remove the local LINKS in the footer and import NAV_LINKS from data/constants instead, or update NAV_LINKS to include the correct href values and then reference NAV_LINKS inside the Footer component (replace occurrences of LINKS with NAV_LINKS) so there is a single source of truth for navigation links.sections/header.tsx (1)
14-19: Add accessible name to the logo link.The logo link contains only visual elements (icon and text) but lacks an accessible name for screen readers. Consider adding an
aria-labelto clarify the link's purpose.♿ Proposed fix
- <Link className="flex-grow-[1] inline-flex items-center gap-1" href="/"> + <Link className="grow inline-flex items-center gap-1" href="/" aria-label="Veo - Go to homepage">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/header.tsx` around lines 14 - 19, The logo Link currently lacks an accessible name for screen readers; update the Link element (the wrapping Link that contains the Logo component and the "Veo" span) to include an appropriate aria-label (e.g., aria-label="Veo homepage" or "Go to Veo homepage") so assistive technologies can announce its purpose; ensure the aria-label is on the Link component (not the inner Logo) and keep the visible text unchanged.components/systaliko-ui/cards/cards-stack-rotated.tsx (1)
69-69: Remove unusedcontainerRef.The ref is created but never used for any purpose.
🧹 Remove dead code
export const CardsContainer: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ children, className, ...props }) => { - const containerRef = React.useRef<HTMLDivElement>(null); - return ( <div - ref={containerRef} className={cn('sticky top-0 left-0', className)} style={{ perspective: '1000px', ...props.style }} {...props} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/systaliko-ui/cards/cards-stack-rotated.tsx` at line 69, Remove the unused ref by deleting the declaration of containerRef (const containerRef = React.useRef<HTMLDivElement>(null)); in the CardsStackRotated component so there is no dead code left; search for "containerRef" to ensure it's not referenced elsewhere and remove any related unused imports if they become unnecessary.components/systaliko-ui/blocks/scroll-animation.tsx (2)
106-111: Use more specific types instead ofunknown[].The range props use
unknown[]which bypasses type safety. Since these are used withuseTransform, they should be typed more specifically.💡 Suggested typing improvement
}: HTMLMotionProps<'div'> & { inputRange?: MapInputRange; - insetRangeY?: unknown[]; - insetXRange?: unknown[]; - roundednessRange?: unknown[]; + insetRangeY?: number[]; + insetXRange?: number[]; + roundednessRange?: number[]; }) {Apply similar changes to other components using
unknown[](lines 137, 155, 173, 191-192).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/systaliko-ui/blocks/scroll-animation.tsx` around lines 106 - 111, The props insetRangeY, insetXRange, and roundednessRange are typed as unknown[] which removes type safety; change their types to more specific arrays (e.g., number[] or (number | string)[] as appropriate for values passed into useTransform) and update the component signature (the function in scroll-animation.tsx with props inputRange, insetRangeY, insetXRange, roundednessRange) to use those concrete types; then adjust any usages/calls to useTransform inside this component to match the new types and apply the same replacement for other occurrences noted (the other components/lines referenced in the review).
196-203:layoutwithout"position"may cause unexpected layout animations.Using
layout(which equalslayout={true}) animates both position and size changes. If only position animations are intended (as in other components likeCardTransformed), uselayout="position"to prevent unwanted size animations.Also,
willChange: 'border-radius'is non-standard. The correct value would be'border-radius'in some browsers, but for better compatibility use'auto'or remove it sinceborderRadiuschanges aren't typically performance-critical.💡 Suggested fix
return ( <motion.div - layout + layout="position" className={className} - style={{ borderRadius, willChange: 'border-radius', ...style }} + style={{ borderRadius, ...style }} {...props} /> );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/systaliko-ui/blocks/scroll-animation.tsx` around lines 196 - 203, The motion.div in ScrollAnimation currently uses layout (i.e., layout={true}) and a nonstandard willChange value; change layout to layout="position" on the motion.div to restrict animations to position-only (avoid unintended size/scale animations), and remove or replace the willChange: 'border-radius' in the style (either remove the willChange entry or use a more compatible value like 'auto' if needed) while preserving borderRadius, className and spreading ...props as before.app/layout.tsx (1)
32-39: Consider deriving OG image URL fromdata.urlfor consistency.The metadata uses
data.urlfor the OpenGraph URL but hardcodes the image URL to'https://veo.vercel.app/og-image.png'. If the deployment URL changes, this could lead to inconsistency.💡 Suggested improvement
images: [ { - url: 'https://veo.vercel.app/og-image.png', + url: `${data.url}/og-image.png`, width: 1200, height: 630, alt: data.name, }, ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/layout.tsx` around lines 32 - 39, The OG image URL is hardcoded instead of being derived from data.url, causing potential deployment inconsistencies; update the metadata construction that builds the images array (images[0].url) to derive the image URL from data.url (e.g., use URL resolution or string join like new URL('/og-image.png', data.url).toString() or `${data.url.replace(/\/$/, '')}/og-image.png`) so the image host matches the site URL; locate the images array in the metadata block where data.name and data.url are used and replace the literal 'https://veo.vercel.app/og-image.png' with the derived URL expression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/desktop-nav.tsx`:
- Around line 22-25: The "Book a meeting" Button currently renders without an
action; update the Button element in components/desktop-nav.tsx (and mirror the
same change in components/mobile-nav.tsx) to provide a real action: either add
an href prop to navigate to your scheduling page (e.g., "/book" or your external
scheduler URL) or attach an onClick handler that opens the scheduling URL in a
new tab or triggers the booking modal (call the existing openBookingModal or
navigate function if available). Ensure you use the same Button component API
(e.g., onClick or href) so the CTA is keyboard-accessible and works the same in
both DesktopNav and MobileNav.
- Around line 12-16: NAV_LINKS currently contains identical hrefs ('/'), causing
all nav items to route to the homepage and breaking both desktop and mobile
navigation; update the NAV_LINKS entries (the array referenced by NAV_LINKS used
in components/desktop-nav.tsx and mobile nav) so each object has a distinct href
that matches the intended destination (e.g., '/about', '/services', '/pricing'
or '#about', '#services', '#pricing' for anchors), then ensure the Link usage in
the desktop NavLink mapping continues to use link.href and link.label as before
so each <Link> points to the new unique hrefs.
In `@components/systaliko-ui/cards/card-testimonial.tsx`:
- Line 55: The Card in card-testimonial.tsx is passing a precomputed className
via cn(cardVariants({ variant, className })) which duplicates the styling
because Card itself calls cardVariants; instead, remove the call to cardVariants
and pass the variant and className props directly to Card (i.e., use <Card
variant={variant} className={className} ...>) so Card can apply cardVariants
internally; update the Card invocation in
components/systaliko-ui/cards/card-testimonial.tsx accordingly and remove
cn(cardVariants(...)) usage.
In `@components/systaliko-ui/cards/cards-stack-rotated.tsx`:
- Line 74: The CSS class string passed into cn in the CardsStackRotated
component contains a typo: change the 'stiky' token in the className expression
(className={cn('stiky top-0 left-0', className)}) to 'sticky' so the sticky
positioning works; update the string used in that className call accordingly.
- Around line 88-98: The destructured prop default incrementRotation = -index +
90 references index during parameter defaulting which is confusing and fragile;
move the derived rotation calculation into the component body instead of using a
parameter default that depends on another parameter (or if you intended a static
default, set incrementRotation to a constant like 0). Locate the component that
destructures { arrayLength, index, incrementY, incrementZ, incrementRotation,
... } (the props block in cards-stack-rotated.tsx), remove the computed default
from the parameter list, and compute a local value (e.g., const computedRotation
= incrementRotation ?? (-index + 90)) inside the function before using it.
In `@components/systaliko-ui/rating-stars.tsx`:
- Around line 14-17: Clamp the incoming rating value to the valid range [0,
maxRating] before computing filledStars, fractionalPart, and emptyStars to avoid
negative counts and Array(...) crashes; e.g., compute a local clampedRating =
Math.max(0, Math.min(rating, maxRating)) and use clampedRating in the existing
calculations (filledStars, fractionalPart, emptyStars) and any other places that
derive star counts (also update the other occurrence around the code mentioned
at lines 59-60).
- Around line 41-56: The gradient id "half" is global and will collide across
instances; update the RatingStars component to generate a unique gradient id per
instance (e.g., using React's useId() or a useRef-based unique string) and
replace id="half" with that unique id and the fill="url(`#half`)" with the
matching fill using the same unique id (e.g., fill={`url(#${uniqueId})`}) so
each rendered star uses its own gradient.
In `@data/constants.ts`:
- Around line 6-16: The navigation entries for labels 'About', 'Services', and
'Pricing' currently all use href: '/'—update each object's href so they point to
the correct destinations (e.g., '/about' or '#about', '/services' or
'#services', '/pricing' or '#pricing' depending on your routing/anchor scheme)
by locating the array of link objects (the ones with label: 'About', label:
'Services', label: 'Pricing') in data/constants.ts and replacing their href
values with the intended paths.
In `@hooks/use-toast.ts`:
- Around line 189-197: The useEffect subscribing the toast listener currently
lists [state] as a dependency which causes repeated unsubscribe/resubscribe on
every state change; update the effect in the hook where React.useEffect adds
listeners.push(setState) and removes it on cleanup to use an empty dependency
array ([]) so the listener is added once on mount and removed on unmount (keep
references to listeners and setState as-is).
In `@sections/footer.tsx`:
- Around line 53-64: The JSX currently nests an <a> inside the Button created
for each SOCIAL_LINKS entry (map over SOCIAL_LINKS using link.id, link.href,
link.icon), producing invalid HTML; change the Button to render as an anchor
using its asChild prop (so Button wraps the anchor element or is rendered as an
<a>) and move href to the rendered anchor (use link.href), add target="_blank"
and rel="noopener noreferrer" for external links, and ensure each icon-only link
has an accessible name by adding an aria-label (e.g., from link.label or
similar) when rendering the icon inside Button/asChild.
In `@sections/hero.tsx`:
- Around line 18-21: The external anchor that opens in a new tab (the <a>
element pointing to "https://systaliko-ui.vercel.app/docs/templates/veo") is
missing rel attributes; update that anchor to include rel="noopener noreferrer"
to prevent tabnabbing and protect the opener, ensuring the element that
currently uses target="_blank" also includes rel="noopener noreferrer".
In `@sections/testimonials.tsx`:
- Around line 63-66: The card is being passed a shifted index (index={index +
2}) which breaks the stacking/transform math and can produce negative z-index or
invalid scroll ranges; change the prop to use the actual zero-based index
(index={index}) wherever the Card (or the component receiving index) relies on
TESTIMONIALS.length for stacking calculations (look for usages of index in the
card transform/z-index/scroll math) and update any dependent arithmetic if you
intentionally need an offset so all z-index/transform calculations remain
consistent with TESTIMONIALS.length and zero-based indexing.
---
Minor comments:
In `@components/icons/linkedin-icon.tsx`:
- Line 7: The LinkedIn icon component uses an incorrect aria-label ("website");
update the accessible label on the LinkedInIcon component (or the element
rendering the SVG/button in linkedin-icon.tsx) to a descriptive value like
"LinkedIn" or "LinkedIn profile" so screen readers convey the correct purpose;
replace aria-label="website" with the new label and ensure it matches the intent
wherever the LinkedInIcon is rendered.
In `@components/mobile-nav.tsx`:
- Around line 29-35: The JSX places a <div> directly inside a <ul>, which is
invalid; replace the <div className="flex flex-col gap-4"> that wraps Button and
ModeToggle with an <li> (you can keep an inner div for layout if needed) or move
that control block entirely outside the <ul>; update the element wrapping Button
and ModeToggle so the list children are proper <li> elements (target the JSX
near Button and ModeToggle in the mobile navigation component).
In `@components/systaliko-ui/cards/cards-stack.tsx`:
- Around line 17-23: The component spreads {...props} after setting style which
allows a caller-provided style to overwrite the default perspective; update the
component (the arrow function receiving ({ children, className, ...props }, ref)
in cards-stack.tsx or the CardsStack wrapper) to either destructure style from
props (e.g., const { style, ...rest } = props) and then render {...rest} before
applying style={{ perspective: '1000px', ...style }}, or move {...props} before
the style prop so the merged style retains the default perspective while
honoring caller overrides.
In `@hooks/use-toast.ts`:
- Line 6: The TOAST_REMOVE_DELAY constant is set to 1000000 (~16.6 minutes),
which is likely unintended; confirm the desired behavior and update
TOAST_REMOVE_DELAY in hooks/use-toast.ts accordingly: if toasts should
auto-dismiss set a typical value between 3000 and 10000 (e.g., 5000), if they
should persist until user action use Infinity (or null) and update any runtime
checks, or rename the constant to TOAST_PERSISTENT/TOAST_AUTO_REMOVE_DELAY to
make intent explicit; ensure any code that reads TOAST_REMOVE_DELAY (e.g.,
showToast, removeToast timers) handles the new value correctly.
In `@sections/services.tsx`:
- Around line 6-43: Update the misspelled id strings in the SERVICES_CARDS
array: change "service-starategy-identity-service" to
"service-strategy-identity-service" and change "growth-anilytics-service" to
"growth-analytics-service" so the unique identifiers in the SERVICE_CARDS
constant match expected naming (locate these values in the SERVICES_CARDS array
to edit the id fields).
In `@sections/testimonials.tsx`:
- Around line 77-79: The ARIA attributes on the card use
aria-labelledby={`card-${testimonial.id}-title`} and
aria-describedby={`card-${testimonial.id}-content`} but no matching id
attributes are rendered; update the TestimonialQuote and TestimonialAuthor (or
the child components that render the title/content) to accept and render id
props and pass id={`card-${testimonial.id}-content`} to TestimonialQuote and
id={`card-${testimonial.id}-title`} to TestimonialAuthor (or apply those ids
directly on the elements that output the title and content) so the ARIA
references resolve correctly for each testimonial.
In `@sections/values.tsx`:
- Around line 19-26: The value object has a typo in its id: change the id value
'value-startegy' to 'value-strategy' in the values array/object (look for the
entry with title 'Strategy first' and icon ListStartIcon) so downstream lookups
and keys use the correct spelling; update only the id string to 'value-strategy'
to fix references and keep consistency.
- Around line 67-71: The element rendering the icon uses an incorrect class
string with a leading hyphen in the gradient utility; update the className
template in the icon container (the div that currently contains
`className={`size-14 flex items-center justify-center -bg-linear-45 rounded
${value.iconBg}`}`) to remove the hyphen so it uses `bg-linear-45` instead of
`-bg-linear-45` (ensure any dynamic `${value.iconBg}` remains intact).
In `@sections/work.tsx`:
- Around line 58-66: The Image usage sets alt="project", which is not
descriptive; update the Image component's alt prop to include the specific
project's name (e.g., use project.name or project.title) and a short descriptor
like "screenshot" or "preview" so screen readers get meaningful context—locate
the Image element that reads src={project.imageUrl} and replace the hardcoded
alt with a descriptive string derived from project (e.g., project.name).
- Around line 43-45: The "Explore all cases" Button in sections/work.tsx
currently has no click action; wrap or render it as a Link to the target route
(e.g., href="/cases") so it navigates, or if the route isn't ready set the
Button to disabled or remove it. Update the Button usage (component named
Button) to support navigation by using its asChild prop and a next/link Link or
by wrapping Button in Link from next/link, and add the href to point to the
intended route (or set disabled={true} and adjust aria-disabled if removing
functionality). Ensure you import Link from next/link and update the Button
element labeled "Explore all cases" accordingly.
---
Nitpick comments:
In `@app/`(home)/page.tsx:
- Around line 1-21: The Home page currently doesn't render the Work section
defined in sections/work.tsx; either import the Work component and add <Work />
into the Home component JSX (e.g., alongside Hero/About/Values/etc.) to display
it, or if the work component is intentionally not ready, remove
sections/work.tsx or add a clear TODO comment near the Work component export to
avoid dead code; reference the Work component in sections/work.tsx and the Home
function in app/(home)/page.tsx to locate where to change.
In `@app/layout.tsx`:
- Around line 32-39: The OG image URL is hardcoded instead of being derived from
data.url, causing potential deployment inconsistencies; update the metadata
construction that builds the images array (images[0].url) to derive the image
URL from data.url (e.g., use URL resolution or string join like new
URL('/og-image.png', data.url).toString() or `${data.url.replace(/\/$/,
'')}/og-image.png`) so the image host matches the site URL; locate the images
array in the metadata block where data.name and data.url are used and replace
the literal 'https://veo.vercel.app/og-image.png' with the derived URL
expression.
In `@components/logo.tsx`:
- Around line 1-6: Import the SVGProps type explicitly from React and update the
Logo component signature to use it; specifically, add an import like "import
React, { SVGProps } from 'react'" (or "import type { SVGProps } from 'react'")
and change the prop type on the Logo function from
"React.SVGProps<SVGSVGElement>" to "SVGProps<SVGSVGElement>" so the component
(Logo) uses the explicit SVGProps type for clarity.
In `@components/systaliko-ui/blocks/scroll-animation.tsx`:
- Around line 106-111: The props insetRangeY, insetXRange, and roundednessRange
are typed as unknown[] which removes type safety; change their types to more
specific arrays (e.g., number[] or (number | string)[] as appropriate for values
passed into useTransform) and update the component signature (the function in
scroll-animation.tsx with props inputRange, insetRangeY, insetXRange,
roundednessRange) to use those concrete types; then adjust any usages/calls to
useTransform inside this component to match the new types and apply the same
replacement for other occurrences noted (the other components/lines referenced
in the review).
- Around line 196-203: The motion.div in ScrollAnimation currently uses layout
(i.e., layout={true}) and a nonstandard willChange value; change layout to
layout="position" on the motion.div to restrict animations to position-only
(avoid unintended size/scale animations), and remove or replace the willChange:
'border-radius' in the style (either remove the willChange entry or use a more
compatible value like 'auto' if needed) while preserving borderRadius, className
and spreading ...props as before.
In `@components/systaliko-ui/cards/cards-stack-rotated.tsx`:
- Line 69: Remove the unused ref by deleting the declaration of containerRef
(const containerRef = React.useRef<HTMLDivElement>(null)); in the
CardsStackRotated component so there is no dead code left; search for
"containerRef" to ensure it's not referenced elsewhere and remove any related
unused imports if they become unnecessary.
In `@components/ui/button.tsx`:
- Around line 38-55: The Button component currently leaves the native button
type unspecified; update Button to default to type="button" unless a caller
passes an explicit type and avoid forcing a type onto a custom child:
destructure type from props with a default (e.g. const { type = "button",
...rest } = props), and when rendering use <Comp ... { ...(Comp === "button" ? {
type } : {}) } {...rest} /> (referencing the Button function, Comp variable,
asChild flag and props) so native buttons default to non-submitting while custom
children aren’t given an invalid type prop.
In `@data/data.ts`:
- Line 3: The hardcoded url field in data.ts (the "url" property) must be
replaced with a deployment-specific value read from environment/config; update
data.ts to import or read your runtime config (e.g.,
process.env.NEXT_PUBLIC_SITE_URL or your existing config helper) and set url to
that env value with a sensible fallback (like the demo URL) so app/layout.tsx
continues to get a string but uses the deployment's canonical URL. Ensure the
symbol "url" in the exported data object is updated and that any helper you use
is available at build/runtime.
In `@hooks/use-toast.ts`:
- Around line 155-159: The update function currently types its parameter as
ToasterToast which requires an id that the function then overrides; change the
parameter type to Partial<ToasterToast> (or Omit<ToasterToast, 'id'>) so callers
aren't forced to pass an id that is ignored, and keep the implementation of
update (which spreads props and sets id) and the dispatch to the "UPDATE_TOAST"
action unchanged; update the signature for the update function in
hooks/use-toast.ts to use the new type and ensure TypeScript no longer errors
when callers omit id.
In `@sections/footer.tsx`:
- Around line 25-42: The footer has a duplicated LINKS constant that conflicts
with the canonical NAV_LINKS; remove the local LINKS in the footer and import
NAV_LINKS from data/constants instead, or update NAV_LINKS to include the
correct href values and then reference NAV_LINKS inside the Footer component
(replace occurrences of LINKS with NAV_LINKS) so there is a single source of
truth for navigation links.
In `@sections/header.tsx`:
- Around line 14-19: The logo Link currently lacks an accessible name for screen
readers; update the Link element (the wrapping Link that contains the Logo
component and the "Veo" span) to include an appropriate aria-label (e.g.,
aria-label="Veo homepage" or "Go to Veo homepage") so assistive technologies can
announce its purpose; ensure the aria-label is on the Link component (not the
inner Logo) and keep the visible text unchanged.
In `@sections/services.tsx`:
- Around line 45-72: The Services component is missing a visible section heading
which harms accessibility; add a semantic heading (e.g., an <h2> with
appropriate text like "Services") inside the Services() return, placed before
the CardsStackContainer so screen readers and users can identify the section;
ensure the heading uses the same styling pattern as other sections (matching
size/utility classes used elsewhere) and keep it outside each CardSticky so it
appears once for the whole SERVICES_CARDS list.
- Around line 53-54: Remove the dead commented-out className in
sections/services.tsx: delete the line starting with "// className="min-h-[50vh]
py-8 px-12 flex flex-wrap md:flex-nowrap md:gap-8 justify-between items-start
even:border bg-card odd:bg-muted"" so only the active className remains
(className="min-h-[50vh] py-8 px-12 flex gap-6 justify-between flex-wrap
even:border bg-card odd:bg-muted"); no behavioral changes required, just remove
the commented code to keep the file clean.
In `@sections/values.tsx`:
- Line 7: The import of stagger from 'motion' is unused in sections/values.tsx;
remove the unused import statement (the symbol stagger) and rely on
ContainerStagger for staggering behavior to eliminate the unused-import lint
warning and dead code.
- Line 50: The "Request demo" Button currently uses variant="link" but must be
rendered as an actual anchor for accessibility/SEO; import Link from next/link
and replace the Button usage so it renders an <a> with an href (either wrap
<Button variant="link"> with <Link href="/your-path">...</Link> or use the UI
library's asChild pattern like <Button asChild> and place <Link
href="/your-path">Request demo</Link> inside); update the Button call in
sections/values.tsx (the Button component) to ensure the href is provided and
the element rendered is an anchor.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a68ed6dd-a3f5-4bc8-bf19-048c46560faf
⛔ Files ignored due to path filters (7)
app/favicon.icois excluded by!**/*.icopackage-lock.jsonis excluded by!**/package-lock.jsonpnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/images/bg_01.pngis excluded by!**/*.pngpublic/images/medbase-coming-soon.jpgis excluded by!**/*.jpgpublic/images/shapes.svgis excluded by!**/*.svgpublic/images/xera.svgis excluded by!**/*.svg
📒 Files selected for processing (52)
.gitignoreapp/(home)/page.tsxapp/globals.cssapp/layout.tsxapp/not-found.tsxapp/page.tsxcomponents.jsoncomponents/LightRays.csscomponents/LightRays.jsxcomponents/SplashCursor.jsxcomponents/desktop-nav.tsxcomponents/icons/github-icon.tsxcomponents/icons/linkedin-icon.tsxcomponents/icons/x-icon.tsxcomponents/logo.tsxcomponents/mobile-nav.tsxcomponents/mode-toggle.tsxcomponents/systaliko-ui/blocks/container-stagger.tsxcomponents/systaliko-ui/blocks/scroll-animation.tsxcomponents/systaliko-ui/cards/card-testimonial.tsxcomponents/systaliko-ui/cards/card.tsxcomponents/systaliko-ui/cards/cards-stack-rotated.tsxcomponents/systaliko-ui/cards/cards-stack.tsxcomponents/systaliko-ui/rating-stars.tsxcomponents/systaliko-ui/text/text-stagger-inview.tsxcomponents/systaliko-ui/utils/animation-variants.tsxcomponents/systaliko-ui/utils/transitions.tsxcomponents/ui/avatar.tsxcomponents/ui/badge.tsxcomponents/ui/button.tsxcomponents/ui/popover.tsxdata/constants.tsdata/data.tseslint.config.mjshooks/use-toast.tsnext.config.tspackage.jsonpostcss.config.mjsprettier.config.jsproviders/route-provider.tsxproviders/theme-provider.tsxsections/about.tsxsections/footer.tsxsections/header.tsxsections/hero.tsxsections/process.tsxsections/services.tsxsections/team.tsxsections/testimonials.tsxsections/values.tsxsections/work.tsxtsconfig.json
💤 Files with no reviewable changes (6)
- components/LightRays.css
- providers/route-provider.tsx
- app/not-found.tsx
- app/page.tsx
- components/LightRays.jsx
- components/SplashCursor.jsx
| NAV_LINKS.map((link) => ( | ||
| <li key={link.label}> | ||
| <Link className="text-foreground/50 hover:text-foreground font-medium" href={link.href}> | ||
| {link.label} | ||
| </Link> |
There was a problem hiding this comment.
Give the nav items distinct destinations.
data/constants.ts:1-18 currently gives every entry href: '/', so About/Services/Pricing all route to the homepage. That leaves both the desktop and mobile navs effectively broken until these point at real routes or section anchors.
Suggested fix
// data/constants.ts
export const NAV_LINKS = [
{ label: 'Home', href: '/' },
- { label: 'About', href: '/' },
- { label: 'Services', href: '/' },
- { label: 'Pricing', href: '/' },
+ { label: 'About', href: '#about' },
+ { label: 'Services', href: '#services' },
+ { label: 'Pricing', href: '#pricing' },
]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/desktop-nav.tsx` around lines 12 - 16, NAV_LINKS currently
contains identical hrefs ('/'), causing all nav items to route to the homepage
and breaking both desktop and mobile navigation; update the NAV_LINKS entries
(the array referenced by NAV_LINKS used in components/desktop-nav.tsx and mobile
nav) so each object has a distinct href that matches the intended destination
(e.g., '/about', '/services', '/pricing' or '#about', '#services', '#pricing'
for anchors), then ensure the Link usage in the desktop NavLink mapping
continues to use link.href and link.label as before so each <Link> points to the
new unique hrefs.
| <div className="flex gap-4 items-center"> | ||
| <Button variant={'outline'}> | ||
| Book a meeting | ||
| </Button> |
There was a problem hiding this comment.
Wire the “Book a meeting” CTA to a real action.
This renders as a primary interactive control but has no href or onClick, so users get a dead CTA. The same placeholder appears in components/mobile-nav.tsx, so both entry points should be hooked up before release.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/desktop-nav.tsx` around lines 22 - 25, The "Book a meeting" Button
currently renders without an action; update the Button element in
components/desktop-nav.tsx (and mirror the same change in
components/mobile-nav.tsx) to provide a real action: either add an href prop to
navigate to your scheduling page (e.g., "/book" or your external scheduler URL)
or attach an onClick handler that opens the scheduling URL in a new tab or
triggers the booking modal (call the existing openBookingModal or navigate
function if available). Ensure you use the same Button component API (e.g.,
onClick or href) so the CTA is keyboard-accessible and works the same in both
DesktopNav and MobileNav.
| <CardTestimonialContext.Provider | ||
| value={{ testimonialQuote, testimonialAuthor, testimonialRating }} | ||
| > | ||
| <Card className={cn(cardVariants({ variant, className }))} {...props}> |
There was a problem hiding this comment.
Bug: Double application of cardVariants.
Looking at the Card component in components/systaliko-ui/cards/card.tsx (lines 20-31), it already applies cardVariants({ variant, className }) internally. Here, you're pre-applying cardVariants to the className before passing it to Card, which will then apply cardVariants again, resulting in duplicated styles.
🐛 Fix by passing variant and className directly to Card
- <Card className={cn(cardVariants({ variant, className }))} {...props}>
+ <Card variant={variant} className={className} {...props}>📝 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.
| <Card className={cn(cardVariants({ variant, className }))} {...props}> | |
| <Card variant={variant} className={className} {...props}> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/systaliko-ui/cards/card-testimonial.tsx` at line 55, The Card in
card-testimonial.tsx is passing a precomputed className via cn(cardVariants({
variant, className })) which duplicates the styling because Card itself calls
cardVariants; instead, remove the call to cardVariants and pass the variant and
className props directly to Card (i.e., use <Card variant={variant}
className={className} ...>) so Card can apply cardVariants internally; update
the Card invocation in components/systaliko-ui/cards/card-testimonial.tsx
accordingly and remove cn(cardVariants(...)) usage.
| return ( | ||
| <div | ||
| ref={containerRef} | ||
| className={cn('stiky top-0 left-0', className)} |
There was a problem hiding this comment.
Typo: stiky should be sticky.
This CSS class typo will prevent the sticky positioning from working.
🐛 Fix typo
- className={cn('stiky top-0 left-0', className)}
+ className={cn('sticky top-0 left-0', className)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| className={cn('stiky top-0 left-0', className)} | |
| className={cn('sticky top-0 left-0', className)} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/systaliko-ui/cards/cards-stack-rotated.tsx` at line 74, The CSS
class string passed into cn in the CardsStackRotated component contains a typo:
change the 'stiky' token in the className expression (className={cn('stiky top-0
left-0', className)}) to 'sticky' so the sticky positioning works; update the
string used in that className call accordingly.
| ( | ||
| { | ||
| arrayLength, | ||
| index, | ||
| incrementY = 10, | ||
| incrementZ = 10, | ||
| incrementRotation = -index + 90, | ||
| className, | ||
| style, | ||
| ...props | ||
| }, |
There was a problem hiding this comment.
Critical: Default parameter references itself causing incorrect behavior.
The default value for incrementRotation is -index + 90, but index is being used in its own destructuring default assignment. In JavaScript, default parameter values are evaluated left-to-right, so index is available here, but this creates a confusing pattern where the default depends on the parameter itself. This means every card will have a different default rotation based on its index, which may be intentional but is unusual for a "default" value.
If intended, consider computing this inside the function body for clarity. If not intended, this should be a static default.
🔧 Suggested fix if rotation should be computed from index
(
{
arrayLength,
index,
incrementY = 10,
incrementZ = 10,
- incrementRotation = -index + 90,
+ incrementRotation,
className,
style,
...props
},
ref,
) => {
+ const rotation = incrementRotation ?? (-index + 90);
const { scrollYProgress } = useContainerScrollContext();
const start = index / (arrayLength + 1);
const end = (index + 1) / (arrayLength + 1);
const range = React.useMemo(() => [start, end], [start, end]);
const rotateRange = [range[0] - 1.5, range[1] / 1.5];
const y = useTransform(scrollYProgress, range, ['0%', '-180%']);
- const rotate = useTransform(scrollYProgress, rotateRange, [
- incrementRotation,
- 0,
- ]);
+ const rotate = useTransform(scrollYProgress, rotateRange, [rotation, 0]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/systaliko-ui/cards/cards-stack-rotated.tsx` around lines 88 - 98,
The destructured prop default incrementRotation = -index + 90 references index
during parameter defaulting which is confusing and fragile; move the derived
rotation calculation into the component body instead of using a parameter
default that depends on another parameter (or if you intended a static default,
set incrementRotation to a constant like 0). Locate the component that
destructures { arrayLength, index, incrementY, incrementZ, incrementRotation,
... } (the props block in cards-stack-rotated.tsx), remove the computed default
from the parameter list, and compute a local value (e.g., const computedRotation
= incrementRotation ?? (-index + 90)) inside the function before using it.
| { | ||
| label: 'About', | ||
| href: '/', | ||
| }, | ||
| { | ||
| label: 'Services', | ||
| href: '/', | ||
| }, | ||
| { | ||
| label: 'Pricing', | ||
| href: '/', |
There was a problem hiding this comment.
Navigation links all resolve to the same destination.
Line 8, Line 12, and Line 16 currently point to '/', so “About”, “Services”, and “Pricing” don’t navigate to their intended sections.
Suggested fix
export const NAV_LINKS = [
{
label: 'Home',
href: '/',
},
{
label: 'About',
- href: '/',
+ href: '/#about',
},
{
label: 'Services',
- href: '/',
+ href: '/#services',
},
{
label: 'Pricing',
- href: '/',
+ href: '/#pricing',
}
]📝 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.
| { | |
| label: 'About', | |
| href: '/', | |
| }, | |
| { | |
| label: 'Services', | |
| href: '/', | |
| }, | |
| { | |
| label: 'Pricing', | |
| href: '/', | |
| export const NAV_LINKS = [ | |
| { | |
| label: 'Home', | |
| href: '/', | |
| }, | |
| { | |
| label: 'About', | |
| href: '/#about', | |
| }, | |
| { | |
| label: 'Services', | |
| href: '/#services', | |
| }, | |
| { | |
| label: 'Pricing', | |
| href: '/#pricing', | |
| } | |
| ] |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@data/constants.ts` around lines 6 - 16, The navigation entries for labels
'About', 'Services', and 'Pricing' currently all use href: '/'—update each
object's href so they point to the correct destinations (e.g., '/about' or
'#about', '/services' or '#services', '/pricing' or '#pricing' depending on your
routing/anchor scheme) by locating the array of link objects (the ones with
label: 'About', label: 'Services', label: 'Pricing') in data/constants.ts and
replacing their href values with the intended paths.
hooks/use-toast.ts
Outdated
| React.useEffect(() => { | ||
| listeners.push(setState); | ||
| return () => { | ||
| const index = listeners.indexOf(setState); | ||
| if (index > -1) { | ||
| listeners.splice(index, 1); | ||
| } | ||
| }; | ||
| }, [state]); |
There was a problem hiding this comment.
Incorrect useEffect dependency causes unnecessary re-subscriptions.
The dependency array [state] causes the effect to re-run on every state change, removing and re-adding the listener repeatedly. Since the purpose is to subscribe once on mount and clean up on unmount, the dependency array should be empty.
🐛 Proposed fix
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
- }, [state]);
+ }, []);📝 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.
| React.useEffect(() => { | |
| listeners.push(setState); | |
| return () => { | |
| const index = listeners.indexOf(setState); | |
| if (index > -1) { | |
| listeners.splice(index, 1); | |
| } | |
| }; | |
| }, [state]); | |
| React.useEffect(() => { | |
| listeners.push(setState); | |
| return () => { | |
| const index = listeners.indexOf(setState); | |
| if (index > -1) { | |
| listeners.splice(index, 1); | |
| } | |
| }; | |
| }, []); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/use-toast.ts` around lines 189 - 197, The useEffect subscribing the
toast listener currently lists [state] as a dependency which causes repeated
unsubscribe/resubscribe on every state change; update the effect in the hook
where React.useEffect adds listeners.push(setState) and removes it on cleanup to
use an empty dependency array ([]) so the listener is added once on mount and
removed on unmount (keep references to listeners and setState as-is).
| <ul className="flex items-center gap-2"> | ||
| { | ||
| SOCIAL_LINKS.map((link) => ( | ||
| <li key={link.id}> | ||
| <Button variant={'ghost'} size={'icon'}> | ||
| <a href={link.href}> | ||
| <link.icon /> | ||
| </a> | ||
| </Button> | ||
| </li> | ||
| )) | ||
| } |
There was a problem hiding this comment.
Invalid HTML: <a> nested inside <Button>.
The Button component renders a <button> element by default. Nesting an <a> inside a <button> creates invalid HTML and causes accessibility issues. Use the asChild prop pattern to render the Button as an anchor.
🐛 Proposed fix
{
SOCIAL_LINKS.map((link) => (
<li key={link.id}>
- <Button variant={'ghost'} size={'icon'}>
- <a href={link.href}>
- <link.icon />
- </a>
+ <Button variant={'ghost'} size={'icon'} asChild>
+ <a href={link.href} target="_blank" rel="noopener noreferrer" aria-label={link.id.replace(/-/g, ' ')}>
+ <link.icon aria-hidden="true" />
+ </a>
</Button>
</li>
))
}Note: Also added target="_blank" and rel="noopener noreferrer" for external links, plus aria-label for accessibility since icon-only links need accessible names.
📝 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.
| <ul className="flex items-center gap-2"> | |
| { | |
| SOCIAL_LINKS.map((link) => ( | |
| <li key={link.id}> | |
| <Button variant={'ghost'} size={'icon'}> | |
| <a href={link.href}> | |
| <link.icon /> | |
| </a> | |
| </Button> | |
| </li> | |
| )) | |
| } | |
| <ul className="flex items-center gap-2"> | |
| { | |
| SOCIAL_LINKS.map((link) => ( | |
| <li key={link.id}> | |
| <Button variant={'ghost'} size={'icon'} asChild> | |
| <a href={link.href} target="_blank" rel="noopener noreferrer" aria-label={link.id.replace(/-/g, ' ')}> | |
| <link.icon aria-hidden="true" /> | |
| </a> | |
| </Button> | |
| </li> | |
| )) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sections/footer.tsx` around lines 53 - 64, The JSX currently nests an <a>
inside the Button created for each SOCIAL_LINKS entry (map over SOCIAL_LINKS
using link.id, link.href, link.icon), producing invalid HTML; change the Button
to render as an anchor using its asChild prop (so Button wraps the anchor
element or is rendered as an <a>) and move href to the rendered anchor (use
link.href), add target="_blank" and rel="noopener noreferrer" for external
links, and ensure each icon-only link has an accessible name by adding an
aria-label (e.g., from link.label or similar) when rendering the icon inside
Button/asChild.
| <a | ||
| target="_blank" | ||
| href="https://systaliko-ui.vercel.app/docs/templates/veo" | ||
| > |
There was a problem hiding this comment.
Harden external _blank link with rel attributes.
Line 19 opens a new tab without rel="noopener noreferrer", which leaves a tabnabbing/security gap.
🔧 Suggested fix
<a
target="_blank"
+ rel="noopener noreferrer"
href="https://systaliko-ui.vercel.app/docs/templates/veo"
>📝 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.
| <a | |
| target="_blank" | |
| href="https://systaliko-ui.vercel.app/docs/templates/veo" | |
| > | |
| <a | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| href="https://systaliko-ui.vercel.app/docs/templates/veo" | |
| > |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sections/hero.tsx` around lines 18 - 21, The external anchor that opens in a
new tab (the <a> element pointing to
"https://systaliko-ui.vercel.app/docs/templates/veo") is missing rel attributes;
update that anchor to include rel="noopener noreferrer" to prevent tabnabbing
and protect the opener, ensuring the element that currently uses target="_blank"
also includes rel="noopener noreferrer".
| arrayLength={TESTIMONIALS.length} | ||
| key={testimonial.id} | ||
| index={index + 2} | ||
| > |
There was a problem hiding this comment.
Card transform index offset likely breaks stack math.
Using index={index + 2} shifts the transform/z-index calculations; the last card can end up with negative z-index and out-of-range scroll windows. This should use the actual zero-based index unless the stack math is adjusted everywhere.
🔧 Suggested fix
<CardTransformed
arrayLength={TESTIMONIALS.length}
key={testimonial.id}
- index={index + 2}
+ index={index}
>📝 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.
| arrayLength={TESTIMONIALS.length} | |
| key={testimonial.id} | |
| index={index + 2} | |
| > | |
| arrayLength={TESTIMONIALS.length} | |
| key={testimonial.id} | |
| index={index} | |
| > |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sections/testimonials.tsx` around lines 63 - 66, The card is being passed a
shifted index (index={index + 2}) which breaks the stacking/transform math and
can produce negative z-index or invalid scroll ranges; change the prop to use
the actual zero-based index (index={index}) wherever the Card (or the component
receiving index) relies on TESTIMONIALS.length for stacking calculations (look
for usages of index in the card transform/z-index/scroll math) and update any
dependent arithmetic if you intentionally need an offset so all
z-index/transform calculations remain consistent with TESTIMONIALS.length and
zero-based indexing.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/globals.css`:
- Around line 38-40: Stylelint is failing on the custom property font values and
an extra blank line before the body declaration: update the font token values so
generic family keywords are lowercase (e.g., keep "Inter" / "Georgia" /
"JetBrains Mono" as-is for the named fonts but change the generic keywords to
"sans-serif", "serif", "monospace" in the --font-sans, --font-serif, and
--font-mono declarations) and remove the stray empty line before the body
selector (or adjust to match your project's declaration-empty-line-before rule)
so the body declaration spacing passes linting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 97971b22-468a-4a34-b5b5-0479734366c4
📒 Files selected for processing (5)
app/(home)/page.tsxapp/globals.cssapp/layout.tsxpackage.jsontsconfig.json
✅ Files skipped from review due to trivial changes (3)
- app/(home)/page.tsx
- package.json
- tsconfig.json
| --font-sans: Inter, system-ui, sans-serif; | ||
| --font-serif: Georgia, serif; | ||
| --font-mono: JetBrains Mono, monospace; |
There was a problem hiding this comment.
Fix current Stylelint errors in font-token declarations and body declaration spacing.
Stylelint is currently flagging these lines (value-keyword-case and declaration-empty-line-before), which can block lint-gated merges.
Proposed fix
- --font-sans: Inter, system-ui, sans-serif;
- --font-serif: Georgia, serif;
- --font-mono: JetBrains Mono, monospace;
+ --font-sans: "Inter", system-ui, sans-serif;
+ --font-serif: "Georgia", serif;
+ --font-mono: "JetBrains Mono", monospace;
...
- --font-sans: Inter, system-ui, sans-serif;
- --font-serif: Georgia, serif;
- --font-mono: JetBrains Mono, monospace;
+ --font-sans: "Inter", system-ui, sans-serif;
+ --font-serif: "Georgia", serif;
+ --font-mono: "JetBrains Mono", monospace;
...
body {
`@apply` bg-background text-foreground;
+
letter-spacing: var(--tracking-normal);
}Also applies to: 93-95, 179-179
🧰 Tools
🪛 Stylelint (17.5.0)
[error] 38-38: Expected "Inter" to be "inter" (value-keyword-case)
(value-keyword-case)
[error] 39-39: Expected "Georgia" to be "georgia" (value-keyword-case)
(value-keyword-case)
[error] 40-40: Expected "JetBrains" to be "jetbrains" (value-keyword-case)
(value-keyword-case)
[error] 40-40: Expected "Mono" to be "mono" (value-keyword-case)
(value-keyword-case)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/globals.css` around lines 38 - 40, Stylelint is failing on the custom
property font values and an extra blank line before the body declaration: update
the font token values so generic family keywords are lowercase (e.g., keep
"Inter" / "Georgia" / "JetBrains Mono" as-is for the named fonts but change the
generic keywords to "sans-serif", "serif", "monospace" in the --font-sans,
--font-serif, and --font-mono declarations) and remove the stray empty line
before the body selector (or adjust to match your project's
declaration-empty-line-before rule) so the body declaration spacing passes
linting.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
components/logo.tsx (1)
9-16: Inconsistent indentation in SVG attributes.The
viewBoxandfillattributes (lines 10-11) lack indentation while subsequent attributes are properly indented. Consider aligning all attributes for consistency.✨ Suggested formatting fix
<svg -viewBox="0 0 127 133" -fill="currentColor" + viewBox="0 0 127 133" + fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-label='Veo Logo' className={cn('w-32', className)} {...props} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/logo.tsx` around lines 9 - 16, The SVG element's attributes are misaligned—move the `viewBox` and `fill` attributes to match the indentation style used for the other attributes on the `<svg>` element (the same indentation as `xmlns`, `aria-label`, `className={cn('w-32', className)}`, and `{...props}`) so all attributes are consistently indented; locate the `<svg` tag in the Logo component and adjust `viewBox="0 0 127 133"` and `fill="currentColor"` to the same indent level as the remaining attributes.sections/team.tsx (1)
28-52: Consider removing commented-out code.Leaving large blocks of commented-out data in the codebase creates noise and can become stale over time. If these team members might be re-added, consider tracking this in a separate issue or using version control history to recover them when needed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sections/team.tsx` around lines 28 - 52, Remove the large commented-out team member objects in sections/team.tsx (the block of avatar/name/role JSON-like objects) to avoid clutter; if you want to preserve them, move the data into a separate seed/test file or create a GitHub issue referencing these entries instead of leaving them commented in the Team component/file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@components/logo.tsx`:
- Around line 9-16: The SVG element's attributes are misaligned—move the
`viewBox` and `fill` attributes to match the indentation style used for the
other attributes on the `<svg>` element (the same indentation as `xmlns`,
`aria-label`, `className={cn('w-32', className)}`, and `{...props}`) so all
attributes are consistently indented; locate the `<svg` tag in the Logo
component and adjust `viewBox="0 0 127 133"` and `fill="currentColor"` to the
same indent level as the remaining attributes.
In `@sections/team.tsx`:
- Around line 28-52: Remove the large commented-out team member objects in
sections/team.tsx (the block of avatar/name/role JSON-like objects) to avoid
clutter; if you want to preserve them, move the data into a separate seed/test
file or create a GitHub issue referencing these entries instead of leaving them
commented in the Team component/file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 10a73c10-5a2e-4c96-b94f-8ae92a91d7e3
📒 Files selected for processing (13)
components/logo.tsxcomponents/systaliko-ui/blocks/scroll-animation.tsxcomponents/systaliko-ui/cards/card-testimonial.tsxcomponents/systaliko-ui/cards/card.tsxcomponents/systaliko-ui/cards/cards-stack-rotated.tsxcomponents/systaliko-ui/cards/cards-stack.tsxcomponents/systaliko-ui/rating-stars.tsxcomponents/ui/avatar.tsxcomponents/ui/badge.tsxcomponents/ui/button.tsxcomponents/ui/popover.tsxhooks/use-toast.tssections/team.tsx
💤 Files with no reviewable changes (1)
- hooks/use-toast.ts
✅ Files skipped from review due to trivial changes (9)
- components/systaliko-ui/rating-stars.tsx
- components/systaliko-ui/cards/cards-stack-rotated.tsx
- components/systaliko-ui/blocks/scroll-animation.tsx
- components/systaliko-ui/cards/card-testimonial.tsx
- components/ui/avatar.tsx
- components/ui/badge.tsx
- components/systaliko-ui/cards/cards-stack.tsx
- components/systaliko-ui/cards/card.tsx
- components/ui/popover.tsx
Summary by CodeRabbit
Style
New Features
Chores
Content