Skip to content

Upgraded Hero Section layout UI (#45) - #52

Merged
krishkhinchi merged 2 commits into
7-Blocks:mainfrom
TheLinuxGuy-ssh:main
Jul 16, 2026
Merged

krishkhinchi merged 2 commits into
7-Blocks:mainfrom
TheLinuxGuy-ssh:main

Conversation

@TheLinuxGuy-ssh

@TheLinuxGuy-ssh TheLinuxGuy-ssh commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Description

  • Installed shadcn UI lib to use magic UI as instructed.
  • Enhanced Hero Section
  • Added Global Transition Class for consistencty in the future

Related Issue

#45

Checklist

  • I have read the contributing guidelines
  • My code follows the project guidelines
  • I have completed testing of my changes
  • Documentation has been updated (if applicable)

Screenshots / Screen Recordings

Screen.Recording.2026-07-16.at.5.59.06.PM.mp4

Breaking Changes

No Breaking Changes


ECSoC26 Submission

ECSoC26 contributors only — select your difficulty level by checking exactly one box below.
Leaving all boxes unchecked, or checking more than one, will cause the automation to fail.

  • ECSoC26-L1 – Beginner
  • ECSoC26-L2 – Intermediate
  • ECSoC26-L3 – Advanced

Summary by CodeRabbit

  • New Features
    • Added an animated landing page hero with orbital visuals, telemetry metrics, a 3D globe, particle effects, and updated calls to action.
    • Introduced reusable UI components: ShimmerButton, OrbitSatellites, Globe, and Particles.
  • Bug Fixes
    • Improved class name handling via a new cn utility for cleaner Tailwind styling.
  • Documentation
    • Updated landing page rendering to use the new shared hero component.
  • Chores
    • Added UI component system configuration and expanded styling/theming (dark mode, theme variables, animation utilities, Geist fonts).

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.

  • Hero.tsx composes four new UI components (Globe, OrbitSatellites, Particles, ShimmerButton) inside a full-viewport section with framer-motion fade-up animations.
  • globe.tsx has an unguarded setTimeout with no clearTimeout in its cleanup; this throws a TypeError when the component unmounts before the 0 ms callback fires, which React Strict Mode reliably triggers.
  • particles.tsx uses Array.splice inside a forEach loop 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

Filename Overview
frontend/src/components/ui/globe.tsx New COBE globe component with pointer-drag interaction; has an unguarded setTimeout (no clearTimeout in cleanup) that throws a TypeError on unmount in React Strict Mode.
frontend/src/components/ui/particles.tsx New canvas-based particle system; the splice-during-forEach pattern in animate skips one particle per out-of-bounds event, causing a minor visual one-frame delay.
frontend/src/components/Hero.tsx Redesigned hero section composing Globe, OrbitSatellites, Particles, and ShimmerButton; ShimmerButton lacks an onClick handler so the CTA does nothing (flagged in prior review).
frontend/src/pages/LandingPage.tsx Landing page integrating the new Hero; handleLaunch is defined but never passed to Hero, so the primary CTA has no navigation handler (flagged in prior review).
frontend/src/styles/index.css Adds shadcn/ui base styles, Geist font import, shimmer/spin-around keyframes, and dark mode variables; two @theme blocks correctly extend the existing token set.

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
Loading
%%{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| NAV
Loading

Comments Outside Diff (2)

  1. frontend/src/pages/LandingPage.tsx, line 977 (link)

    P1 Primary CTA button is non-functional

    The new <Hero /> component removed the onLaunchDashboard prop that wired the "Continue to dashboard" ShimmerButton to handleLaunch. The button now has no onClick handler, 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
    This is a comment left during a code review.
    Path: frontend/src/pages/LandingPage.tsx
    Line: 977
    
    Comment:
    **Primary CTA button is non-functional**
    
    The new `<Hero />` component removed the `onLaunchDashboard` prop that wired the "Continue to dashboard" `ShimmerButton` to `handleLaunch`. The button now has no `onClick` handler, 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.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. frontend/src/components/ui/particles.tsx, line 592-595 (link)

    P1 initCanvas creates twice as many particles as requested

    initCanvas calls resizeCanvas() followed by drawParticles(). resizeCanvas already clears circles.current and pushes quantity new circles into it via drawCircle. Then drawParticles calls drawCircle another quantity times, each of which also pushes into circles.current. On every canvas initialization the array ends up with 2 × quantity live circles — so Particles quantity={220} spawns 440 particles instead of 220. drawParticles should be removed from initCanvas since resizeCanvas already handles the initial population.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: frontend/src/components/ui/particles.tsx
    Line: 592-595
    
    Comment:
    **`initCanvas` creates twice as many particles as requested**
    
    `initCanvas` calls `resizeCanvas()` followed by `drawParticles()`. `resizeCanvas` already clears `circles.current` and pushes `quantity` new circles into it via `drawCircle`. Then `drawParticles` calls `drawCircle` another `quantity` times, each of which also pushes into `circles.current`. On every canvas initialization the array ends up with `2 × quantity` live circles — so `Particles quantity={220}` spawns 440 particles instead of 220. `drawParticles` should be removed from `initCanvas` since `resizeCanvas` already handles the initial population.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
frontend/src/components/ui/globe.tsx:94-98
**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)
    }
```

Reviews (2): Last reviewed commit: "subtle fixes and optimisations" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@codeant-ai

codeant-ai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Skipping CodeAnt AI review — this PR is a back-merge between long-lived branches (mainmain). The diff here has already been reviewed when the underlying commits landed on the source branch, so re-running analysis would produce duplicate findings on already-reviewed code.

If you want to analyze this anyway (e.g. you resolved conflicts with new logic), comment @codeant-ai : review and CodeAnt will start a review.

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation frontend Frontend development size/XL Very large or complex contribution. type:bug Fixes an existing bug or unexpected behavior. type:documentation Improves project documentation. type:frontend Changes frontend or client-side code. labels Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73dc9b5e-5aca-40bc-8354-3a0b2e80edca

📥 Commits

Reviewing files that changed from the base of the PR and between 5cbcfd8 and ac05232.

📒 Files selected for processing (2)
  • frontend/src/components/Hero.tsx
  • frontend/src/pages/LandingPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/components/Hero.tsx
  • frontend/src/pages/LandingPage.tsx

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Landing Hero Experience

Layer / File(s) Summary
UI foundation and theme setup
frontend/components.json, frontend/package.json, frontend/src/lib/utils.ts, frontend/src/styles/index.css
Adds Shadcn configuration, UI dependencies, the cn class-merging utility, shared theme variables, animations, font imports, and dark-mode styling.
Animated visual and button primitives
frontend/src/components/ui/*
Adds orbit satellites, an interactive Cobe globe, cursor-responsive particles, and a configurable shimmer button.
Animated hero composition
frontend/src/components/Hero.tsx
Adds animated hero content, ambient visuals, CTA links, and telemetry metric cards.
Landing page integration
frontend/src/pages/LandingPage.tsx
Uses the new Hero component and updates navigation branding and dashboard CTA styling and text.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning Most required sections are filled, but the ECSoC26 submission is incomplete because no difficulty level is checked. Check exactly one ECSoC26 difficulty box, or clarify that the submission section does not apply.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the hero section UI upgrade.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added AI Artificial Intelligence and Machine Learning enhancement New feature or request type:feature Introduces a new feature or enhancement. labels Jul 16, 2026
Comment on lines +45 to +48
<OrbitSatellites />
<div className="absolute inset-0 flex items-center justify-center">
<Globe className="h-max-[480px] h-max-[480px]" />
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
<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">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
<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.

Comment thread frontend/src/components/Hero.tsx Outdated
Comment thread frontend/src/pages/LandingPage.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (2)
frontend/src/components/ui/globe.tsx (1)

86-91: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Apply one consistent reduced-motion policy across the animated primitives.

  • frontend/src/components/ui/globe.tsx#L86-L91: stop incrementing phiRef when 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: disable animate-shimmer-slide and animate-spin-around through 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 win

Hero color palette diverges from the page's documented design tokens.

LandingPage.tsx documents 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

📥 Commits

Reviewing files that changed from the base of the PR and between 4d5f601 and 5cbcfd8.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • frontend/components.json
  • frontend/package.json
  • frontend/src/components/Hero.tsx
  • frontend/src/components/ui/OrbitSatellites.tsx
  • frontend/src/components/ui/globe.tsx
  • frontend/src/components/ui/particles.tsx
  • frontend/src/components/ui/shimmer-button.tsx
  • frontend/src/lib/utils.ts
  • frontend/src/pages/LandingPage.tsx
  • frontend/src/styles/index.css

Comment on lines +17 to +24
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 },
}),
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Comment thread frontend/src/components/Hero.tsx Outdated
>
<OrbitSatellites />
<div className="absolute inset-0 flex items-center justify-center">
<Globe className="h-max-[480px] h-max-[480px]" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
<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">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Comment on lines +96 to +101
<ShimmerButton shimmerColor="#4CDfF0">
<span className="inline-flex items-center gap-2">
Continue to dashboard
<ArrowRight size={15} />
</span>
</ShimmerButton>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Comment on lines +64 to +69
const updateMovement = (clientX: number) => {
if (pointerInteracting.current !== null) {
const delta = clientX - pointerInteracting.current
pointerInteractionMovement.current = delta
r.set(r.get() + delta / MOVEMENT_DAMPING)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
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.

Comment on lines +15 to +34
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
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 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.

Comment on lines +141 to +144
const initCanvas = () => {
resizeCanvas()
drawParticles()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 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.

Comment on lines +20 to +50
{
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}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' frontend/src/components/ui/shimmer-button.tsx

Repository: 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.

Suggested change
{
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.

Comment thread frontend/src/pages/LandingPage.tsx Outdated
Comment on lines 88 to +98
body {
background-color: #000000;
color: #dfe2eb;
overflow: hidden;
@apply bg-background text-foreground;
}
* {
@apply border-border outline-ring/50;
}
html {
@apply font-sans;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Comment on lines +94 to +98
setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0)
return () => {
globe.destroy()
window.removeEventListener("resize", onResize)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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().

Suggested change
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.

@krishkhinchi krishkhinchi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Artificial Intelligence and Machine Learning bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request frontend Frontend development size/XL Very large or complex contribution. type:bug Fixes an existing bug or unexpected behavior. type:documentation Improves project documentation. type:feature Introduces a new feature or enhancement. type:frontend Changes frontend or client-side code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants