Upgraded Hero Section layout UI (#45) - #52
Conversation
|
Skipping CodeAnt AI review — this PR is a back-merge between long-lived branches ( If you want to analyze this anyway (e.g. you resolved conflicts with new logic), comment |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds an animated landing-page hero with particle, orbit, and globe visuals, telemetry cards, a shimmer CTA, updated navigation styling, Shadcn configuration, supporting dependencies, and theme variables. ChangesLanding Hero Experience
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant LandingPage
participant Hero
participant Particles
participant OrbitSatellites
participant Globe
LandingPage->>Hero: Render landing hero
Hero->>Particles: Render particle background
Hero->>OrbitSatellites: Render orbit visuals
Hero->>Globe: Render interactive globe
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
| <OrbitSatellites /> | ||
| <div className="absolute inset-0 flex items-center justify-center"> | ||
| <Globe className="h-max-[480px] h-max-[480px]" /> | ||
| </div> |
There was a problem hiding this comment.
Wrong and duplicated Tailwind class.
h-max-[480px] is not a valid Tailwind utility; the correct class for max-height is max-h-[480px]. The duplicate is also removed.
| <OrbitSatellites /> | |
| <div className="absolute inset-0 flex items-center justify-center"> | |
| <Globe className="h-max-[480px] h-max-[480px]" /> | |
| </div> | |
| <OrbitSatellites /> | |
| <div className="absolute inset-0 flex items-center justify-center"> | |
| <Globe className="max-h-[480px]" /> | |
| </div> |
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/Hero.tsx
Line: 45-48
Comment:
Wrong and duplicated Tailwind class. `h-max-[480px]` is not a valid Tailwind utility; the correct class for `max-height` is `max-h-[480px]`. The duplicate is also removed.
```suggestion
<OrbitSatellites />
<div className="absolute inset-0 flex items-center justify-center">
<Globe className="max-h-[480px]" />
</div>
```
How can I resolve this? If you propose a fix, please make it concise.| variants={fadeUp} | ||
| className="mt-8 flex flex-wrap items-center gap-4" | ||
| > | ||
| <ShimmerButton shimmerColor="#4CDfF0"> |
There was a problem hiding this comment.
Typo in the shimmer color hex value.
#4CDfF0 resolves to rgb(76, 223, 240) (DF = 223) whereas the brand color used consistently throughout the rest of the component is #4CD6F0 (D6 = 214). The lowercase f in Df was likely meant to be 6.
| <ShimmerButton shimmerColor="#4CDfF0"> | |
| <ShimmerButton shimmerColor="#4CD6F0"> |
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/Hero.tsx
Line: 96
Comment:
Typo in the shimmer color hex value. `#4CDfF0` resolves to `rgb(76, 223, 240)` (`DF` = 223) whereas the brand color used consistently throughout the rest of the component is `#4CD6F0` (`D6` = 214). The lowercase `f` in `Df` was likely meant to be `6`.
```suggestion
<ShimmerButton shimmerColor="#4CD6F0">
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (2)
frontend/src/components/ui/globe.tsx (1)
86-91: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy liftApply one consistent reduced-motion policy across the animated primitives.
frontend/src/components/ui/globe.tsx#L86-L91: stop incrementingphiRefwhen reduced motion is requested.frontend/src/components/ui/particles.tsx#L255-L303: avoid scheduling the continuous animation loop, or render a static frame.frontend/src/components/ui/shimmer-button.tsx#L60-L62: disableanimate-shimmer-slideandanimate-spin-aroundthrough a reduced-motion variant.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/ui/globe.tsx` around lines 86 - 91, Apply one consistent reduced-motion policy across all three sites: in frontend/src/components/ui/globe.tsx lines 86-91, prevent the onRender logic from incrementing phiRef when reduced motion is requested; in frontend/src/components/ui/particles.tsx lines 255-303, avoid scheduling the continuous animation loop or render only a static frame; and in frontend/src/components/ui/shimmer-button.tsx lines 60-62, add the reduced-motion variant that disables animate-shimmer-slide and animate-spin-around.frontend/src/components/Hero.tsx (1)
28-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHero color palette diverges from the page's documented design tokens.
LandingPage.tsxdocuments tokens#060A14/#0C1220/#1B2436/#E7EBF3/#8892A6/#4FE0C8/#FFB020, used consistently in NavBar/HowItWorks/Reliability/Footer. This Hero introduces an entirely separate palette (#0B111F,#05070C,#8793AC,#4CD6F0,#9D7BFF,#A6B0C4,#EAF1FC,#6B7690,#C7CEDD), creating a visual seam between the hero and the rest of the page. Confirm this divergence is an intentional design choice for the hero, or align it with the established tokens/document the new tokens alongside the existing ones.Also applies to: 60-131
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/Hero.tsx` at line 28, Update the Hero component’s colors to use the documented design tokens from LandingPage.tsx, replacing its separate background, text, muted, accent, and gradient colors across the Hero section and its content. Preserve the existing layout and styling while reusing the established palette consistently with NavBar, HowItWorks, Reliability, and Footer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/Hero.tsx`:
- Line 96: Update the shimmerColor value on the ShimmerButton to use the
established brand cyan `#4CD6F0`, matching the color used elsewhere in Hero.tsx.
- Around line 17-24: Update the Hero component’s returned JSX to be wrapped in
MotionConfig with reducedMotion set to "user", so all nested motion.* entrance
animations respect the user’s prefers-reduced-motion setting while preserving
existing animation definitions and behavior for users without that preference.
- Line 47: Update the Globe component’s className to use the valid max-height
utility max-h-[480px] instead of the duplicated invalid h-max-[480px] classes.
- Around line 96-101: Wire the “Continue to dashboard” CTA in Hero to navigate
to the dashboard when activated. Restore the Hero prop/callback contract used by
LandingPage.tsx, or use the existing routing mechanism such as useNavigate, and
pass the resulting handler or destination to ShimmerButton while preserving its
current label and styling.
In `@frontend/src/components/ui/globe.tsx`:
- Around line 94-98: Store the timeout handle created in the globe setup effect,
then clear it in the cleanup function before destroying the globe and removing
the resize listener. Update the opacity callback cleanup around globe
initialization so it cannot access canvasRef.current after unmount.
- Around line 64-69: Update updateMovement so that after calculating and
applying the delta from pointerInteracting.current, it advances
pointerInteracting.current to the current clientX. Preserve the existing
movement tracking and rotation calculation while ensuring repeated events at the
same coordinate produce no additional rotation.
In `@frontend/src/components/ui/particles.tsx`:
- Around line 141-144: Update the particle initialization and resize flow around
initCanvas, resizeCanvas, and drawParticles so resizing does not create one
quantity of particles and then append another during the same initialization.
Ensure each canvas setup produces exactly the configured quantity, including the
Hero quantity={220}, while preserving the existing resize and drawing behavior.
- Around line 15-34: Update MousePosition to use a ref for pointer coordinates
instead of React state and an effect. Remove the useState/useEffect listener
bridge, and have the mousemove handler write clientX and clientY directly to the
mouse.current ref while preserving the existing coordinate shape.
In `@frontend/src/components/ui/shimmer-button.tsx`:
- Around line 20-50: Update the forwarded props handling in the shimmer button
component so the caller-provided style is merged with the inline shimmer CSS
variables rather than replacing them. Preserve all existing shimmer variable
values while allowing consumer styles, and ensure the merged style is applied to
the button.
In `@frontend/src/pages/LandingPage.tsx`:
- Around line 159-162: Remove the redundant utility classes from the Dashboard
button in the LandingPage JSX: retain only one background source between
bg-primary-container and bg-[`#4FE0C8`], and only one transition definition
between transition-ui and transition-colors duration-150. Preserve all other
button styling and behavior.
In `@frontend/src/styles/index.css`:
- Around line 88-98: Ensure the default landing-page theme remains dark by
updating the root theme configuration associated with body’s bg-background and
text-foreground utilities: either set the :root tokens to dark defaults or apply
the .dark class during bootstrap before first paint. Preserve existing theme
switching behavior.
---
Nitpick comments:
In `@frontend/src/components/Hero.tsx`:
- Line 28: Update the Hero component’s colors to use the documented design
tokens from LandingPage.tsx, replacing its separate background, text, muted,
accent, and gradient colors across the Hero section and its content. Preserve
the existing layout and styling while reusing the established palette
consistently with NavBar, HowItWorks, Reliability, and Footer.
In `@frontend/src/components/ui/globe.tsx`:
- Around line 86-91: Apply one consistent reduced-motion policy across all three
sites: in frontend/src/components/ui/globe.tsx lines 86-91, prevent the onRender
logic from incrementing phiRef when reduced motion is requested; in
frontend/src/components/ui/particles.tsx lines 255-303, avoid scheduling the
continuous animation loop or render only a static frame; and in
frontend/src/components/ui/shimmer-button.tsx lines 60-62, add the
reduced-motion variant that disables animate-shimmer-slide and
animate-spin-around.
🪄 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 Plus
Run ID: 7b54e9e3-10f3-4625-90a8-4061b107a8e8
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
frontend/components.jsonfrontend/package.jsonfrontend/src/components/Hero.tsxfrontend/src/components/ui/OrbitSatellites.tsxfrontend/src/components/ui/globe.tsxfrontend/src/components/ui/particles.tsxfrontend/src/components/ui/shimmer-button.tsxfrontend/src/lib/utils.tsfrontend/src/pages/LandingPage.tsxfrontend/src/styles/index.css
| const fadeUp = { | ||
| hidden: { opacity: 0, y: 18 }, | ||
| show: (delay: number) => ({ | ||
| opacity: 1, | ||
| y: 0, | ||
| transition: { duration: 0.7, delay, ease: [0.16, 1, 0.3, 1] as const }, | ||
| }), | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reduced-motion preference no longer respected.
All motion.* entrance animations run unconditionally. Per the diff details, LandingPage.tsx used to pass prefersReducedMotion into the hero to gate animation; the new Hero() accepts no props and has no internal check (no useReducedMotion/MotionConfig), so users with prefers-reduced-motion: reduce now get motion they previously didn't.
♿ Proposed fix
+import { useReducedMotion } from "framer-motion";
+
export function Hero() {
+ const shouldReduceMotion = useReducedMotion();
return (
<section ...>
...
<motion.div
initial="hidden"
- animate="show"
+ animate={shouldReduceMotion ? "show" : "show"}Simpler: wrap the returned JSX in <MotionConfig reducedMotion="user">…</MotionConfig> so all child motion.* elements automatically respect the OS setting without per-element changes.
Also applies to: 55-132
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Hero.tsx` around lines 17 - 24, Update the Hero
component’s returned JSX to be wrapped in MotionConfig with reducedMotion set to
"user", so all nested motion.* entrance animations respect the user’s
prefers-reduced-motion setting while preserving existing animation definitions
and behavior for users without that preference.
| > | ||
| <OrbitSatellites /> | ||
| <div className="absolute inset-0 flex items-center justify-center"> | ||
| <Globe className="h-max-[480px] h-max-[480px]" /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Duplicate/invalid Tailwind class on Globe.
h-max is a real Tailwind keyword utility (height: max-content); appending -[480px] to it produces an unrecognized token rather than a max-height constraint, and the class is duplicated. Likely meant max-h-[480px].
🐛 Proposed fix
- <Globe className="h-max-[480px] h-max-[480px]" />
+ <Globe className="max-h-[480px]" />📝 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.
| <Globe className="h-max-[480px] h-max-[480px]" /> | |
| <Globe className="max-h-[480px]" /> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Hero.tsx` at line 47, Update the Globe component’s
className to use the valid max-height utility max-h-[480px] instead of the
duplicated invalid h-max-[480px] classes.
| variants={fadeUp} | ||
| className="mt-8 flex flex-wrap items-center gap-4" | ||
| > | ||
| <ShimmerButton shimmerColor="#4CDfF0"> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Likely color typo: #4CDfF0 vs. brand #4CD6F0.
The brand cyan used elsewhere in this file (lines 62, 71) is #4CD6F0; the shimmer color here is #4CDfF0, a one-character transposition.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Hero.tsx` at line 96, Update the shimmerColor value
on the ShimmerButton to use the established brand cyan `#4CD6F0`, matching the
color used elsewhere in Hero.tsx.
| <ShimmerButton shimmerColor="#4CDfF0"> | ||
| <span className="inline-flex items-center gap-2"> | ||
| Continue to dashboard | ||
| <ArrowRight size={15} /> | ||
| </span> | ||
| </ShimmerButton> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
CTA button is a dead click — no navigation wired.
ShimmerButton forwards standard button props but none are supplied here (no onClick/href). Per the diff details, LandingPage.tsx previously passed onLaunchDashboard into the hero; Hero() now takes zero props and never imports useNavigate, so "Continue to dashboard" does nothing when clicked. This is the primary conversion CTA on the landing page.
🐛 Proposed fix
+import { useNavigate } from "react-router-dom";
+
export function Hero() {
+ const navigate = useNavigate();
return (
<section ...>
...
- <ShimmerButton shimmerColor="`#4CDfF0`">
+ <ShimmerButton shimmerColor="`#4CD6F0`" onClick={() => navigate("/dashboard")}>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/Hero.tsx` around lines 96 - 101, Wire the “Continue
to dashboard” CTA in Hero to navigate to the dashboard when activated. Restore
the Hero prop/callback contract used by LandingPage.tsx, or use the existing
routing mechanism such as useNavigate, and pass the resulting handler or
destination to ShimmerButton while preserving its current label and styling.
| const updateMovement = (clientX: number) => { | ||
| if (pointerInteracting.current !== null) { | ||
| const delta = clientX - pointerInteracting.current | ||
| pointerInteractionMovement.current = delta | ||
| r.set(r.get() + delta / MOVEMENT_DAMPING) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Update the drag origin after consuming each delta.
Every mouse event currently adds the full displacement from the original pointer-down position. Holding the pointer at the same displaced coordinate therefore keeps rotating the globe.
Proposed fix
const updateMovement = (clientX: number) => {
if (pointerInteracting.current !== null) {
const delta = clientX - pointerInteracting.current
- pointerInteractionMovement.current = delta
+ pointerInteracting.current = clientX
r.set(r.get() + delta / MOVEMENT_DAMPING)
}
}📝 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.
| const updateMovement = (clientX: number) => { | |
| if (pointerInteracting.current !== null) { | |
| const delta = clientX - pointerInteracting.current | |
| pointerInteractionMovement.current = delta | |
| r.set(r.get() + delta / MOVEMENT_DAMPING) | |
| } | |
| const updateMovement = (clientX: number) => { | |
| if (pointerInteracting.current !== null) { | |
| const delta = clientX - pointerInteracting.current | |
| pointerInteracting.current = clientX | |
| r.set(r.get() + delta / MOVEMENT_DAMPING) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/globe.tsx` around lines 64 - 69, Update
updateMovement so that after calculating and applying the delta from
pointerInteracting.current, it advances pointerInteracting.current to the
current clientX. Preserve the existing movement tracking and rotation
calculation while ensuring repeated events at the same coordinate produce no
additional rotation.
| function MousePosition(): MousePosition { | ||
| const [mousePosition, setMousePosition] = useState<MousePosition>({ | ||
| x: 0, | ||
| y: 0, | ||
| }) | ||
|
|
||
| useEffect(() => { | ||
| const handleMouseMove = (event: MouseEvent) => { | ||
| setMousePosition({ x: event.clientX, y: event.clientY }) | ||
| } | ||
|
|
||
| window.addEventListener("mousemove", handleMouseMove) | ||
|
|
||
| return () => { | ||
| window.removeEventListener("mousemove", handleMouseMove) | ||
| } | ||
| }, []) | ||
|
|
||
| return mousePosition | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
# Map the component and related references
git ls-files frontend/src/components/ui | sed -n '1,120p'
printf '\n--- particles.tsx outline ---\n'
ast-grep outline frontend/src/components/ui/particles.tsx --view expanded || true
printf '\n--- particles.tsx with line numbers ---\n'
cat -n frontend/src/components/ui/particles.tsx | sed -n '1,260p'
printf '\n--- search for MousePosition usage and related canvas animation hooks ---\n'
rg -n "MousePosition|mousePosition|mousemove|requestAnimationFrame|prefers-reduced-motion|resizeCanvas|drawParticles|quantity" frontend/src/components/ui frontend/src -g '*.tsx' -g '*.ts'Repository: 7-Blocks/Kepler
Length of output: 13580
🏁 Script executed:
set -euo pipefail
# Read the target file and nearby references only
cat -n frontend/src/components/ui/particles.tsx | sed -n '1,260p'
printf '\n--- search references ---\n'
rg -n "MousePosition|mousePosition|mousemove|requestAnimationFrame|prefers-reduced-motion|resizeCanvas|drawParticles|quantity" frontend/src/components/ui frontend/src -g '*.tsx' -g '*.ts'Repository: 7-Blocks/Kepler
Length of output: 12807
🏁 Script executed:
set -euo pipefail
cat -n frontend/src/components/ui/particles.tsx | sed -n '1,220p'Repository: 7-Blocks/Kepler
Length of output: 7413
🏁 Script executed:
set -euo pipefail
cat -n frontend/src/components/ui/particles.tsx | sed -n '255,360p'Repository: 7-Blocks/Kepler
Length of output: 2748
Move pointer coordinates into a ref. mousemove currently drives a React state update and re-render on every pointer event, but the value only feeds mouse.current. Update the ref directly in the listener and drop the useState/useEffect bridge.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/particles.tsx` around lines 15 - 34, Update
MousePosition to use a ref for pointer coordinates instead of React state and an
effect. Remove the useState/useEffect listener bridge, and have the mousemove
handler write clientX and clientY directly to the mouse.current ref while
preserving the existing coordinate shape.
| const initCanvas = () => { | ||
| resizeCanvas() | ||
| drawParticles() | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Avoid creating every particle twice.
resizeCanvas() clears and creates quantity circles, then drawParticles() immediately appends another quantity. The Hero’s quantity={220} therefore renders 440 particles.
Proposed fix
const initCanvas = () => {
resizeCanvas()
- drawParticles()
}Also applies to: 160-177, 234-241
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/particles.tsx` around lines 141 - 144, Update the
particle initialization and resize flow around initCanvas, resizeCanvas, and
drawParticles so resizing does not create one quantity of particles and then
append another during the same initialization. Ensure each canvas setup produces
exactly the configured quantity, including the Hero quantity={220}, while
preserving the existing resize and drawing behavior.
| { | ||
| shimmerColor = "#ffffff", | ||
| shimmerSize = "0.05em", | ||
| shimmerDuration = "3s", | ||
| borderRadius = "100px", | ||
| background = "rgba(0, 0, 0, 1)", | ||
| className, | ||
| children, | ||
| ...props | ||
| }, | ||
| ref | ||
| ) => { | ||
| return ( | ||
| <button | ||
| style={ | ||
| { | ||
| "--spread": "90deg", | ||
| "--shimmer-color": shimmerColor, | ||
| "--radius": borderRadius, | ||
| "--speed": shimmerDuration, | ||
| "--cut": shimmerSize, | ||
| "--bg": background, | ||
| } as CSSProperties | ||
| } | ||
| className={cn( | ||
| "group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] border border-white/10 px-6 py-3 whitespace-nowrap text-white [background:var(--bg)]", | ||
| "transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px", | ||
| className | ||
| )} | ||
| ref={ref} | ||
| {...props} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,220p' frontend/src/components/ui/shimmer-button.tsxRepository: 7-Blocks/Kepler
Length of output: 2999
Merge style with the shimmer CSS variables.
A caller-provided style prop currently overrides the inline CSS variables because {...props} comes after style, which breaks the shimmer when consumers pass styles.
Proposed fix
background = "rgba(0, 0, 0, 1)",
className,
children,
+ style,
...props
@@
"--cut": shimmerSize,
"--bg": background,
+ ...style,
} as CSSProperties📝 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.
| { | |
| shimmerColor = "#ffffff", | |
| shimmerSize = "0.05em", | |
| shimmerDuration = "3s", | |
| borderRadius = "100px", | |
| background = "rgba(0, 0, 0, 1)", | |
| className, | |
| children, | |
| ...props | |
| }, | |
| ref | |
| ) => { | |
| return ( | |
| <button | |
| style={ | |
| { | |
| "--spread": "90deg", | |
| "--shimmer-color": shimmerColor, | |
| "--radius": borderRadius, | |
| "--speed": shimmerDuration, | |
| "--cut": shimmerSize, | |
| "--bg": background, | |
| } as CSSProperties | |
| } | |
| className={cn( | |
| "group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] border border-white/10 px-6 py-3 whitespace-nowrap text-white [background:var(--bg)]", | |
| "transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px", | |
| className | |
| )} | |
| ref={ref} | |
| {...props} | |
| { | |
| shimmerColor = "`#ffffff`", | |
| shimmerSize = "0.05em", | |
| shimmerDuration = "3s", | |
| borderRadius = "100px", | |
| background = "rgba(0, 0, 0, 1)", | |
| className, | |
| children, | |
| style, | |
| ...props | |
| }, | |
| ref | |
| ) => { | |
| return ( | |
| <button | |
| style={ | |
| { | |
| "--spread": "90deg", | |
| "--shimmer-color": shimmerColor, | |
| "--radius": borderRadius, | |
| "--speed": shimmerDuration, | |
| "--cut": shimmerSize, | |
| "--bg": background, | |
| ...style, | |
| } as CSSProperties | |
| } | |
| className={cn( | |
| "group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] border border-white/10 px-6 py-3 whitespace-nowrap text-white [background:var(--bg)]", | |
| "transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px", | |
| className | |
| )} | |
| ref={ref} | |
| {...props} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/shimmer-button.tsx` around lines 20 - 50, Update
the forwarded props handling in the shimmer button component so the
caller-provided style is merged with the inline shimmer CSS variables rather
than replacing them. Preserve all existing shimmer variable values while
allowing consumer styles, and ensure the merged style is applied to the button.
| body { | ||
| background-color: #000000; | ||
| color: #dfe2eb; | ||
| overflow: hidden; | ||
| @apply bg-background text-foreground; | ||
| } | ||
| * { | ||
| @apply border-border outline-ring/50; | ||
| } | ||
| html { | ||
| @apply font-sans; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Preserve the dark default. body now gets bg-background text-foreground, but :root still sets a light --background, and nothing in the app bootstrap installs .dark before first paint. That makes the landing page render light unless the class is added elsewhere.
Either make the root tokens dark by default or apply .dark before these utilities run.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/styles/index.css` around lines 88 - 98, Ensure the default
landing-page theme remains dark by updating the root theme configuration
associated with body’s bg-background and text-foreground utilities: either set
the :root tokens to dark defaults or apply the .dark class during bootstrap
before first paint. Preserve existing theme switching behavior.
| setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0) | ||
| return () => { | ||
| globe.destroy() | ||
| window.removeEventListener("resize", onResize) | ||
| } |
There was a problem hiding this comment.
Unguarded
setTimeout causes TypeError on unmount
The setTimeout callback is not cleared in the effect cleanup. In React Strict Mode — which double-invokes effects — the cleanup from the first invocation nullifies canvasRef.current, and when the zero-delay timeout fires, canvasRef.current!.style.opacity throws a TypeError: Cannot read properties of null. The same failure occurs in production if the user navigates away before the callback fires. Store the timeout ID and clear it in the cleanup alongside globe.destroy().
| setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0) | |
| return () => { | |
| globe.destroy() | |
| window.removeEventListener("resize", onResize) | |
| } | |
| const fadeInId = setTimeout(() => { | |
| if (canvasRef.current) canvasRef.current.style.opacity = "1" | |
| }, 0) | |
| return () => { | |
| clearTimeout(fadeInId) | |
| globe.destroy() | |
| window.removeEventListener("resize", onResize) | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/ui/globe.tsx
Line: 94-98
Comment:
**Unguarded `setTimeout` causes TypeError on unmount**
The `setTimeout` callback is not cleared in the effect cleanup. In React Strict Mode — which double-invokes effects — the cleanup from the first invocation nullifies `canvasRef.current`, and when the zero-delay timeout fires, `canvasRef.current!.style.opacity` throws a `TypeError: Cannot read properties of null`. The same failure occurs in production if the user navigates away before the callback fires. Store the timeout ID and clear it in the cleanup alongside `globe.destroy()`.
```suggestion
const fadeInId = setTimeout(() => {
if (canvasRef.current) canvasRef.current.style.opacity = "1"
}, 0)
return () => {
clearTimeout(fadeInId)
globe.destroy()
window.removeEventListener("resize", onResize)
}
```
How can I resolve this? If you propose a fix, please make it concise.
Description
Related Issue
#45
Checklist
Screenshots / Screen Recordings
Screen.Recording.2026-07-16.at.5.59.06.PM.mp4
Breaking Changes
No Breaking Changes
ECSoC26 Submission
ECSoC26-L1– BeginnerECSoC26-L2– IntermediateECSoC26-L3– AdvancedSummary by CodeRabbit
cnutility for cleaner Tailwind styling.Greptile Summary
This PR replaces the hero section with an animated layout featuring an interactive COBE globe, orbiting satellite icons, a canvas particle field, shimmer button CTA, and telemetry stat cards, while installing shadcn/ui, MagicUI, and supporting dependencies.
Globe,OrbitSatellites,Particles,ShimmerButton) inside a full-viewport section with framer-motion fade-up animations.setTimeoutwith noclearTimeoutin its cleanup; this throws aTypeErrorwhen the component unmounts before the 0 ms callback fires, which React Strict Mode reliably triggers.Array.spliceinside aforEachloop in the animation tick, causing the particle that shifts into the vacated slot to be skipped in the current frame.Confidence Score: 3/5
The globe component will throw a TypeError in React Strict Mode on every development render cycle due to an unguarded setTimeout; the primary hero CTA button still has no navigation handler after the previous review cycle.
The globe's unguarded setTimeout fires after the effect cleanup nullifies the canvas ref, throwing unconditionally in React Strict Mode. Combined with the still-unresolved ShimmerButton onClick wiring from the prior review, the hero section is functionally incomplete on two counts before this can ship.
frontend/src/components/ui/globe.tsx (setTimeout cleanup) and frontend/src/components/Hero.tsx / frontend/src/pages/LandingPage.tsx (CTA navigation wiring).
Important Files Changed
animateskips one particle per out-of-bounds event, causing a minor visual one-frame delay.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD LP[LandingPage] -->|renders| NB[NavBar\nonLaunchDashboard=handleLaunch] LP -->|renders| H[Hero] LP -->|renders| HIW[HowItWorks] LP -->|renders| R[Reliability] LP -->|renders| F[Footer] H -->|composes| P[Particles\ncanvas animation] H -->|composes| OS[OrbitSatellites\nCSS keyframes] H -->|composes| G[Globe\nCOBE WebGL] H -->|composes| SB[ShimmerButton\nno onClick] G -->|setTimeout\nno cleanup| FO[canvas opacity 1] P -->|forEach splice\nskips particle| AN[animate loop] NB -->|onClick| NAV[navigate /dashboard] SB -.-x|missing| NAV%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD LP[LandingPage] -->|renders| NB[NavBar\nonLaunchDashboard=handleLaunch] LP -->|renders| H[Hero] LP -->|renders| HIW[HowItWorks] LP -->|renders| R[Reliability] LP -->|renders| F[Footer] H -->|composes| P[Particles\ncanvas animation] H -->|composes| OS[OrbitSatellites\nCSS keyframes] H -->|composes| G[Globe\nCOBE WebGL] H -->|composes| SB[ShimmerButton\nno onClick] G -->|setTimeout\nno cleanup| FO[canvas opacity 1] P -->|forEach splice\nskips particle| AN[animate loop] NB -->|onClick| NAV[navigate /dashboard] SB -.-x|missing| NAVComments Outside Diff (2)
frontend/src/pages/LandingPage.tsx, line 977 (link)The new
<Hero />component removed theonLaunchDashboardprop that wired the "Continue to dashboard"ShimmerButtontohandleLaunch. The button now has noonClickhandler, so clicking it does nothing. The old Hero forwarded the callback to both the primary and secondary CTA buttons; the replacement component has no mechanism to trigger navigation.Prompt To Fix With AI
frontend/src/components/ui/particles.tsx, line 592-595 (link)initCanvascreates twice as many particles as requestedinitCanvascallsresizeCanvas()followed bydrawParticles().resizeCanvasalready clearscircles.currentand pushesquantitynew circles into it viadrawCircle. ThendrawParticlescallsdrawCircleanotherquantitytimes, each of which also pushes intocircles.current. On every canvas initialization the array ends up with2 × quantitylive circles — soParticles quantity={220}spawns 440 particles instead of 220.drawParticlesshould be removed frominitCanvassinceresizeCanvasalready handles the initial population.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "subtle fixes and optimisations" | Re-trigger Greptile